/** * OpenOLAT - Online Learning and Training</a><br> * <a href="http://www.openolat.org"> * <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.Comparator; import java.util.List; 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.components.velocity.VelocityContainer; 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.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Formatter; import org.olat.core.util.vfs.VFSContainer; import org.olat.modules.video.VideoManager; import org.olat.modules.video.VideoMeta; import org.olat.modules.video.VideoModule; import org.olat.modules.video.VideoTranscoding; import org.olat.modules.video.manager.VideoMediaMapper; import org.olat.modules.video.ui.VideoQualityTableModel.QualityTableCols; import org.olat.repository.RepositoryEntry; import org.olat.resource.OLATResource; import org.springframework.beans.factory.annotation.Autowired; /** * table to show the different available transcoded video versions of a video resource * @author dfurrer, dirk.furrer@frentix.com, http://www.frentix.com * */ public class VideoQualityTableFormController extends FormBasicController { private FlexiTableElement tableEl; private VideoQualityTableModel tableModel; private CloseableModalController cmc; private VelocityContainer previewVC; private OLATResource videoResource; private FormItemContainer formLayout; private FormLink refreshbtn; private int count = 0; private static final OLog log = Tracing.createLoggerFor(VideoQualityTableFormController.class); @Autowired private VideoManager videoManager; @Autowired private VideoModule videoModule; public VideoQualityTableFormController(UserRequest ureq, WindowControl wControl, RepositoryEntry videoEntry) { super(ureq, wControl, "video_quality"); this.videoResource = videoEntry.getOlatResource(); initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { this.formLayout = formLayout; FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(QualityTableCols.resolution)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(QualityTableCols.dimension)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(QualityTableCols.size, new TranscodingErrorIconRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(QualityTableCols.format)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(QualityTableCols.delete)); tableModel = new VideoQualityTableModel(columnsModel, getTranslator()); initTable(); } private void initTable(){ List<QualityTableRow> rows = new ArrayList<>(); VideoMeta videoMetadata = videoManager.getVideoMetadata(videoResource); // Add master video file FormLink previewMasterLink = uifactory.addFormLink("view", "viewQuality", "quality.master", "quality.master", flc, Link.LINK); Object[] statusMaster = new Object[]{100, Formatter.formatBytes(videoManager.getVideoFile(videoResource).length())}; rows.add(new QualityTableRow(previewMasterLink, videoMetadata.getWidth() +"x"+ videoMetadata.getHeight(), statusMaster, "mp4",null)); // Add all the transcoded versions List<VideoTranscoding> videoTranscodings = videoManager.getVideoTranscodings(videoResource); for(VideoTranscoding videoTranscoding:videoTranscodings){ String title = videoManager.getDisplayTitleForResolution(videoTranscoding.getResolution(), getTranslator()); FormLink previewVersionLink = uifactory.addFormLink("res_" + count++, "viewQuality", title, title, flc, Link.LINK + Link.NONTRANSLATED); FormLink deleteLink = uifactory.addFormLink("del_" + count++, "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); } int width = videoTranscoding.getWidth(); int height = videoTranscoding.getHeight(); String dimension = width +"x"+ height; String fileSize = ""; int status = videoTranscoding.getStatus(); if (videoTranscoding.getSize() != 0 && status > -1) { fileSize = Formatter.formatBytes(videoTranscoding.getSize()); } else if (status == VideoTranscoding.TRANSCODING_STATUS_WAITING) { fileSize = translate("transcoding.waiting"); } 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"); } Object[] statusTranscoding = new Object[]{status, fileSize}; rows.add(new QualityTableRow(previewVersionLink, dimension,statusTranscoding, videoTranscoding.getFormat(), deleteLink)); } List<Integer> missingResolutions = videoManager.getMissingTranscodings(videoResource); if (videoModule.isTranscodingEnabled()) { for(Integer missingRes : missingResolutions){ if (missingRes <= videoMetadata.getHeight()) { String title = videoManager.getDisplayTitleForResolution(missingRes, getTranslator()); FormLink transcodeLink = uifactory.addFormLink("res_" + count++, "startTranscoding", "quality.transcode", "quality.transcode", flc, Link.LINK); transcodeLink.setUserObject(missingRes); transcodeLink.setIconLeftCSS("o_icon o_icon_refresh o_icon-fw"); FormLink previewMissingLink= uifactory.addFormLink("res_" + count++, "viewQuality", title, title, flc, Link.LINK + Link.NONTRANSLATED); previewMissingLink.setEnabled(false); Object[] status = new Object[]{-1, "-"}; rows.add(new QualityTableRow(previewMissingLink, missingRes.toString(),status, "mp4", transcodeLink)); } } } rows.sort(new VideoComparator()); tableModel.setObjects(rows); if (formLayout.hasFormComponent(tableEl)){ formLayout.remove(tableEl); } if (formLayout.hasFormComponent(refreshbtn)){ formLayout.remove(refreshbtn); } tableEl = uifactory.addTableElement(getWindowControl(), "qualityTable", tableModel, getTranslator(), formLayout); tableEl.setCustomizeColumns(false); tableEl.setNumOfRowsEnabled(false); refreshbtn = uifactory.addFormLink("button.refresh", formLayout, Link.BUTTON); refreshbtn.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, org.olat.core.gui.control.Event event) { if (source instanceof FormLink && ((FormLink) source).getCmd().equals("viewQuality")) { if (cmc == null) { // initialize preview controller only once previewVC = createVelocityContainer("video_preview"); cmc = new CloseableModalController(getWindowControl(), "close", previewVC); listenTo(cmc); } } super.event(ureq, source, event); } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if (source instanceof FormLink && ((FormLink) source).getCmd().equals("viewQuality")) { if (cmc == null) { // initialize preview controller only once previewVC = createVelocityContainer("video_preview"); cmc = new CloseableModalController(getWindowControl(), "close", previewVC); listenTo(cmc); } // Get the user object from the link to access version object FormLink link = (FormLink) source; VideoTranscoding videoTranscoding = (VideoTranscoding) link.getUserObject(); if (videoTranscoding == null) { // this is the master video // VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource); VideoMeta videoMetadata = videoManager.getVideoMetadata(videoResource); previewVC.contextPut("width", videoMetadata.getWidth()); previewVC.contextPut("height", videoMetadata.getHeight()); previewVC.contextPut("filename", "video.mp4"); VFSContainer container = videoManager.getMasterContainer(videoResource); String transcodedUrl = registerMapper(ureq, new VideoMediaMapper(container)); previewVC.contextPut("mediaUrl", transcodedUrl); } else { // this is a version previewVC.contextPut("width", videoTranscoding.getWidth()); previewVC.contextPut("height", videoTranscoding.getHeight()); previewVC.contextPut("filename", videoTranscoding.getResolution() + "video.mp4"); VFSContainer container = videoManager.getTranscodingContainer(videoResource); String transcodedUrl = registerMapper(ureq, new VideoMediaMapper(container)); previewVC.contextPut("mediaUrl", transcodedUrl); } // activate dialog to bring it in front cmc.activate(); } else 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("startTranscoding")) { videoManager.createTranscoding(videoResource, (int) source.getUserObject(), "mp4"); } initTable(); } @Override protected void formOK(UserRequest ureq) { // nothing to do, events cached in formInnerEvent } @Override protected void doDispose() { // controller auto disposed } private class VideoComparator implements Comparator<QualityTableRow> { @Override public int compare(QualityTableRow row1, QualityTableRow row2) { if (row1 == null || row1.getResolution() == null) return -1; if (row2 == null || row2.getResolution() == null) return -1; String s1 = translate(row1.getResolution().getI18nKey()); String s2 = translate(row2.getResolution().getI18nKey()); if (s1 == null || s1.length() == 0) return -1; if (s2 == null || s2.length() == 0) return 1; if ("Master video".equals(s1)) return -1; else if ("Master video".equals(s2)) return 1; else { try { int comp = Integer.parseInt(s2.substring(0, s2.length() < 30 ? s2.length() : 30).replaceAll("[^0-9]", "")) - Integer.parseInt(s1.substring(0, s1.length() < 30 ? s1.length() : 30).replaceAll("[^0-9]", "")); return comp; } catch (Exception e) { log.error("No valid transcoding resolution available", e); return 0; } } } } }