From cd96b388e3a2a3b6e27a52a87bc3031f51383073 Mon Sep 17 00:00:00 2001 From: fkiefer <none@none> Date: Wed, 1 Feb 2017 17:39:55 +0100 Subject: [PATCH] OO-2483 Add error handling to transcoding and admin GUI --- .../olat/modules/video/VideoTranscoding.java | 3 + .../video/ui/TranscodingQueueTableModel.java | 6 +- .../video/ui/TranscodingQueueTableRow.java | 27 +- .../olat/modules/video/ui/TranscodingRow.java | 18 +- .../video/ui/TranscodingTableModel.java | 2 + .../video/ui/VideoAdminController.java | 15 +- .../video/ui/VideoAdminErrorController.java | 259 ++++++++++++++++++ .../video/ui/VideoAdminListController.java | 45 ++- .../ui/VideoAdminTranscodingController.java | 18 +- .../video/ui/VideoDisplayController.java | 10 + .../ui/VideoQualityTableFormController.java | 15 +- .../modules/video/ui/_content/video_run.html | 6 +- 12 files changed, 391 insertions(+), 33 deletions(-) create mode 100644 src/main/java/org/olat/modules/video/ui/VideoAdminErrorController.java diff --git a/src/main/java/org/olat/modules/video/VideoTranscoding.java b/src/main/java/org/olat/modules/video/VideoTranscoding.java index 42fe4e41c1f..624df6701a5 100644 --- a/src/main/java/org/olat/modules/video/VideoTranscoding.java +++ b/src/main/java/org/olat/modules/video/VideoTranscoding.java @@ -34,6 +34,9 @@ import org.olat.resource.OLATResource; public interface VideoTranscoding extends CreateInfo { public static final int TRANSCODING_STATUS_WAITING = -1; public static final int TRANSCODING_STATUS_DONE = 100; + public static final int TRANSCODING_STATUS_INEFFICIENT = -2; + public static final int TRANSCODING_STATUS_ERROR = -3; + public static final int TRANSCODING_STATUS_TIMEOUT = -4; public static final String FORMAT_MP4 = "mp4"; public static final String TRANSCODER_LOCAL = "Local HandBrakeCLI"; diff --git a/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableModel.java b/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableModel.java index 47660b462a9..d2f8cabbdf0 100644 --- a/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableModel.java +++ b/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableModel.java @@ -58,6 +58,8 @@ public class TranscodingQueueTableModel extends DefaultFlexiTableDataModel<Trans case size: return video.getSize(); case format: return video.getFormat(); case delete: return video.getDeleteLink(); + case retranscode: return video.getRetranscodeLink(); + case failureReason: return video.getFailureReason(); default: return ""; } } @@ -70,7 +72,9 @@ public class TranscodingQueueTableModel extends DefaultFlexiTableDataModel<Trans dimension("quality.table.header.dimension"), size("quality.table.header.size"), format("quality.table.header.format"), - delete("quality.table.header.delete"); + delete("quality.table.header.delete"), + retranscode("queue.table.header.retranscode"), + failureReason("queue.table.failure.reason"); private final String i18nKey; diff --git a/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableRow.java b/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableRow.java index 5759f99cb15..aa4b1d5ff4e 100644 --- a/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableRow.java +++ b/src/main/java/org/olat/modules/video/ui/TranscodingQueueTableRow.java @@ -31,7 +31,7 @@ import org.olat.core.gui.components.form.flexible.elements.FormLink; */ public class TranscodingQueueTableRow { - private String resid; + private FormLink resid; private String displayname; private FormLink creator; private Date creationDate; @@ -39,6 +39,8 @@ public class TranscodingQueueTableRow { private String size; private String format; private FormLink deleteLink; + private FormLink retranscodeLink; + private String failureReason; protected FormUIFactory uifactory = FormUIFactory.getInstance(); @@ -53,7 +55,7 @@ public class TranscodingQueueTableRow { * @param format the format * @param deleteLink FormLink for delete row and corresponding Videodata or null if no deleteLink should be shown */ - public TranscodingQueueTableRow(String resid, String displayname, Date creationDate, FormLink resolution, String dimension, String size, String format, FormLink deleteLink) { + public TranscodingQueueTableRow(FormLink resid, String displayname, Date creationDate, FormLink resolution, String dimension, String size, String format, FormLink deleteLink) { this.resid = resid; this.displayname = displayname; this.creator = resolution; @@ -65,6 +67,15 @@ public class TranscodingQueueTableRow { if(deleteLink != null) this.deleteLink = deleteLink; } + + public String getFailureReason() { + return failureReason; + } + + public void setFailureReason(String failureReason) { + this.failureReason = failureReason; + } + public Date getCreationDate(){ return creationDate; } @@ -81,11 +92,11 @@ public class TranscodingQueueTableRow { this.displayname = displayname; } - public String getResid(){ + public FormLink getResid(){ return resid; } - public void setResid(String resid){ + public void setResid(FormLink resid){ this.resid = resid; } @@ -128,5 +139,13 @@ public class TranscodingQueueTableRow { public void setDeleteLink(FormLink deleteLink) { this.deleteLink = deleteLink; } + + public FormLink getRetranscodeLink() { + return retranscodeLink; + } + + public void setRetranscodeLink(FormLink retranscodeLink) { + this.retranscodeLink = retranscodeLink; + } } diff --git a/src/main/java/org/olat/modules/video/ui/TranscodingRow.java b/src/main/java/org/olat/modules/video/ui/TranscodingRow.java index bd81b47ea37..af565405fab 100644 --- a/src/main/java/org/olat/modules/video/ui/TranscodingRow.java +++ b/src/main/java/org/olat/modules/video/ui/TranscodingRow.java @@ -29,16 +29,18 @@ public class TranscodingRow { private int resolution; private int sumVideos; private int missingTranscodings; + private int failedTranscodings; private int numberTranscodings; private boolean allTranscoded; - public TranscodingRow(int resolution, int numberTranscodings, int sumVideos, boolean mayTranscode) { + public TranscodingRow(int resolution, int numberTranscodings, int failedTranscodings, int sumVideos, boolean mayTranscode) { super(); this.resolution = resolution; this.numberTranscodings = numberTranscodings; this.sumVideos = sumVideos; - this.missingTranscodings = sumVideos - numberTranscodings; - this.allTranscoded = numberTranscodings < sumVideos && mayTranscode; + this.missingTranscodings = sumVideos - numberTranscodings - failedTranscodings; + this.failedTranscodings = failedTranscodings; + this.allTranscoded = numberTranscodings + failedTranscodings < sumVideos && mayTranscode; } @@ -74,9 +76,17 @@ public class TranscodingRow { public void setNumberTranscodings(int numberTranscodings) { this.numberTranscodings = numberTranscodings; } + + public int getFailedTranscodings() { + return failedTranscodings; + } + + public void setFailedTranscodings(int failedTranscodings) { + this.failedTranscodings = failedTranscodings; + } public int getMissingTranscodings() { - return missingTranscodings; + return missingTranscodings >= 0 ? missingTranscodings : 0; } public void setMissingTranscodings(int missingTranscodings) { diff --git a/src/main/java/org/olat/modules/video/ui/TranscodingTableModel.java b/src/main/java/org/olat/modules/video/ui/TranscodingTableModel.java index 81a6aadf20a..d5534291b93 100644 --- a/src/main/java/org/olat/modules/video/ui/TranscodingTableModel.java +++ b/src/main/java/org/olat/modules/video/ui/TranscodingTableModel.java @@ -52,6 +52,7 @@ public class TranscodingTableModel extends DefaultFlexiTableDataModel<Transcodin case resolutions: return translator.translate("quality.resolution." + resolution.getResolution()); case sumVideos: return resolution.getSumVideos(); case numberTranscodings: return resolution.getNumberTranscodings(); + case failedTranscodings: return resolution.getFailedTranscodings(); case missingTranscodings: return resolution.getMissingTranscodings(); case transcode: return resolution.isAllTranscoded(); case delete: return resolution.getNumberTranscodings() > 0; @@ -63,6 +64,7 @@ public class TranscodingTableModel extends DefaultFlexiTableDataModel<Transcodin resolutions("quality.table.header.resolution"), sumVideos("sum.video"), numberTranscodings("number.transcodings"), + failedTranscodings("number.transcodings.failed"), missingTranscodings("missing.transcodings"), transcode("quality.transcode"), delete("quality.delete"); diff --git a/src/main/java/org/olat/modules/video/ui/VideoAdminController.java b/src/main/java/org/olat/modules/video/ui/VideoAdminController.java index 8060dadc148..1d2563114a1 100644 --- a/src/main/java/org/olat/modules/video/ui/VideoAdminController.java +++ b/src/main/java/org/olat/modules/video/ui/VideoAdminController.java @@ -40,12 +40,13 @@ public class VideoAdminController extends BasicController { private final SegmentViewComponent segmentView; - private Link adminSetLink, adminListLink, adminTranscodingLink; + private Link adminSetLink, adminListLink, adminTranscodingLink, adminErrorLink; private VelocityContainer mainVC; private VideoAdminSetController adminSetController; private VideoAdminListController adminListController; private VideoAdminTranscodingController adminTranscodingController; + private VideoAdminErrorController adminErrorController; public VideoAdminController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl); @@ -57,6 +58,8 @@ public class VideoAdminController extends BasicController { segmentView.addSegment(adminSetLink, true); adminListLink = LinkFactory.createLink("tab.admin.list", mainVC, this); segmentView.addSegment(adminListLink, false); + adminErrorLink = LinkFactory.createLink("tab.admin.error", mainVC, this); + segmentView.addSegment(adminErrorLink, false); adminTranscodingLink = LinkFactory.createLink("tab.admin.transcoding", mainVC, this); segmentView.addSegment(adminTranscodingLink, false); @@ -80,11 +83,21 @@ public class VideoAdminController extends BasicController { doOpenAdminList(ureq); } else if (clickedLink == adminTranscodingLink){ doOpenTranscodingAdmin(ureq); + } else if (clickedLink == adminErrorLink) { + doOpenErrorAdmin(ureq); } } } } + private void doOpenErrorAdmin(UserRequest ureq) { + if(adminErrorController == null) { + adminErrorController = new VideoAdminErrorController(ureq, getWindowControl()); + listenTo(adminErrorController); + } + mainVC.put("segmentCmp", adminErrorController.getInitialComponent()); + } + private void doOpenAdminConfig(UserRequest ureq) { if(adminSetController == null) { adminSetController = new VideoAdminSetController(ureq, getWindowControl()); diff --git a/src/main/java/org/olat/modules/video/ui/VideoAdminErrorController.java b/src/main/java/org/olat/modules/video/ui/VideoAdminErrorController.java new file mode 100644 index 00000000000..533100152d5 --- /dev/null +++ b/src/main/java/org/olat/modules/video/ui/VideoAdminErrorController.java @@ -0,0 +1,259 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.video.ui; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.olat.NewControllerFactory; +import org.olat.basesecurity.BaseSecurity; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.id.Identity; +import org.olat.course.CorruptedCourseException; +import org.olat.modules.video.VideoManager; +import org.olat.modules.video.VideoTranscoding; +import org.olat.modules.video.ui.TranscodingQueueTableModel.TranscodingQueueTableCols; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryService; +import org.olat.repository.handlers.RepositoryHandler; +import org.olat.repository.handlers.RepositoryHandlerFactory; +import org.olat.user.HomePageConfig; +import org.olat.user.HomePageDisplayController; +import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial Date: 31.01.2017 + * The Class VideoAdminErrorController. + * @author fkiefer fabian.kiefer@frentix.com + * + * shows a list of all FAILED transcoding orders + */ +public class VideoAdminErrorController extends FormBasicController { + + private TranscodingQueueTableModel tableModel; + private FlexiTableElement tableEl; + private FormItemContainer formLayout; + private FormLink refreshButton; + private CloseableModalController closeableModalController; + private HomePageDisplayController homePageDisplayController; + + + private int counter = 0; + + @Autowired + private VideoManager videoManager; + @Autowired + private UserManager userManager; + @Autowired + private RepositoryService repositoryService; + @Autowired + private BaseSecurity baseSecurity; + @Autowired + private RepositoryHandlerFactory repositoryHandlerFactory; + + + public VideoAdminErrorController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl,"transcoding_queue"); + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.resid)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.displayname)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.failureReason)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.creator)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.creationDate)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.dimension)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.format)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.retranscode)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingQueueTableCols.delete)); + tableModel = new TranscodingQueueTableModel(columnsModel, getTranslator()); + + initForm(ureq); + } + + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + this.formLayout = formLayout; + setFormTitle("number.transcodings"); + setFormDescription("number.transcodings"); + setFormContextHelp("Portfolio template: Administration and editing#configuration"); + + initTable(); + } + + private void initTable () { + List<VideoTranscoding> videoTranscodings = videoManager.getFailedVideoTranscodings(); + List<TranscodingQueueTableRow> rows = new ArrayList<>(); + + for (VideoTranscoding videoTranscoding : videoTranscodings) { + + String title = videoManager.getDisplayTitleForResolution(videoTranscoding.getResolution(), getTranslator()); + String resid = String.valueOf(videoTranscoding.getVideoResource().getResourceableId()); + FormLink resourceLink = uifactory.addFormLink("res_" + counter++, "viewResource", resid, resid, flc, Link.LINK + Link.NONTRANSLATED); + resourceLink.setUserObject(videoTranscoding); + FormLink deleteLink = uifactory.addFormLink("del_" + counter++, "deleteQuality", "quality.delete", "quality.delete", flc, Link.LINK); + deleteLink.setUserObject(videoTranscoding); + deleteLink.setIconLeftCSS("o_icon o_icon_delete_item o_icon-fw"); + FormLink retranscodeLink = uifactory.addFormLink("trans_" + counter++, "retranscode", "queue.retranscode", "queue.retranscode", flc, Link.LINK); + retranscodeLink.setUserObject(videoTranscoding); + retranscodeLink.setIconLeftCSS("o_icon o_icon_refresh o_icon-fw"); + + String failureReason = ""; + if (videoTranscoding.getStatus() == VideoTranscoding.TRANSCODING_STATUS_INEFFICIENT) { + failureReason = translate("transcoding.inefficient"); + } else if (videoTranscoding.getStatus() == VideoTranscoding.TRANSCODING_STATUS_ERROR) { + failureReason = translate("transcoding.error"); + } else if (videoTranscoding.getStatus() == VideoTranscoding.TRANSCODING_STATUS_TIMEOUT) { + failureReason = translate("transcoding.timeout"); + } + + RepositoryEntry videoRe = repositoryService.loadByResourceKey(videoTranscoding.getVideoResource().getKey()); + if (videoRe == null) continue; + String displayname = videoRe.getDisplayname(); + String initialAuthor = videoRe.getInitialAuthor(); + String fullName = userManager.getUserDisplayName(initialAuthor); + FormLink authorLink = uifactory.addFormLink("author_" + counter++, "viewAuthor", + fullName, fullName, flc, Link.LINK + Link.NONTRANSLATED); + authorLink.setUserObject(initialAuthor); + Date creationDate = videoTranscoding.getCreationDate(); + TranscodingQueueTableRow transcodingrow = new TranscodingQueueTableRow(resourceLink, displayname, creationDate, authorLink, + title, null, videoTranscoding.getFormat(), deleteLink); + transcodingrow.setFailureReason(failureReason); + transcodingrow.setRetranscodeLink(retranscodeLink); + + rows.add(transcodingrow); + } + tableModel.setObjects(rows); + + if (formLayout.hasFormComponent(tableEl)){ + formLayout.remove(tableEl); + } + if (formLayout.hasFormComponent(refreshButton)){ + formLayout.remove(refreshButton); + } + + tableEl = uifactory.addTableElement(getWindowControl(), "queue", tableModel, getTranslator(), formLayout); + tableEl.setCustomizeColumns(false); + tableEl.setNumOfRowsEnabled(false); + + refreshButton = uifactory.addFormLink("button.refresh", formLayout,Link.BUTTON); + refreshButton.setIconLeftCSS("o_icon o_icon_refresh o_icon-fw"); + } + + @Override + public void event(UserRequest ureq, Component source, Event event) { + super.event(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == homePageDisplayController || source == closeableModalController){ + cleanUp(); + } + super.event(ureq, source, event); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source instanceof FormLink && ((FormLink) source).getCmd().equals("deleteQuality")) { + FormLink link = (FormLink) source; + VideoTranscoding videoTranscoding = (VideoTranscoding) link.getUserObject(); + videoManager.deleteVideoTranscoding(videoTranscoding); + } else if (source instanceof FormLink && ((FormLink) source).getCmd().equals("viewAuthor")) { + showUserInfo(ureq, baseSecurity.findIdentityByName((String) source.getUserObject())); + } else if (source instanceof FormLink && ((FormLink) source).getCmd().equals("retranscode")) { + FormLink link = (FormLink) source; + VideoTranscoding videoTranscoding = (VideoTranscoding) link.getUserObject(); + videoManager.retranscodeFailedVideoTranscoding(videoTranscoding); + } else if (source instanceof FormLink && ((FormLink) source).getCmd().equals("viewResource")) { + FormLink link = (FormLink) source; + VideoTranscoding videoTranscoding = (VideoTranscoding) link.getUserObject(); + launch(ureq, videoTranscoding); + } + initTable(); + } + + @Override + protected void formOK(UserRequest ureq) { + + } + + @Override + protected void doDispose() { + + } + + protected void launch(UserRequest ureq, VideoTranscoding videoTranscoding) { + RepositoryEntry videoRe = repositoryService.loadByResourceKey(videoTranscoding.getVideoResource().getKey()); + try { + RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler("FileResource.VIDEO"); + if(handler != null) { + + String businessPath = "[RepositoryEntry:" + videoRe.getKey() + "]"; + if(!NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl())) { + tableEl.reloadData(); + } + } + } catch (CorruptedCourseException e) { + logError("Course corrupted: " + videoRe.getKey(), e); + showError("cif.error.corrupted"); + } + } + + /** + * Method to open the users visiting card in a new tab. Public to call it also from the parent controller + * @param ureq + */ + public void showUserInfo(UserRequest ureq, Identity userID) { + + homePageDisplayController = new HomePageDisplayController(ureq, getWindowControl(), userID, new HomePageConfig()); + + closeableModalController = new CloseableModalController(getWindowControl(), translate("close"), + homePageDisplayController.getInitialComponent(), true, translate("video.contact")); + listenTo(closeableModalController); + + closeableModalController.activate(); + } + + private void cleanUp(){ + closeableModalController.deactivate(); + removeAsListenerAndDispose(closeableModalController); + closeableModalController = null; + homePageDisplayController = null; + } + +} diff --git a/src/main/java/org/olat/modules/video/ui/VideoAdminListController.java b/src/main/java/org/olat/modules/video/ui/VideoAdminListController.java index dbe8a99d769..2de3edb35ed 100644 --- a/src/main/java/org/olat/modules/video/ui/VideoAdminListController.java +++ b/src/main/java/org/olat/modules/video/ui/VideoAdminListController.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import org.olat.NewControllerFactory; import org.olat.basesecurity.BaseSecurity; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -42,11 +43,14 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.id.Identity; import org.olat.core.util.Formatter; +import org.olat.course.CorruptedCourseException; import org.olat.modules.video.VideoManager; import org.olat.modules.video.VideoTranscoding; import org.olat.modules.video.ui.TranscodingQueueTableModel.TranscodingQueueTableCols; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryService; +import org.olat.repository.handlers.RepositoryHandler; +import org.olat.repository.handlers.RepositoryHandlerFactory; import org.olat.user.HomePageConfig; import org.olat.user.HomePageDisplayController; import org.olat.user.UserManager; @@ -80,6 +84,8 @@ public class VideoAdminListController extends FormBasicController { private RepositoryService repositoryService; @Autowired private BaseSecurity baseSecurity; + @Autowired + protected RepositoryHandlerFactory repositoryHandlerFactory; public VideoAdminListController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl,"transcoding_queue"); @@ -112,18 +118,15 @@ public class VideoAdminListController extends FormBasicController { List<VideoTranscoding> videoTranscodings = videoManager.getVideoTranscodingsPendingAndInProgress(); List<TranscodingQueueTableRow> rows = new ArrayList<>(); - for(VideoTranscoding videoTranscoding:videoTranscodings){ + for (VideoTranscoding videoTranscoding : videoTranscodings) { String title = videoManager.getDisplayTitleForResolution(videoTranscoding.getResolution(), getTranslator()); String resid = String.valueOf(videoTranscoding.getVideoResource().getResourceableId()); - FormLink previewVersionLink = uifactory.addFormLink("res_" + counter++, "viewQuality", resid, resid, flc, Link.LINK + Link.NONTRANSLATED); + FormLink resourceLink = uifactory.addFormLink("res_" + counter++, "viewResource", resid, resid, flc, Link.LINK + Link.NONTRANSLATED); + resourceLink.setUserObject(videoTranscoding); FormLink deleteLink = uifactory.addFormLink("del_" + counter++, "deleteQuality", "quality.delete", "quality.delete", flc, Link.LINK); deleteLink.setUserObject(videoTranscoding); - deleteLink.setIconLeftCSS("o_icon o_icon_delete_item o_icon-fw"); - - previewVersionLink.setUserObject(videoTranscoding); - if (videoTranscoding.getStatus() < VideoTranscoding.TRANSCODING_STATUS_DONE) { - previewVersionLink.setEnabled(false); - } + deleteLink.setIconLeftCSS("o_icon o_icon_delete_item o_icon-fw"); + String fileSize = ""; if (videoTranscoding.getSize() != 0) { fileSize = Formatter.formatBytes(videoTranscoding.getSize()); @@ -133,6 +136,7 @@ public class VideoAdminListController extends FormBasicController { fileSize = translate("transcoding.processing") + ": " + videoTranscoding.getStatus() + "%"; } RepositoryEntry videoRe = repositoryService.loadByResourceKey(videoTranscoding.getVideoResource().getKey()); + if (videoRe == null) continue; String displayname = videoRe.getDisplayname(); String initialAuthor = videoRe.getInitialAuthor(); String fullName = userManager.getUserDisplayName(initialAuthor); @@ -140,7 +144,7 @@ public class VideoAdminListController extends FormBasicController { fullName, fullName, flc, Link.LINK + Link.NONTRANSLATED); authorLink.setUserObject(initialAuthor); Date creationDate = videoTranscoding.getCreationDate(); - rows.add(new TranscodingQueueTableRow(resid, displayname, creationDate, authorLink, title, fileSize, videoTranscoding.getFormat(), deleteLink)); + rows.add(new TranscodingQueueTableRow(resourceLink, displayname, creationDate, authorLink, title, fileSize, videoTranscoding.getFormat(), deleteLink)); } tableModel.setObjects(rows); @@ -158,8 +162,6 @@ public class VideoAdminListController extends FormBasicController { refreshButton = uifactory.addFormLink("button.refresh", formLayout,Link.BUTTON); refreshButton.setIconLeftCSS("o_icon o_icon_refresh o_icon-fw"); - - } @Override @@ -183,6 +185,10 @@ public class VideoAdminListController extends FormBasicController { videoManager.deleteVideoTranscoding(videoTranscoding); } else if (source instanceof FormLink && ((FormLink) source).getCmd().equals("viewAuthor")) { showUserInfo(ureq, baseSecurity.findIdentityByName((String) source.getUserObject())); + } else if (source instanceof FormLink && ((FormLink) source).getCmd().equals("viewResource")) { + FormLink link = (FormLink) source; + VideoTranscoding videoTranscoding = (VideoTranscoding) link.getUserObject(); + launch(ureq, videoTranscoding); } initTable(); }; @@ -197,6 +203,23 @@ public class VideoAdminListController extends FormBasicController { } + private void launch(UserRequest ureq, VideoTranscoding videoTranscoding) { + RepositoryEntry videoRe = repositoryService.loadByResourceKey(videoTranscoding.getVideoResource().getKey()); + try { + RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler("FileResource.VIDEO"); + if(handler != null) { + + String businessPath = "[RepositoryEntry:" + videoRe.getKey() + "]"; + if(!NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl())) { + tableEl.reloadData(); + } + } + } catch (CorruptedCourseException e) { + logError("Course corrupted: " + videoRe.getKey(), e); + showError("cif.error.corrupted"); + } + } + /** * Method to open the users visiting card in a new tab. Public to call it also from the parent controller * @param ureq diff --git a/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java b/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java index f0d8d4f29da..cc67f036cb7 100644 --- a/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java +++ b/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java @@ -91,6 +91,7 @@ public class VideoAdminTranscodingController extends FormBasicController { transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.resolutions)); transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.sumVideos)); transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.numberTranscodings)); + transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.failedTranscodings)); transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.missingTranscodings)); transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.transcode, "quality.transcode", new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("quality.transcode"), "quality.transcode"), null))); @@ -122,17 +123,24 @@ public class VideoAdminTranscodingController extends FormBasicController { List<TranscodingRow> resolutions = new ArrayList<>(); // Hardcoded same as VideoAdminSetController int[] fixresolution = { 2160, 1080, 720, 480, 360, 240 }; - Map<Integer, Integer> resCount = new HashMap<>(); - for (TranscodingCount transcodingCount : videoManager.getAllVideoTranscodingsCount()) { - resCount.put(transcodingCount.getResolution(), transcodingCount.getCount()); + Map<Integer, Integer> successCount = new HashMap<>(); + int beginErrorCode = VideoTranscoding.TRANSCODING_STATUS_INEFFICIENT; + for (TranscodingCount transcodingCount : videoManager.getAllVideoTranscodingsCountSuccess(beginErrorCode)) { + successCount.put(transcodingCount.getResolution(), transcodingCount.getCount()); + } + Map<Integer, Integer> failCount = new HashMap<>(); + for (TranscodingCount transcodingCount : videoManager.getAllVideoTranscodingsCountFails(beginErrorCode)) { + failCount.put(transcodingCount.getResolution(), transcodingCount.getCount()); } for (int i = 0; i < fixresolution.length; i++) { int counter = 0; for (OLATResource videoResource : nativeResolutions.keySet()) { if (nativeResolutions.get(videoResource) >= fixresolution[i]) counter++; } - int rescount = resCount.get(fixresolution[i]) != null ? resCount.get(fixresolution[i]) : 0; - resolutions.add(new TranscodingRow(fixresolution[i], rescount, counter, mayTranscode(fixresolution[i]))); + int Scount = successCount.get(fixresolution[i]) != null ? successCount.get(fixresolution[i]) : 0; + int Fcount = failCount.get(fixresolution[i]) != null ? failCount.get(fixresolution[i]) : 0; + TranscodingRow transcodingRow = new TranscodingRow(fixresolution[i], Scount, Fcount, counter, mayTranscode(fixresolution[i])); + resolutions.add(transcodingRow); } if (resolutions != null){ tableModel.setObjects(resolutions); diff --git a/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java b/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java index cd3abd12cb5..5c7b7e797ba 100644 --- a/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java +++ b/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java @@ -28,6 +28,7 @@ import org.olat.core.commons.services.commentAndRating.CommentAndRatingDefaultSe import org.olat.core.commons.services.commentAndRating.CommentAndRatingSecurityCallback; import org.olat.core.commons.services.commentAndRating.ReadOnlyCommentsSecurityCallback; import org.olat.core.commons.services.commentAndRating.ui.UserCommentsAndRatingsController; +import org.olat.core.commons.services.image.Size; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.htmlheader.jscss.JSAndCSSComponent; @@ -194,6 +195,11 @@ public class VideoDisplayController extends BasicController { mainVC.contextPut("authors", (StringHelper.containsNonWhitespace(authors) ? authors : null)); if(video != null) { + // get resolution of master video resource + Size masterResolution = videoManager.getVideoResolutionFromOLATResource(entry.getOlatResource()); + String masterTitle = videoManager.getDisplayTitleForResolution(masterResolution.getHeight(), getTranslator()); + String masterSize = " (" + Formatter.formatBytes(videoManager.getVideoMetadata(entry.getOlatResource()).getSize()) + ")"; + boolean addMaster = true; // Mapper for Video String masterMapperId = "master-" + entry.getOlatResource().getResourceableId(); String masterUrl = registerCacheableMapper(ureq, masterMapperId, new VideoMediaMapper(videoManager.getMasterContainer(entry.getOlatResource()))); @@ -213,6 +219,8 @@ public class VideoDisplayController extends BasicController { for (VideoTranscoding videoTranscoding : videos) { if (videoTranscoding.getStatus() == VideoTranscoding.TRANSCODING_STATUS_DONE) { readyToPlayVideos.add(videoTranscoding); + // Check if at least one has equal height, else use master as resource + addMaster &= videoTranscoding.getHeight() < masterResolution.getHeight(); // Use the users preferred resolution or the next higher resolution if (videoTranscoding.getResolution() >= userPreferredResolution.intValue()) { preferredAvailableResolution = readyToPlayVideos.size() - 1; @@ -222,6 +230,8 @@ public class VideoDisplayController extends BasicController { displayTitles.add(title); } } + mainVC.contextPut("addMaster", addMaster); + mainVC.contextPut("masterTitle", masterTitle + masterSize); mainVC.contextPut("videos", readyToPlayVideos); mainVC.contextPut("displayTitles", displayTitles); mainVC.contextPut("useSourceChooser", Boolean.valueOf(readyToPlayVideos.size() > 1)); diff --git a/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java b/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java index eff5876ed81..1cf1a788ff5 100644 --- a/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java +++ b/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java @@ -124,13 +124,20 @@ public class VideoQualityTableFormController extends FormBasicController { int height = videoTranscoding.getHeight(); String dimension = width +"x"+ height; String fileSize = ""; - if (videoTranscoding.getSize() != 0) { + int status = videoTranscoding.getStatus(); + if (videoTranscoding.getSize() != 0 && status > -1) { fileSize = Formatter.formatBytes(videoTranscoding.getSize()); - } else if (videoTranscoding.getStatus() == VideoTranscoding.TRANSCODING_STATUS_WAITING) { + } else if (status == VideoTranscoding.TRANSCODING_STATUS_WAITING) { fileSize = translate("transcoding.waiting"); - } else if (videoTranscoding.getStatus() <= VideoTranscoding.TRANSCODING_STATUS_DONE){ + } else if (status <= VideoTranscoding.TRANSCODING_STATUS_DONE && status > -1){ fileSize = translate("transcoding.processing") + ": " + videoTranscoding.getStatus() + "%"; - } + } else if (status == VideoTranscoding.TRANSCODING_STATUS_INEFFICIENT) { + fileSize = translate("transcoding.inefficient"); + } else if (status == VideoTranscoding.TRANSCODING_STATUS_ERROR) { + fileSize = translate("transcoding.error"); + } else if (status == VideoTranscoding.TRANSCODING_STATUS_TIMEOUT) { + fileSize = translate("transcoding.timeout"); + } rows.add(new QualityTableRow(previewVersionLink, dimension, fileSize, videoTranscoding.getFormat(), deleteLink)); } List<Integer> missingResolutions = videoManager.getMissingTranscodings(videoResource); diff --git a/src/main/java/org/olat/modules/video/ui/_content/video_run.html b/src/main/java/org/olat/modules/video/ui/_content/video_run.html index 997c835ea33..cf97e03f476 100644 --- a/src/main/java/org/olat/modules/video/ui/_content/video_run.html +++ b/src/main/java/org/olat/modules/video/ui/_content/video_run.html @@ -1,14 +1,14 @@ <div class="o_video_run o_block_large_bottom clearfix"> <div class="olatFlashMovieViewer"> <video id="$r.getId("o_vid")" width="$width" height="$height" #if($usePoster) poster="$masterUrl/poster.jpg" #end controls #if(!$hasChapters) preload="none" #end oncontextmenu="return false;" #if( $autoplay ) autoplay #end class="o_video"> + #if ($videos.size() == 0 || $addMaster) + <source type="video/mp4" src="$masterUrl/video.mp4" title="$masterTitle"/> + #end #foreach($video in $videos) #set($position = $velocityCount - 1) <source type="video/mp4" src="$transcodedUrl/${video.getResolution()}video.mp4" title="$displayTitles.get($position) ($r.formatBytes(${video.getSize()}))" /> #end ## Use master video file if not optimized video is found - #if ($videos.size() == 0) - <source type="video/mp4" src="$masterUrl/video.mp4" /> - #end #if( $trackfiles ) #foreach( $track in $trackfiles.keySet()) -- GitLab