From 327658c60dbc9f76fdbb96525fa5e7a859d62954 Mon Sep 17 00:00:00 2001
From: fkiefer <none@none>
Date: Tue, 15 Nov 2016 19:46:28 +0100
Subject: [PATCH] OO-2361 VideoAdminTranscodingController restructure to
 FlexiTable, add resolutioncheck

---
 .../ims/qti/resultexport/ResultDetail.java    |  29 ---
 .../olat/modules/video/ui/TranscodingRow.java |  89 +++++++++
 .../video/ui/TranscodingTableModel.java       |  96 ++++++++++
 .../ui/VideoAdminTranscodingController.java   | 173 +++++++++---------
 .../ui/VideoQualityTableFormController.java   |  37 +++-
 .../video/ui/_content/transcoding_admin.html  |  11 ++
 .../video/ui/_i18n/LocalStrings_de.properties | 113 ++++++------
 .../video/ui/_i18n/LocalStrings_en.properties |  84 ++++-----
 8 files changed, 411 insertions(+), 221 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/video/ui/TranscodingRow.java
 create mode 100644 src/main/java/org/olat/modules/video/ui/TranscodingTableModel.java
 create mode 100644 src/main/java/org/olat/modules/video/ui/_content/transcoding_admin.html

diff --git a/src/main/java/org/olat/ims/qti/resultexport/ResultDetail.java b/src/main/java/org/olat/ims/qti/resultexport/ResultDetail.java
index 487cd5ccbe6..3437a9d848a 100644
--- a/src/main/java/org/olat/ims/qti/resultexport/ResultDetail.java
+++ b/src/main/java/org/olat/ims/qti/resultexport/ResultDetail.java
@@ -23,11 +23,8 @@ public class ResultDetail {
 	
 	private String assessmentID;
 	private String assessmentDate;
-	private String dateCaption;
 	private String duration;
-	private String durationCaption;
 	private float score;
-	private String scoreCaption;
 	private String passed;
 	private String link;
 	
@@ -43,8 +40,6 @@ public class ResultDetail {
 		this.passed = passed;
 		this.link = link;
 	}
-
-
 	
 
 	public String getLink() {
@@ -55,30 +50,6 @@ public class ResultDetail {
 		this.link = link;
 	}
 
-	public String getDateCaption() {
-		return dateCaption;
-	}
-
-	public void setDateCaption(String dateCaption) {
-		this.dateCaption = dateCaption;
-	}
-
-	public String getDurationCaption() {
-		return durationCaption;
-	}
-
-	public void setDurationCaption(String durationCaption) {
-		this.durationCaption = durationCaption;
-	}
-
-	public String getScoreCaption() {
-		return scoreCaption;
-	}
-
-	public void setScoreCaption(String scoreCaption) {
-		this.scoreCaption = scoreCaption;
-	}
-
 	public String getAssessmentID() {
 		return assessmentID;
 	}
diff --git a/src/main/java/org/olat/modules/video/ui/TranscodingRow.java b/src/main/java/org/olat/modules/video/ui/TranscodingRow.java
new file mode 100644
index 00000000000..1dc8a890911
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/ui/TranscodingRow.java
@@ -0,0 +1,89 @@
+/**
+ * <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;
+
+/**
+ * The Class TranscodingRow.
+ * Initial date: 15.11.2016<br>
+ * @author Fabian Kiefer, fabian.kiefer@frentix.com, http://www.frentix.com
+ */
+public class TranscodingRow {
+	
+	private int resolution;
+	private int sumVideos;
+	private int missingTranscodings;
+	private int numberTranscodings;
+	private boolean allTranscoded;
+
+	public TranscodingRow(int resolution, int numberTranscodings, int sumVideos) {
+		super();
+		this.resolution = resolution;
+		this.numberTranscodings = numberTranscodings;
+		this.sumVideos = sumVideos;
+		this.missingTranscodings = sumVideos - numberTranscodings;
+		this.allTranscoded = numberTranscodings != sumVideos;		
+	}
+
+	
+	
+	public boolean isAllTranscoded() {
+		return allTranscoded;
+	}
+
+	public void setAllTranscoded(boolean allTranscoded) {
+		this.allTranscoded = allTranscoded;
+	}
+
+	public int getResolution() {
+		return resolution;
+	}
+
+	public void setResolution(int resolution) {
+		this.resolution = resolution;
+	}
+
+	public int getSumVideos() {
+		return sumVideos;
+	}
+
+	public void setSumVideos(int sumVideos) {
+		this.sumVideos = sumVideos;
+	}
+
+	public int getNumberTranscodings() {
+		return numberTranscodings;
+	}
+
+	public void setNumberTranscodings(int numberTranscodings) {
+		this.numberTranscodings = numberTranscodings;
+	}
+
+	public int getMissingTranscodings() {
+		return missingTranscodings;
+	}
+
+	public void setMissingTranscodings(int missingTranscodings) {
+		this.missingTranscodings = 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
new file mode 100644
index 00000000000..81a6aadf20a
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/ui/TranscodingTableModel.java
@@ -0,0 +1,96 @@
+/**
+ * <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 org.olat.core.gui.components.form.flexible.FormUIFactory;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.translator.Translator;
+
+/**
+ *
+ * Initial date: 15.11.2016<br>
+ * @author Fabian Kiefer, fabian.kiefer@frentix.com, http://www.frentix.com
+ *
+ */
+public class TranscodingTableModel extends DefaultFlexiTableDataModel<TranscodingRow>{
+
+	protected FormUIFactory uifactory = FormUIFactory.getInstance();
+	private Translator translator;
+	public TranscodingTableModel(FlexiTableColumnModel columnModel, Translator translator) {
+		super(columnModel);
+		this.translator = translator;
+	}
+
+	@Override
+	public TranscodingTableModel createCopyWithEmptyList() {
+		return new TranscodingTableModel(getTableColumnModel(), translator);
+	}
+
+	@Override
+	public Object getValueAt(int row, int col) {
+		TranscodingRow resolution = getObject(row);
+		switch(TranscodingCols.values()[col]) {
+			case resolutions: return translator.translate("quality.resolution." + resolution.getResolution());
+			case sumVideos: return resolution.getSumVideos();
+			case numberTranscodings: return resolution.getNumberTranscodings();
+			case missingTranscodings: return resolution.getMissingTranscodings();
+			case transcode: return resolution.isAllTranscoded();
+			case delete: return resolution.getNumberTranscodings() > 0;
+			default: return "";
+		}
+	}
+
+	public enum TranscodingCols implements FlexiSortableColumnDef {
+		resolutions("quality.table.header.resolution"),
+		sumVideos("sum.video"),
+		numberTranscodings("number.transcodings"),
+		missingTranscodings("missing.transcodings"),
+		transcode("quality.transcode"),
+		delete("quality.delete");
+
+		private final String i18nKey;
+
+		private TranscodingCols(String i18nKey) {
+			this.i18nKey = i18nKey;
+		}
+
+		@Override
+		public String i18nHeaderKey() {
+			return i18nKey;
+		}
+
+		@Override
+		public boolean sortable() {
+			return true;
+		}
+
+		@Override
+		public String sortKey() {
+			return name();
+		}
+		
+		public String i18nKey() {
+			return i18nKey;
+		}
+	}
+
+}
\ No newline at end of file
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 ee4a6d846fb..0065d7007c4 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java
@@ -29,13 +29,21 @@ import java.util.Set;
 import org.olat.core.gui.UserRequest;
 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.MultipleSelectionElement;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
 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.BooleanCellRenderer;
+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.form.flexible.impl.elements.table.SelectionEvent;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.VideoMetadata;
 import org.olat.modules.video.VideoTranscoding;
+import org.olat.modules.video.ui.TranscodingTableModel.TranscodingCols;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceManager;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -49,16 +57,12 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 public class VideoAdminTranscodingController extends FormBasicController {
 	
-	private MultipleSelectionElement enable2160SelectionEl;
-	private MultipleSelectionElement enable1080SelectionEl;
-	private MultipleSelectionElement enable720SelectionEl;
-	private MultipleSelectionElement enable480SelectionEl;
-	private MultipleSelectionElement enable360SelectionEl;
-	private MultipleSelectionElement enable240SelectionEl;
-	
-	private Map<Integer,Set<Long>> availableTranscodings;
+	private Map<Integer,Set<OLATResource>> availableTranscodings;
 	private List<OLATResource> olatresources;
+	private TranscodingTableModel tableModel;
+	private FlexiTableElement transcodingTable;
 	
+	private List<TranscodingRow> resolutions;
 	
 	@Autowired
 	private OLATResourceManager olatresourceManager;
@@ -66,8 +70,8 @@ public class VideoAdminTranscodingController extends FormBasicController {
 	private VideoManager videoManager;
 
 	public VideoAdminTranscodingController(UserRequest ureq, WindowControl wControl) {
-		super(ureq,wControl);
-
+		super(ureq, wControl, "transcoding_admin");
+		resolutions = new ArrayList<>();
 		generateStatusOfTranscodings();
 
 		initForm(ureq);
@@ -79,83 +83,79 @@ public class VideoAdminTranscodingController extends FormBasicController {
 		setFormDescription("manage.transcodings.description");
 		setFormContextHelp("Portfolio template: Administration and editing#configuration");		
 		
-		String[] enableKeys = new String[]{ "on" };
-		String[] enableValues = new String[]{ translate("on") };
-
-		enable2160SelectionEl = uifactory.addCheckboxesHorizontal("quality.resolution.2160", formLayout, enableKeys, enableValues);
-		enable2160SelectionEl.addActionListener(FormEvent.ONCHANGE);
-		enable2160SelectionEl.setUserObject(2160);
-		
-		enable1080SelectionEl = uifactory.addCheckboxesHorizontal("quality.resolution.1080", formLayout, enableKeys, enableValues);
-		enable1080SelectionEl.addActionListener(FormEvent.ONCHANGE);
-		enable1080SelectionEl.setUserObject(1080);
-		
-		enable720SelectionEl = uifactory.addCheckboxesHorizontal("quality.resolution.720", formLayout, enableKeys, enableValues);
-		enable720SelectionEl.addActionListener(FormEvent.ONCHANGE);
-		enable720SelectionEl.setUserObject(720);
-		
-		enable480SelectionEl = uifactory.addCheckboxesHorizontal("quality.resolution.480", formLayout, enableKeys, enableValues);
-		enable480SelectionEl.addActionListener(FormEvent.ONCHANGE);
-		enable480SelectionEl.setUserObject(480);
-		
-		enable360SelectionEl = uifactory.addCheckboxesHorizontal("quality.resolution.360", formLayout, enableKeys, enableValues);
-		enable360SelectionEl.addActionListener(FormEvent.ONCHANGE);
-		enable360SelectionEl.setUserObject(360);
-		
-		enable240SelectionEl = uifactory.addCheckboxesHorizontal("quality.resolution.240", formLayout, enableKeys, enableValues);
-		enable240SelectionEl.addActionListener(FormEvent.ONCHANGE);
-		enable240SelectionEl.setUserObject(240);
+		FlexiTableColumnModel transcodingModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
+		transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.resolutions));
+		transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.sumVideos));
+		transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.numberTranscodings));
+		transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.missingTranscodings));
+		transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.transcode, "quality.transcode", 
+				new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("quality.transcode"), "quality.transcode"), null)));
+		transcodingModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TranscodingCols.delete, "quality.delete", 
+				new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("quality.delete"), "quality.delete"), null)));
+		tableModel = new TranscodingTableModel(transcodingModel, getTranslator());
 		
+		transcodingTable = uifactory.addTableElement(getWindowControl(), "table", tableModel, getTranslator(), formLayout);
+		transcodingTable.setCustomizeColumns(false);
+		transcodingTable.setNumOfRowsEnabled(false);
+				
 		setChecks();
 	}
 	
+	private void loadTable(){
+		//Hardcoded same as VideoAdminSetController
+		int[] resolution = {2160, 1080, 720, 480, 360, 240};
+		for (int i = 0; i < resolution.length; i++) {
+			int sizeOfTranscodings = availableTranscodings.get(resolution[i]).size();
+			int counter = 0;
+			for (OLATResource videoResource : olatresources) {
+				VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource);
+				if (videoMetadata != null && videoMetadata.getHeight() >= resolution[i]) counter++;
+			}
+			resolutions.add(new TranscodingRow(resolution[i], sizeOfTranscodings, counter));
+		}		
+		if (resolutions != null) tableModel.setObjects(resolutions);
+		transcodingTable.reset(true, true, true);	
+	}
+	
+	/**
+	 * Update Table Content of all available Transcodings
+	 */
 	public void setChecks(){	
-		
 		generateStatusOfTranscodings();
-		
-		MultipleSelectionElement[] resolutionSelectionEls = {enable240SelectionEl,enable360SelectionEl,
-				enable480SelectionEl, enable720SelectionEl,enable1080SelectionEl,enable2160SelectionEl};
-		
-		//iterate all MultiSelectionElements and decide if checked or not
-		for (MultipleSelectionElement mse : resolutionSelectionEls) {
-			int sizeOfTranscodings = availableTranscodings.get((int) mse.getUserObject()).size();
-			if (sizeOfTranscodings == olatresources.size()){
-				mse.select("on",true);
-			} else {
-				mse.select("on",false);
-			}
-			String transcoded = " " + translate("number.transcodings");
-			mse.setKeysAndValues(new String[]{ "on" }, new String[]{ sizeOfTranscodings + "/" + olatresources.size() + transcoded});
-		}
+		resolutions.clear();
+		loadTable();
 	}
 
+
+	
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if (source == enable2160SelectionEl){
-			queueCreateOrDeleteTranscoding(source);
-		} else if (source == enable1080SelectionEl){
-			queueCreateOrDeleteTranscoding(source);
-		} else if (source == enable720SelectionEl){
-			queueCreateOrDeleteTranscoding(source);
-		} else if (source == enable480SelectionEl){
-			queueCreateOrDeleteTranscoding(source);
-		} else if (source == enable360SelectionEl){
-			queueCreateOrDeleteTranscoding(source);
-		} else if (source == enable240SelectionEl){	
-			queueCreateOrDeleteTranscoding(source);	
+		if(source == transcodingTable) {
+			if(event instanceof SelectionEvent) {
+				SelectionEvent se = (SelectionEvent)event;
+				TranscodingRow currentObject = (TranscodingRow) tableModel.getObject(se.getIndex());
+				if ("quality.delete".equals(se.getCommand())){
+					queueDeleteTranscoding(currentObject);
+					showInfo("delete.transcodings");
+				} else if ("quality.transcode".equals(se.getCommand())){
+					queueCreateTranscoding(currentObject);
+					showInfo("info.transcoding");
+				} 
+			}
 		}
+		
 		//refresh checks
 		setChecks();
 	}
 	
 	private void generateStatusOfTranscodings() {
 		availableTranscodings = new HashMap<>();
-		availableTranscodings.put(240, new HashSet<Long>());
-		availableTranscodings.put(360, new HashSet<Long>());
-		availableTranscodings.put(480, new HashSet<Long>());
-		availableTranscodings.put(720, new HashSet<Long>());
-		availableTranscodings.put(1080, new HashSet<Long>());
-		availableTranscodings.put(2160, new HashSet<Long>());
+		availableTranscodings.put(240, new HashSet<OLATResource>());
+		availableTranscodings.put(360, new HashSet<OLATResource>());
+		availableTranscodings.put(480, new HashSet<OLATResource>());
+		availableTranscodings.put(720, new HashSet<OLATResource>());
+		availableTranscodings.put(1080, new HashSet<OLATResource>());
+		availableTranscodings.put(2160, new HashSet<OLATResource>());
 		//determine resource type of interest
 		List<String> types = new ArrayList<>();
 		types.add("FileResource.VIDEO");
@@ -167,48 +167,45 @@ public class VideoAdminTranscodingController extends FormBasicController {
 			List<VideoTranscoding> transcodings = videoManager.getVideoTranscodings(videoResource);
 			//map resource IDs to resolution
 			for (VideoTranscoding videoTranscoding : transcodings) {
-				if (videoTranscoding != null && videoTranscoding.getStatus() != -1) {
-					Set<Long> oneResolution = availableTranscodings.get(videoTranscoding.getResolution());
+				if (videoTranscoding != null) {
+					Set<OLATResource> oneResolution = availableTranscodings.get(videoTranscoding.getResolution());
 					if (oneResolution != null) {
-						oneResolution.add(videoTranscoding.getVideoResource().getKey());						
+						oneResolution.add(videoTranscoding.getVideoResource());						
 					}
 				}
 			}
 		}
 	}
 	
-	//create of delete resources, depended on MultiSelectionElement status
-	private void queueCreateOrDeleteTranscoding(FormItem source){
-		if (source instanceof MultipleSelectionElement && ((MultipleSelectionElement)source).isSelected(0)){
-			queueCreateTranscoding(source);
-		} else {
-			queueDeleteTranscoding(source);
-		}	
-	}
 	
 	//state orders for inexistent transcodings
-	private void queueCreateTranscoding(FormItem source){
+	private void queueCreateTranscoding(TranscodingRow source){
 		for (OLATResource videoResource : olatresources) {
-			if (!availableTranscodings.get((int) source.getUserObject()).contains(videoResource.getKey())){
-				videoManager.createTranscoding(videoResource, (int) source.getUserObject(), "mp4");				
+			if (!availableTranscodings.get(source.getResolution()).contains(videoResource)){
+				VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource);
+				if (videoMetadata != null && videoMetadata.getHeight() >= source.getResolution()) {					
+					videoManager.createTranscoding(videoResource, source.getResolution(), "mp4");				
+				}
 			}
 		}
 	}
 	
 	//go through all and delete selection
-	private void queueDeleteTranscoding(FormItem source) {
+	private void queueDeleteTranscoding(TranscodingRow source) {
 		for (OLATResource videoResource : olatresources) {
-			if (availableTranscodings.get((int) source.getUserObject()).contains(videoResource.getKey())) {
+			if (availableTranscodings.get(source.getResolution()).contains(videoResource)) {
 				List<VideoTranscoding> videoTranscodings = videoManager.getVideoTranscodings(videoResource);
 
 				for (VideoTranscoding videoTranscoding : videoTranscodings) {
-					if (videoTranscoding.getResolution() == (int) source.getUserObject()) {
+					if (videoTranscoding.getResolution() == source.getResolution()) {
 						videoManager.deleteVideoTranscoding(videoTranscoding);
 					}
 				}
 			}
 		}
 	}
+	
+
 
 	@Override
 	protected void formOK(UserRequest ureq) {
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 e6279608a70..299dd49ac28 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
@@ -1,6 +1,6 @@
 /**
- * <a href="http://www.openolat.org">
  * 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>
@@ -40,6 +40,8 @@ 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;
@@ -68,6 +70,8 @@ public class VideoQualityTableFormController extends FormBasicController {
 	private FormLink refreshbtn;
 
 	private int count = 0;
+	
+	private static final OLog log = Tracing.createLoggerFor(VideoQualityTableFormController.class);
 
 	@Autowired
 	private VideoManager videoManager;
@@ -235,16 +239,29 @@ public class VideoQualityTableFormController extends FormBasicController {
 	private class VideoComparator implements Comparator<QualityTableRow> {
 
 		@Override
-		public int compare(QualityTableRow row1, QualityTableRow row2) {	
-			if (translate(row1.getResolution().getI18nKey()).equals("Master video"))return -1;
-			else if (translate(row2.getResolution().getI18nKey()).equals("Master video"))return 1;
-			else { 
-				String s1 =translate(row1.getResolution().getI18nKey());
-				String s2 = translate(row2.getResolution().getI18nKey());
-				return 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]", ""));
+		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;
+				}
 			}
 		}
-		
 	}
 }
diff --git a/src/main/java/org/olat/modules/video/ui/_content/transcoding_admin.html b/src/main/java/org/olat/modules/video/ui/_content/transcoding_admin.html
new file mode 100644
index 00000000000..fe409aac066
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/ui/_content/transcoding_admin.html
@@ -0,0 +1,11 @@
+<fieldset>
+	<legend>
+		$r.contextHelpWithWrapper("Learning resource: Video#_video_resolution")
+		$r.translate("manage.transcodings.title")
+	</legend>	
+	<div class="container-fluid">
+		<div class="row clearfix">
+			$r.render("table")
+		</div>
+	</div>
+</fieldset>
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties
index 138182934a3..fb40225bf33 100644
--- a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties
@@ -1,61 +1,47 @@
-#Tue Nov 15 16:10:16 CET 2016
+#Thu Apr 21 15:21:09 CEST 2016
 add.track=Untertitel hinzuf\u00FCgen
 admin.config.enable=Videoressource einschalten
-admin.config.hint=Sind Sie sicher dass sie das Videotranscoding deaktivieren wollen?
-admin.config.hint.title=Achtung\!
 admin.config.title=Videokonfiguration
 admin.config.transcoding=Transcoding aktivieren
 admin.config.videoNode=Video Kursbaustein aktivieren
 admin.menu.title=Video
 admin.menu.title.alt=Konfiguration der Video-Resource
 admin.menu.transcoding.title=Transkodierung
-button.refresh=neu laden
-chapter.error.already.exists=Ein Kapitel mit dieser Zeitangaben existiert bereits.
-chapter.error.format=Bitte verwenden Sie nur das Zeitformat HH\:mm\:ss\! 
-chapter.error.name.already.exists=Ein Kapitel mit diesem Namen existiert bereits.
-chapter.error.notime=Bitte geben Sie eine Zeit ein\!
-chapter.error.notitle=Bitte geben Sie einen Titel ein\!
-chapter.error.out.of.range=Ihre Zeitangabe \u00FCberschreitet die L\u00E4nge des Videos\!
+admin.config.hint=Sind Sie sicher dass sie das Videotranscoding deaktivieren wollen?
+admin.config.hint.title=Achtung!
 listing.viewing.counter={0} Aufrufe
-manage.transcodings.description=Verwalten Sie alle Transkodierungen einer Aufl\u00F6sungen mit einem Klick. Ist das H\u00E4kchen gesetzt, sind alle Transkodierungen vorhanden und k\u00F6nnen durch Deaktivieren des Kontrollk\u00E4stchens gel\u00F6scht werden. Sind die Transkodieungen unvollst\u00E4ndig, werden fehlende transkodiert.
-manage.transcodings.title=Transkodierungen verwalten
-number.transcodings=sind bereits transkodiert
 poster.error.filetype=F\u00FCr Poster Bild werden nur Bilder vom Typ JPG unterst\u00FCtzt.
-poster.help=Bild vom Typ JPG. Bitte \u00FCberpr\u00FCfen Sie, ob das Poster genau die gleiche Aufl\u00F6sung hat wie das Originalvideo (gleiche H\u00F6he und Breite in Pixel).
 poster.select=W\u00E4hlen Sie ein Posterbild aus
-quality.delete=l\u00F6schen
-quality.master=Master video
-quality.resolution.1080=1080p Full-HD
-quality.resolution.2160=2160p 4K
-quality.resolution.240=240p
-quality.resolution.360=360p
-quality.resolution.480=480p
-quality.resolution.720=720p HD
-quality.resolution.default=Standardaufl\u00F6sung
-quality.table.header.delete=Aktion
+poster.help=Bild vom Typ JPG. Bitte \u00FCberpr\u00FCfen Sie, ob das Poster genau die gleiche Aufl\u00F6sung hat wie das Originalvideo (gleiche Höhe und Breite in Pixel).
 quality.table.header.dimension=Dimension
 quality.table.header.format=Format
-quality.table.header.resolution=Aufl\u00F6sung
 quality.table.header.size=Gr\u00F6sse
+quality.table.header.resolution=Aufl\u00F6sung
 quality.table.header.view=ansehen
-quality.transcode=transkodieren
+quality.table.header.delete=Aktion
 quality.view=vorschau
-queue.table.header.creationDate=erstellt, den
-queue.table.header.creator=Ersteller
-queue.table.header.displayname=Name
-queue.table.header.resid=ID
-queue.trans.description=Warteschlange aller zu transcodierenden Videos
-queue.trans.title=Warteschlange aller zu transcodierenden Videos
-tab.admin.list=Warteschlange
-tab.admin.set=Videokonfiguration
-tab.admin.transcoding=Transcodings verwalten
-tab.video.chapterConfig=Kapitel
-tab.video.exchange=Video ersetzen
+quality.delete=l\u00F6schen
+quality.transcode=transkodieren
+quality.master=Master video
+quality.resolution.2160=2160p 4K
+quality.resolution.1080=1080p Full-HD
+quality.resolution.720=720p HD
+quality.resolution.480=480p
+quality.resolution.360=360p
+quality.resolution.240=240p
+quality.resolution.default=Standardaufl\u00F6sung
+transcoding.waiting=In Warteschlange
+transcoding.processing=In Bearbeitung
 tab.video.metaDataConfig=Metadaten
 tab.video.posterConfig=Poster konfigurieren
 tab.video.qualityConfig=Videoqualit\u00E4ten
 tab.video.settings=Videokonfiguration
 tab.video.trackConfig=Untertitel konfigurieren
+tab.video.chapterConfig=Kapitel
+tab.video.exchange=Video ersetzen
+tab.admin.set=Videokonfiguration
+tab.admin.list=Warteschlange
+tab.admin.transcoding=Transcodings verwalten
 topnav.video=LearnTube
 topnav.video.alt=Bibliothek mit frei verf\u00FCgbaren Lernvideos
 track.delete=L\u00F6schen
@@ -70,17 +56,6 @@ track.upload.error.nofile=Bitte w\u00E4hlen Sie eine Datei aus.
 track.upload.error.nolang=Bitte w\u00E4hlen Sie eine Sprache aus dieser Liste aus
 transcoding.processing=In Bearbeitung
 transcoding.waiting=In Warteschlange
-video.chapter.add=Kapitel hinzuf\u00FCgen
-video.chapter.chapterName=Kapitel
-video.chapter.deleteLink=L\u00F6schen
-video.chapter.edit=Kapitel editieren
-video.chapter.editLink=Editieren
-video.chapter.from=Anfang
-video.chapter.intervals=Intervall
-video.chapter.name=Kapitel
-video.chapter.new=Neues Kapitel
-video.chapter.title=Kapitel verwalten
-video.chapter.to=Bis
 video.config.creationDate=Erstellungsdatum
 video.config.description=Beschreibung
 video.config.duration=L\u00E4nge
@@ -99,10 +74,42 @@ video.config.tracks.table.add=hinzuf\u00FCgen
 video.config.tracks.table.delete=l\u00F6schen
 video.config.tracks.table.lang=Sprache
 video.config.width=Breite
-video.contact=Kontakt
-video.mime.type=Ressourcentyp
-video.mime.type.error=Bitte verwenden Sie nur ".mov" or ".mp4" Video-Dateien\!
-video.not.replaced=Ihre Video-Datei wurde nicht ersetzt. Bitte w\u00E4hlen Sie eine Video-Datei aus.
-video.replace.desc=Bitte w\u00E4hlen Sie eine Video-Datei aus. Um die alte Datei zu ersetzen, best\u00E4tigen Sie ihre Auswahl mit der Schaltfl\u00E4che "Video ersetzen".
+listing.viewing.counter={0} Aufrufe
+button.refresh=neu laden
+queue.table.header.resid=ID
+queue.table.header.displayname=Name
+queue.table.header.creator=Ersteller
+queue.table.header.creationDate=erstellt, den
+queue.trans.title=Warteschlange aller zu transcodierenden Videos
+queue.trans.description=Warteschlange aller zu transcodierenden Videos
+number.transcodings=bereits transkodiert
+manage.transcodings.description=Verwalten Sie alle Transkodierungen einer Aufl\u00F6sungen mit einem Klick. Ist das H\u00E4kchen gesetzt, sind alle Transkodierungen vorhanden und k\u00F6nnen durch Deaktivieren des Kontrollk\u00E4stchens gel\u00F6scht werden. Sind die Transkodieungen unvollst\u00E4ndig, werden fehlende transkodiert.
+manage.transcodings.title=Transkodierungen verwalten
+video.chapter.title=Kapitel verwalten
+video.chapter.add=Kapitel hinzuf\u00FCgen
+video.chapter.edit=Kapitel editieren
+video.chapter.from=Anfang
+video.chapter.to=Bis
+video.chapter.chapterName=Kapitel
+video.chapter.intervals=Interval
+video.chapter.editLink=Editieren
+video.chapter.deleteLink=L\u00F6schen
+video.chapter.name=Kapitel
+video.chapter.new=Neues Kapitel
+chapter.error.notime=Bitte geben Sie eine Zeit ein!
+chapter.error.out.of.range=Ihre Zeitangabe \u00FCberschreitet die L\u00E4nge des Videos!
+chapter.error.already.exists=Ein Kapitel mit dieser Zeitangaben existiert bereits.
+chapter.error.name.already.exists=Ein Kapitel mit diesem Namen existiert bereits.
+chapter.error.format=Bitte verwenden Sie nur das Zeitformat HH:mm:ss! 
+chapter.error.notitle=Bitte geben Sie einen Titel ein!
+video.replace.desc=Bitte w\u00E4hlen Sie eine Video-Datei aus. Um die alte Datei zu ersetzen, best\u00E4tigen Sie ihre Auswahl mit dem Button "Video ersetzen".
 video.replace.upload=Video Datei Upload
 video.replaced=Ihre Video-Datei wurde ersetzt und weitere Aufl\u00F6sungen werden transkodiert.
+video.not.replaced=Ihre Video-Datei wurde nicht ersetzt. Bitte w\u00E4hlen Sie eine Video-Datei aus.
+video.mime.type.error=Bitte verwenden Sie nur ".mov" or ".mp4" Video-Dateien!
+video.mime.type=Ressourcentyp
+video.contact=Kontakt
+sum.video=Anzahl Videos
+missing.transcodings=Fehlend
+delete.transcodings=Alle Transcodings dieser Aufl\u00F6sung wurden gel\u00F6scht.
+info.transcoding=Fehlende Transcodings werden erstellt.
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties
index fc6b22ad75f..82839419b4c 100644
--- a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties
@@ -1,29 +1,18 @@
-#Tue Nov 15 09:57:06 CET 2016
+#Thu Jun 02 15:11:46 CEST 2016
 add.track=Add Subtitle
 admin.config.enable=Enable video resource
-admin.config.hint=Do you really want to deactivate the video transcoding?
-admin.config.hint.title=Warning
 admin.config.title=Video configuration
 admin.config.transcoding=Enable video transcoding
 admin.config.videoNode=Enable video course element
 admin.menu.title=Video
 admin.menu.title.alt=Configuration of the Video-Resource
 admin.menu.transcoding.title=transcoding
-button.refresh=refresh
-chapter.error.already.exists=A chapter with this Timecode already exists\!
-chapter.error.format=Please only use HH\:mm\:ss time format\!
-chapter.error.name.already.exists=A chapter with this name already exists\!
-chapter.error.notime=Please put a valid time\!
-chapter.error.notitle=Please set a valid Chapter Title\!
-chapter.error.out.of.range=Your Chapter Timecode exceeds the length of the Video\!
 listing.viewing.counter={0} viewings
-manage.transcodings.description=Manage all transcodings per selection of the checkbox. Is the resolution checkbox is checked, unselect the checkbox to delete all. Are transcodings incomplete, select the checkbox to transcode the missing.
-manage.transcodings.title=Manage transcoding
-number.transcodings=are already transcoded
 poster.error.filetype=For poster images only images of type JPG are supported.
 poster.help=Image of type JPG. Make sure the poster has the exact same dimension as the original video (same height and width in pixel).
 poster.select=Select poster frame
 quality.delete=delete
+quality.transcode=transcode
 quality.master=Master video
 quality.resolution.1080=1080p Full-HD
 quality.resolution.2160=2160p 4K
@@ -32,29 +21,23 @@ quality.resolution.360=360p
 quality.resolution.480=480p
 quality.resolution.720=720p HD
 quality.resolution.default=Default resolution
-quality.table.header.delete=Do
 quality.table.header.dimension=Dimension
 quality.table.header.format=Format
 quality.table.header.resolution=Resolution
 quality.table.header.size=Size
 quality.table.header.view=Preview
-quality.transcode=transcode
+quality.table.header.delete=Do
 quality.view=preview
-queue.table.header.creationDate=Created on
-queue.table.header.creator=Creator
-queue.table.header.displayname=Name
-queue.table.header.resid=ID
-queue.trans.description=Waiting list of the video to transcode
-tab.admin.list=Pending transcodings
-tab.admin.set=Video configuration
-tab.admin.transcoding=Administer transcodings
-tab.video.chapterConfig=Chapters
-tab.video.exchange=Replace Video
 tab.video.metaDataConfig=Metadata
 tab.video.posterConfig=Replace poster
 tab.video.qualityConfig=Video quality
+tab.video.chapterConfig=Chapters
 tab.video.settings=Video settings
 tab.video.trackConfig=Subtitle configuration
+tab.video.exchange=Replace Video
+tab.admin.set=Video configuration
+tab.admin.list=Pending transcodings
+tab.admin.transcoding=Administer transcodings
 topnav.video=LearnTube
 topnav.video.alt=Library with freely available learning videos
 track.delete=Delete
@@ -69,18 +52,6 @@ track.upload.error.nofile=Please select a file.
 track.upload.error.nolang=Please select a language form the list
 transcoding.processing=Processing
 transcoding.waiting=Queuing
-video.chapter.add=Add Chapter
-video.chapter.chapterName=Chapter
-video.chapter.deleteLink=delete
-video.chapter.edit=Edit Chapter
-video.chapter.editLink=edit
-video.chapter.from=Begin
-video.chapter.intervals=Interval
-video.chapter.name=Create Chapter
-video.chapter.new=New Chapter
-video.chapter.remove=Remove Chapter
-video.chapter.title=Manage Chapters
-video.chapter.to=To
 video.config.creationDate=Creation Date
 video.config.description=Description
 video.config.duration=Running time
@@ -99,10 +70,41 @@ video.config.tracks.table.add=add
 video.config.tracks.table.delete=delete
 video.config.tracks.table.lang=language
 video.config.width=Width
-video.contact=Contact
-video.mime.type=Resource Type
-video.mime.type.error=Please only choose Movie-Files of Type ".mov" or ".mp4"\!
-video.not.replaced=Your Video file has not been replaced. Please choose a valid video resource.
+video.chapter.name=Create Chapter
+video.chapter.add=Add Chapter
+video.chapter.edit=Edit Chapter
+video.chapter.remove=Remove Chapter
+video.chapter.from=Begin
+video.chapter.to=To
+video.chapter.title=Manage Chapters
+video.chapter.add=Add Chapter
+video.chapter.chapterName=Chapter
+video.chapter.intervals=Interval
+video.chapter.editLink=edit
+video.chapter.deleteLink=delete
+video.chapter.new=New Chapter
+chapter.error.notime=Please put a valid time!
+chapter.error.out.of.range=Your Chapter Timecode exceeds the length of the Video!
+chapter.error.already.exists=A chapter with this Timecode already exists!
+chapter.error.name.already.exists=A chapter with this name already exists!
+chapter.error.format=Please only use HH:mm:ss time format!
+chapter.error.notitle=Please set a valid Chapter Title!
 video.replace.desc=Please choose a Video-File from your File-System. To replace the old File, confirm by pressing the "Replace Video"-Button.
 video.replace.upload=Video File Upload 
 video.replaced=Your Video file has been replaced, and new Resolutions are beeing transcoded. 
+video.not.replaced=Your Video file has not been replaced. Please choose a valid video resource.
+video.mime.type=Resource Type
+video.mime.type.error=Please only choose Movie-Files of Type ".mov" or ".mp4"!
+button.refresh=refresh
+queue.table.header.resid=ID
+queue.table.header.displayname=Name
+queue.table.header.creator=Creator
+queue.table.header.creationDate=Created on
+number.transcodings=already transcoded
+manage.transcodings.description=Manage all transcodings per selection of the checkbox. Is the resolution checkbox is checked, unselect the checkbox to delete all. Are transcodings incomplete, select the checkbox to transcode the missing.
+manage.transcodings.title=Manage transcoding
+video.contact=Contact
+sum.video=Sum Video
+missing.transcodings=Missing
+delete.transcodings=All Transcodings of this Resolution have been deleted.
+info.transcoding=Missing Transcodings will be created.
\ No newline at end of file
-- 
GitLab