From 090a35d3ba6f58d9488fa6a9c64c1a4a0bd34c91 Mon Sep 17 00:00:00 2001
From: fkiefer <none@none>
Date: Mon, 30 Jan 2017 14:59:33 +0100
Subject: [PATCH] OO-2454 Improve performance of video management: replace
 metadata xml with database adapt track files

---
 .../org/olat/modules/video/VideoManager.java  | 123 +++++++--
 .../org/olat/modules/video/VideoMeta.java     | 115 +++++++++
 .../video/manager/VideoManagerImpl.java       | 233 ++++++++++++------
 .../video/manager/VideoMetadataDAO.java       | 142 +++++++++++
 .../video/manager/VideoTranscodingDAO.java    |  66 ++++-
 .../modules/video/model/TranscodingCount.java |  23 ++
 .../modules/video/model/VideoMetaImpl.java    | 190 ++++++++++++++
 .../video/ui/VideoAdminController.java        |   2 +-
 .../ui/VideoAdminTranscodingController.java   | 132 +++++-----
 .../video/ui/VideoDisplayController.java      |   9 +-
 .../ui/VideoMetaDataEditFormController.java   |   4 +-
 .../ui/VideoQualityTableFormController.java   |   7 +-
 .../video/ui/VideoResourceEditController.java |  31 ++-
 .../video/ui/VideoTrackEditController.java    |   3 +-
 .../video/ui/VideoTrackUploadForm.java        |  12 +-
 .../repository/handlers/VideoHandler.java     |  14 +-
 .../org/olat/upgrade/OLATUpgrade_11_3_0.java  |  67 ++++-
 src/main/resources/META-INF/persistence.xml   |   1 +
 .../database/mysql/alter_11_2_x_to_11_3_0.sql |  16 ++
 .../database/mysql/setupDatabase.sql          |  16 ++
 .../oracle/alter_11_2_x_to_11_3_0.sql         |  15 ++
 .../database/oracle/setupDatabase.sql         |  15 ++
 .../postgresql/alter_11_2_x_to_11_3_0.sql     |  15 ++
 .../database/postgresql/setupDatabase.sql     |  15 ++
 .../video/manager/VideoMetadataDAOTest.java   | 102 ++++++++
 .../manager/VideoTranscodingDAOTest.java      |   6 +-
 .../java/org/olat/test/AllTestsJunit4.java    |   1 +
 27 files changed, 1166 insertions(+), 209 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/video/VideoMeta.java
 create mode 100644 src/main/java/org/olat/modules/video/manager/VideoMetadataDAO.java
 create mode 100644 src/main/java/org/olat/modules/video/model/TranscodingCount.java
 create mode 100644 src/main/java/org/olat/modules/video/model/VideoMetaImpl.java
 create mode 100644 src/test/java/org/olat/modules/video/manager/VideoMetadataDAOTest.java

diff --git a/src/main/java/org/olat/modules/video/VideoManager.java b/src/main/java/org/olat/modules/video/VideoManager.java
index affe3a378ec..2a0470e58cd 100644
--- a/src/main/java/org/olat/modules/video/VideoManager.java
+++ b/src/main/java/org/olat/modules/video/VideoManager.java
@@ -21,14 +21,17 @@ package org.olat.modules.video;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
+import org.olat.core.commons.services.image.Size;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.fileresource.types.ResourceEvaluation;
 import org.olat.modules.video.manager.VideoExportMediaResource;
+import org.olat.modules.video.model.TranscodingCount;
+import org.olat.modules.video.model.VideoMetaImpl;
 import org.olat.modules.video.ui.VideoChapterTableRow;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
@@ -76,15 +79,15 @@ public interface VideoManager {
 	 * @param videoResource
 	 * @return HashMap<String, VFSLeaf>
 	 */
-	public abstract HashMap<String, VFSLeaf> getAllTracks(OLATResource videoResource);
-
-	/**
-	 * add track file for given language to videoResource
-	 * @param videoResource
-	 * @param lang
-	 * @param trackFile
-	 */
-	public abstract void addTrack(OLATResource videoResource, String lang, VFSLeaf trackFile);
+	public abstract Map<String, VFSLeaf> getAllTracks(OLATResource videoResource);
+//
+//	/**
+//	 * add track file for given language to videoResource
+//	 * @param videoResource
+//	 * @param lang
+//	 * @param trackFile
+//	 */
+//	public abstract void addTrack(OLATResource videoResource, String lang, VFSLeaf trackFile);
 
 	/**
 	 * get Track in given lang as VFSLeaf
@@ -134,6 +137,27 @@ public interface VideoManager {
 	 * @return
 	 */
 	public abstract List<VideoTranscoding> getVideoTranscodings(OLATResource video);
+	
+	/**
+	 * Gets the all vidoe transcodings.
+	 *
+	 * @return the all vidoe transcodings
+	 */
+	public abstract List<VideoTranscoding> getAllVideoTranscodings();
+	
+	/**
+	 * Gets the one video resolution.
+	 *
+	 * @return the one video resolution
+	 */
+	public abstract List<VideoTranscoding> getOneVideoResolution(int resolution);
+	
+	/**
+	 * Gets the all video transcodings.
+	 *
+	 * @return the all video transcodings
+	 */
+	public abstract List<TranscodingCount> getAllVideoTranscodingsCount();
 
 	/**
 	 * Get a human readable aspect ratio from the given video size. Recognizes
@@ -297,12 +321,83 @@ public interface VideoManager {
 	 * @return the video duration
 	 */
 	public abstract long getVideoDuration(OLATResource videoResource);
+	
+	/**
+	 * Gets the video resolution from olat resource.
+	 *
+	 * @param videoResource the video resource
+	 * @return the video resolution from olat resource
+	 */
+	public Size getVideoResolutionFromOLATResource (OLATResource videoResource);
+
+//	/**
+//	 * Gets the meta data from olat resource.
+//	 *
+//	 * @param OLATResource videoResource the video resource
+//	 * @return the metadata from videoResource 
+//	 */
+//	public abstract VideoMetadata getMetaDataFromOLATResource(OLATResource videoResource);
+//	
+	/**
+	 * Gets the all video resources metadata.
+	 *
+	 * @return the all video resources metadata
+	 */
+	public abstract List<VideoMetaImpl> getAllVideoResourcesMetadata();
+	
+	/**
+	 * Gets the video meta data.
+	 *
+	 * @param videoResource the video resource
+	 * @return the video meta data
+	 */
+	public abstract VideoMetaImpl getVideoMetadata(OLATResource videoResource);
+
+	/**
+	 * Exchange poster of the new resource.
+	 *
+	 * @param videoResource the OLATResource
+	 */
+	public void exchangePoster(OLATResource videoResource);
+
+	/**
+	 * Update video metadata.
+	 *
+	 * @param videoResource the OLATResource
+	 * @param uploadVideo the upload video
+	 */
+	public void updateVideoMetadata(OLATResource videoResource, VFSLeaf uploadVideo);
+	
+	/**
+	 * Gets the all video repo entries.
+	 *
+	 * @param typename of a type
+	 * @return repo entries
+	 */
+	public List<RepositoryEntry> getAllVideoRepoEntries (String typename);
 
 	/**
-	 * Gets the meta data from olat resource.
+	 * Delete video metadata.
 	 *
-	 * @param OLATResource videoResource the video resource
-	 * @return the metadata from videoResource 
+	 * @param videoResource the video resource
 	 */
-	public abstract VideoMetadata getMetaDataFromOLATResource(OLATResource videoResource);
+	public boolean deleteVideoMetadata(OLATResource videoResource);
+
+	/**
+	 * Creates the video metadata.
+	 *
+	 * @param repoEntry the repo entry
+	 * @param size the size
+	 * @param fileName the file name
+	 * @return the video meta
+	 */
+	public VideoMeta createVideoMetadata(RepositoryEntry repoEntry, long size, String fileName);
+
+	/**
+	 * Start transcoding process if enabled.
+	 *
+	 * @param video the video
+	 */
+	public void startTranscodingProcessIfEnabled(OLATResource video);
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/video/VideoMeta.java b/src/main/java/org/olat/modules/video/VideoMeta.java
new file mode 100644
index 00000000000..74f152c11db
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/VideoMeta.java
@@ -0,0 +1,115 @@
+/**
+ * <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;
+
+import java.util.Date;
+
+import org.olat.core.id.CreateInfo;
+import org.olat.resource.OLATResource;
+
+/**
+ * Represents the metadata of a transcoded video file 
+ * 
+ * Initial date: 19.01.2017<br>
+ * @author fkiefer, fabian.kiefer@frentix.com, http://www.frentix.com
+ *
+ */
+public interface VideoMeta extends CreateInfo {
+	public static final String FORMAT_MP4 = "mp4";
+	
+	/**
+	 * @return key, the database identifier
+	 */
+	public Long getKey();
+
+	/**
+	 * @return The video resource of the master video
+	 */
+	public OLATResource getVideoResource();
+	
+	/**
+	 * @return width of transcoded video in pixel
+	 */
+	public int getWidth();
+
+	/**
+	 * @param width of video in pixel after transcoding
+	 */
+	public void setWidth(int width);
+
+	/**
+	 * @return height of transcoded video in pixel
+	 */
+	public int getHeight();
+
+	/**
+	 * @param height of video in pixel after transcoding
+	 */
+	public void setHeight(int height);
+
+	/**
+	 * @return the video file size in bytes
+	 */
+	public long getSize();
+
+	/**
+	 * @param size the file size (bytes) of the transcoded video
+	 */
+	public void setSize(long size);
+
+	/**
+	 * @return the transcoding format, e.g. mp4
+	 */
+	public String getFormat();
+
+	/**
+	 * @return format the transcoding format, e.g. mp4
+	 */
+	public void setFormat(String format);
+	
+	/**
+	 * Gets the length of the video as string.
+	 *
+	 * @return the length
+	 */
+	public String getLength();
+	
+	/**
+	 * Sets the length.
+	 *
+	 * @param length the new length
+	 */
+	public void setLength(String length);
+
+	/**
+	 * Sets the creation date.
+	 *
+	 * @param creationdate
+	 */
+	public void setCreationDate(Date creationdate);
+
+	/**
+	 * Sets the video resource.
+	 *
+	 * @param videoResource the new video resource
+	 */
+	public void setVideoResource(OLATResource videoResource);
+	
+}
diff --git a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
index f054c21a1f0..a8504c6bc0f 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
@@ -36,7 +36,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map.Entry;
+import java.util.Map;
 import java.util.TimeZone;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -45,6 +45,7 @@ import java.util.zip.ZipFile;
 
 import javax.imageio.ImageIO;
 
+import org.apache.commons.io.FilenameUtils;
 import org.jcodec.api.FrameGrab;
 import org.jcodec.common.FileChannelWrapper;
 import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
@@ -66,13 +67,17 @@ import org.olat.core.util.vfs.VFSItem;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSManager;
 import org.olat.core.util.vfs.VFSStatus;
+import org.olat.core.util.vfs.filters.VFSItemSuffixFilter;
 import org.olat.core.util.xml.XStreamHelper;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.fileresource.types.ResourceEvaluation;
 import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.VideoMeta;
 import org.olat.modules.video.VideoMetadata;
 import org.olat.modules.video.VideoModule;
 import org.olat.modules.video.VideoTranscoding;
+import org.olat.modules.video.model.TranscodingCount;
+import org.olat.modules.video.model.VideoMetaImpl;
 import org.olat.modules.video.model.VideoMetadataImpl;
 import org.olat.modules.video.ui.VideoChapterTableRow;
 import org.olat.repository.RepositoryEntry;
@@ -99,11 +104,14 @@ public class VideoManagerImpl implements VideoManager {
 	protected static final String DIRNAME_REPOENTRY = "repoentry";
 	public static final String FILETYPE_MP4 = "mp4";
 	private static final String FILETYPE_JPG = "jpg";
+	private static final String FILETYPE_SRT = "srt";
 	private static final String FILENAME_POSTER_JPG = "poster.jpg";
 	private static final String FILENAME_VIDEO_MP4 = "video.mp4";
 	private static final String FILENAME_CHAPTERS_VTT = "chapters.vtt";
 	private static final String FILENAME_VIDEO_METADATA_XML = "video_metadata.xml";
 	private static final String DIRNAME_MASTER = "master";
+	public static final String TRACK = "track_";
+	public static final String DOT = "." ;
 	
 	private static final SimpleDateFormat displayDateFormat = new SimpleDateFormat("HH:mm:ss");
 	private static final SimpleDateFormat vttDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
@@ -117,6 +125,8 @@ public class VideoManagerImpl implements VideoManager {
 	@Autowired
 	private VideoTranscodingDAO videoTranscodingDao;
 	@Autowired
+	private VideoMetadataDAO videoMetadataDao;
+	@Autowired
 	private Scheduler scheduler;
 	@Autowired
 	private ImageService imageHelper;
@@ -156,7 +166,7 @@ public class VideoManagerImpl implements VideoManager {
 	 * @param posterframe the newPosterFile
 	 */
 	public void setPosterframeResizeUploadfile(OLATResource videoResource, VFSLeaf newPosterFile) {
-		VideoMetadata videoMetadata = readVideoMetadataFile(videoResource);
+		VideoMeta videoMetadata = getVideoMetadata(videoResource);
 		Size posterRes = imageHelper.getSize(newPosterFile, FILETYPE_JPG);
 		// file size needs to be bigger than target resolution, otherwise use image as it comes
 		if (posterRes != null 
@@ -180,23 +190,23 @@ public class VideoManagerImpl implements VideoManager {
 		}
 	}
 
-	/**
-	 * add a subtitle-track to the videoresource
-	 */
-	@Override
-	public void addTrack(OLATResource videoResource, String lang, VFSLeaf trackFile){
-		VideoMetadata metaData = readVideoMetadataFile(videoResource);
-		metaData.addTrack(lang, trackFile.getName());
-		writeVideoMetadataFile(metaData, videoResource);
-	}
+//	/**
+//	 * add a subtitle-track to the videoresource
+//	 */
+//	@Override
+//	public void addTrack(OLATResource videoResource, String lang, VFSLeaf trackFile){
+//		VideoMetadata metaData = readVideoMetadataFile(videoResource);
+//		metaData.addTrack(lang, trackFile.getName());
+//		writeVideoMetadataFile(metaData, videoResource);
+//	}
 
 	/**
 	 * get a specific subtitle-track of the videoresource
 	 */
 	@Override
 	public VFSLeaf getTrack(OLATResource videoResource, String lang) {
-		VideoMetadata metaData = readVideoMetadataFile(videoResource);
-		return resolveFromMasterContainer(videoResource, metaData.getTrack(lang));
+		String path = TRACK + lang + DOT + FILETYPE_SRT;
+		return resolveFromMasterContainer(videoResource, path);
 	}
 	
 	/**
@@ -204,22 +214,31 @@ public class VideoManagerImpl implements VideoManager {
 	 */
 	@Override
 	public void removeTrack(OLATResource videoResource, String lang){
-		VideoMetadata metaData = readVideoMetadataFile(videoResource);
-		resolveFromMasterContainer(videoResource, metaData.getTrack(lang)).delete();
-		metaData.removeTrack(lang);
-		writeVideoMetadataFile(metaData, videoResource);
+		VFSContainer vfsContainer = getMasterContainer(videoResource);
+		for (VFSItem item : vfsContainer.getItems(new VFSItemSuffixFilter(new String[]{FILETYPE_SRT}))) {
+			if (item.getName().contains(lang)) {
+				item.delete();
+			}
+		}
 	}
 	
 	/**
 	 * get all tracks saved in the video metadata as map
 	 */
 	@Override
-	public HashMap<String, VFSLeaf> getAllTracks(OLATResource videoResource) {
-		VideoMetadata metaData = readVideoMetadataFile(videoResource);
-		HashMap<String, VFSLeaf> tracks = new HashMap<String, VFSLeaf>();
-		for(Entry<String, String> trackEntry : metaData.getAllTracks().entrySet()){
-			tracks.put(trackEntry.getKey(), resolveFromMasterContainer(videoResource, trackEntry.getValue()));
+	public Map<String, VFSLeaf> getAllTracks(OLATResource videoResource) {
+		Map<String, VFSLeaf> tracks = new HashMap<>();
+		VFSContainer vfsContainer = getMasterContainer(videoResource);
+		for (VFSItem item : vfsContainer.getItems(new VFSItemSuffixFilter(new String[]{FILETYPE_SRT}))) {
+			String itemname = item.getName();
+			String key = itemname.substring(itemname.indexOf("_") + 1, itemname.indexOf("."));
+			tracks.put(key, resolveFromMasterContainer(videoResource, itemname));
 		}
+//		VideoMetadata metaData = readVideoMetadataFile(videoResource);
+//		for(Entry<String, String> trackEntry : metaData.getAllTracks().entrySet()){
+//		for(Entry<String, String> trackEntry : alltracks.entrySet()){
+//			tracks.put(trackEntry.getKey(), resolveFromMasterContainer(videoResource, trackEntry.getValue()));
+//		}
 		return tracks;
 	}
 	
@@ -247,7 +266,7 @@ public class VideoManagerImpl implements VideoManager {
 			FileChannel ch = randomAccessFile.getChannel();
 			FileChannelWrapper in = new FileChannelWrapper(ch);
 			FrameGrab frameGrab = new FrameGrab(in).seekToFrameSloppy(frameNumber);
-			OutputStream frameOutputStream = frame.getOutputStream(true);
+			OutputStream frameOutputStream = frame.getOutputStream(false);
 
 			BufferedImage bufImg = frameGrab.getFrame();
 			ImageIO.write(bufImg, "JPG", frameOutputStream);
@@ -294,16 +313,16 @@ public class VideoManagerImpl implements VideoManager {
 		}
 	}
 
-	/**
-	 * Write the metdatadata-xml in the videoresource folder
-	 * @param metaData
-	 * @param videoResource
-	 */
-	private void writeVideoMetadataFile(VideoMetadata metaData, OLATResource videoResource){
-		VFSContainer baseContainer= FileResourceManager.getInstance().getFileResourceRootImpl(videoResource);
-		VFSLeaf metaDataFile = VFSManager.resolveOrCreateLeafFromPath(baseContainer, FILENAME_VIDEO_METADATA_XML);
-		XStreamHelper.writeObject(XStreamHelper.createXStreamInstance(), metaDataFile, metaData);
-	}
+//	/**
+//	 * Write the metdatadata-xml in the videoresource folder
+//	 * @param metaData
+//	 * @param videoResource
+//	 */
+//	private void writeVideoMetadataFile(VideoMetadata metaData, OLATResource videoResource){
+//		VFSContainer baseContainer= FileResourceManager.getInstance().getFileResourceRootImpl(videoResource);
+//		VFSLeaf metaDataFile = VFSManager.resolveOrCreateLeafFromPath(baseContainer, FILENAME_VIDEO_METADATA_XML);
+//		XStreamHelper.writeObject(XStreamHelper.createXStreamInstance(), metaDataFile, metaData);
+//	}
 
 	@Override
 	public VideoMetadata readVideoMetadataFile(OLATResource videoResource){
@@ -321,10 +340,17 @@ public class VideoManagerImpl implements VideoManager {
 		}
 	}
 	
+	@Override
+	public void startTranscodingProcessIfEnabled(OLATResource video) {
+		if (videoModule.isTranscodingEnabled()) {
+			startTranscodingProcess(video);
+		}
+	}
+	
 	@Override
 	public void startTranscodingProcess(OLATResource video) {
 		List<VideoTranscoding> existingTranscodings = getVideoTranscodings(video);
-		VideoMetadata videoMetadata = readVideoMetadataFile(video);
+		VideoMeta videoMetadata = getVideoMetadata(video);
 		int height = videoMetadata.getHeight();
 		// 1) setup transcoding job for original file size
 		createTranscodingIfNotCreatedAlready(video, height, VideoTranscoding.FORMAT_MP4, existingTranscodings);
@@ -374,6 +400,24 @@ public class VideoManagerImpl implements VideoManager {
 		return videoTranscodings;
 	}
 	
+	@Override
+	public List<VideoTranscoding> getAllVideoTranscodings() {
+		List<VideoTranscoding> videoTranscodings = videoTranscodingDao.getAllVideoTranscodings();
+		return videoTranscodings;
+	}
+	
+	@Override 
+	public List<TranscodingCount> getAllVideoTranscodingsCount() {
+		List<TranscodingCount> allVideoTranscodings = videoTranscodingDao.getAllVideoTranscodingsCount();
+		return allVideoTranscodings;
+	}
+	
+	@Override
+	public List<VideoTranscoding> getOneVideoResolution(int resolution) {
+		List<VideoTranscoding> oneResolution = videoTranscodingDao.getOneVideoResolution(resolution);
+		return oneResolution;
+	}
+	
 
 	@Override
 	public String getAspectRatio(int width, int height) {
@@ -489,15 +533,15 @@ public class VideoManagerImpl implements VideoManager {
 		try {
 			zipFile = new ZipFile(file);
 			// 1) Check if it contains a metadata file
-			ZipEntry metadataEntry = zipFile.getEntry(VideoManagerImpl.FILENAME_VIDEO_METADATA_XML);
-			VideoMetadata videoMetadataImpl = null;
-			if (metadataEntry != null) {
-				InputStream metaDataStream = zipFile.getInputStream(metadataEntry);
-				videoMetadataImpl = (VideoMetadata) XStreamHelper.readObject(XStreamHelper.createXStreamInstance(), metaDataStream);
-				if (videoMetadataImpl != null) {
-					eval.setValid(true);
-				}
-			}
+//			ZipEntry metadataEntry = zipFile.getEntry(VideoManagerImpl.FILENAME_VIDEO_METADATA_XML);
+//			VideoMetadata videoMetadataImpl = null;
+//			if (metadataEntry != null) {// does no harm
+//				InputStream metaDataStream = zipFile.getInputStream(metadataEntry);
+//				videoMetadataImpl = (VideoMetadata) XStreamHelper.readObject(XStreamHelper.createXStreamInstance(), metaDataStream);
+//				if (videoMetadataImpl != null) {
+//					eval.setValid(true);
+//				}
+//			}
 			// 2) Propose title from repo metadata
 			ZipEntry repoMetadataEntry = zipFile.getEntry(DIRNAME_REPOENTRY + "/" + RepositoryEntryImportExport.PROPERTIES_FILE);
 			RepositoryEntryImport repoMetadata = null;
@@ -525,17 +569,6 @@ public class VideoManagerImpl implements VideoManager {
 		VFSManager.copyContent(masterVideo, targetFile);
 		masterVideo.delete();
 
-		// 2) generate Metadata file
-		VideoMetadata metaData = new VideoMetadataImpl();
-		// calculate video size
-		Size videoSize = movieService.getSize(targetFile, FILETYPE_MP4);
-		if (videoSize != null) {
-			metaData.setWidth(videoSize.getWidth());
-			metaData.setHeight(videoSize.getHeight());			
-		} else {
-			metaData.setHeight(600);
-			metaData.setWidth(800);						
-		}
 		// calculate video duration
 		long duration = movieService.getDuration(targetFile, FILETYPE_MP4);
 		if (duration != -1) {
@@ -544,42 +577,56 @@ public class VideoManagerImpl implements VideoManager {
 		// generate a poster image, use 20th frame as a default
 		VFSLeaf posterResource = VFSManager.resolveOrCreateLeafFromPath(masterContainer, FILENAME_POSTER_JPG);
 		getFrame(videoResource, 20, posterResource);
-		// finally safe to disk
-		writeVideoMetadataFile(metaData, videoResource);
 
-		// 4) Set poster image for repo entry
+		// 2) Set poster image for repo entry
 		VFSLeaf posterImage = (VFSLeaf)masterContainer.resolve(FILENAME_POSTER_JPG);
 		if (posterImage != null) {
 			repositoryManager.setImage(posterImage, repoEntry);
 		}
-		
-		// 5) start transcoding process
-		if (videoModule.isTranscodingEnabled()) {
-			startTranscodingProcess(videoResource);
-		}
-		
+				
 		return true;
 	}
 	
 	@Override
-	public VideoMetadata getMetaDataFromOLATResource (OLATResource videoResource){
+	public Size getVideoResolutionFromOLATResource (OLATResource videoResource) {
 		VFSContainer masterContainer = getMasterContainer(videoResource);
 		VFSLeaf targetFile = (VFSLeaf) masterContainer.resolve(FILENAME_VIDEO_MP4);
-		
-		// 1) generate Metadata file
-		VideoMetadata metaData = new VideoMetadataImpl();
-		// calculate video size
 		Size videoSize = movieService.getSize(targetFile, FILETYPE_MP4);
-		if (videoSize != null) {
-			metaData.setWidth(videoSize.getWidth());
-			metaData.setHeight(videoSize.getHeight());			
-		} else {
-			metaData.setWidth(800);
-			metaData.setHeight(600);						
+		if (videoSize == null) {
+			videoSize = new Size(800, 600, false);
+		}
+		return videoSize;
+	}
+	
+	@Override
+	public void exchangePoster (OLATResource videoResource) {
+		VFSContainer masterContainer = getMasterContainer(videoResource);
+		VFSLeaf posterResource = VFSManager.resolveOrCreateLeafFromPath(masterContainer, FILENAME_POSTER_JPG);
+		getFrame(videoResource, 20, posterResource);
+		// Update also repository entry image, use new posterframe
+		VFSLeaf posterImage = (VFSLeaf)masterContainer.resolve(FILENAME_POSTER_JPG);
+		if (posterImage != null) {
+			RepositoryEntry repoEntry = repositoryManager.lookupRepositoryEntry(videoResource, true);
+			repositoryManager.setImage(posterImage, repoEntry);
+		}
+	}
+	
+	@Override
+	public void updateVideoMetadata (OLATResource videoResource,VFSLeaf uploadVideo) {	
+		VideoMeta meta = getVideoMetadata(videoResource);
+
+		Size dimensions = movieService.getSize(uploadVideo, VideoManagerImpl.FILETYPE_MP4);
+		// update video duration
+		long duration = movieService.getDuration(uploadVideo, VideoTranscoding.FORMAT_MP4);
+
+		if (duration != -1) {
+			String length = Formatter.formatTimecode(duration);
+			meta.setSize(uploadVideo.getSize());
+			meta.setWidth(dimensions.getWidth());
+			meta.setHeight(dimensions.getHeight());
+			meta.setFormat(FilenameUtils.getExtension(uploadVideo.getName()));
+			meta.setLength(length);
 		}
-		// 2) update XML file
-		writeVideoMetadataFile(metaData, videoResource);
-		return metaData;
 	}
 
 	@Override
@@ -605,10 +652,6 @@ public class VideoManagerImpl implements VideoManager {
 		if (posterImage != null) {
 			repositoryManager.setImage(posterImage, repoEntry);
 		}
-		// 3) start transcoding process
-		if (videoModule.isTranscodingEnabled()) {
-			startTranscodingProcess(videoResource);
-		}
 
 		return true;
 	}
@@ -636,6 +679,12 @@ public class VideoManagerImpl implements VideoManager {
 		VFSStatus deleteStatus = getTranscodingContainer(videoResource).delete();
 		return (deleteStatus == VFSConstants.YES ? true : false);
 	}
+	
+	@Override
+	public boolean deleteVideoMetadata(OLATResource videoResource) {
+		int deleted = videoMetadataDao.deleteVideoMetadata(videoResource);
+		return 0 < deleted;
+	}
 
 	@Override
 	public List<VideoTranscoding> getVideoTranscodingsPendingAndInProgress() {
@@ -760,5 +809,27 @@ public class VideoManagerImpl implements VideoManager {
 		long duration = movieService.getDuration(video, FILETYPE_MP4);
 		return duration;
 	}
+	
+	@Override
+	public List<VideoMetaImpl> getAllVideoResourcesMetadata() {
+		List<VideoMetaImpl> metadata = videoMetadataDao.getAllVideoResourcesMetadata();
+		return metadata;
+	}
+	
+	@Override
+	public VideoMetaImpl getVideoMetadata(OLATResource videoResource) {
+		VideoMetaImpl meta = videoMetadataDao.getVideoMetadata(videoResource);
+		return meta;
+	}
+	
+	@Override 
+	public VideoMeta createVideoMetadata(RepositoryEntry repoEntry, long size, String fileName) {
+		return videoMetadataDao.createVideoMetadata(repoEntry, size, fileName); 
+	}
+	
+	@Override
+	public List<RepositoryEntry> getAllVideoRepoEntries(String typename) {
+		return videoMetadataDao.getAllVideoRepoEntries(typename);
+	}
 
 }
diff --git a/src/main/java/org/olat/modules/video/manager/VideoMetadataDAO.java b/src/main/java/org/olat/modules/video/manager/VideoMetadataDAO.java
new file mode 100644
index 00000000000..992d757d8a7
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/manager/VideoMetadataDAO.java
@@ -0,0 +1,142 @@
+/**
+ * <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.manager;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.io.FilenameUtils;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.image.Size;
+import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.model.VideoMetaImpl;
+import org.olat.repository.RepositoryEntry;
+import org.olat.resource.OLATResource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * DAO implementation for manipulating VideoMetadata objects
+ * 
+ * Initial date: January 2017<br>
+ * 
+ * @author fkiefer, fabian.kiefer@frentix.com, http://www.frentix.com
+ *
+ */
+@Service("videoMetadataDao")
+public class VideoMetadataDAO {
+
+	@Autowired
+	private DB dbInstance;
+	@Autowired 
+	private VideoManager videoManager;
+
+	
+	/**
+	 * Gets the video meta data. 
+	 *
+	 * @param videoResource the OLATResource
+	 * @return the videometadata or null
+	 */
+	VideoMetaImpl getVideoMetadata(OLATResource videoResource) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select meta from videometadata as meta")
+			.append(" where meta.videoResource=:videoresource");
+		List<VideoMetaImpl> metadata = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(),VideoMetaImpl.class)
+				.setParameter("videoresource", videoResource)
+				.getResultList();
+		if (metadata.size() > 0) {
+			return metadata.get(0);
+		} else {
+			return null;
+		}
+	}
+	
+	/**
+	 * Gets the all video resources metadata.
+	 *
+	 * @return the all video resources metadata
+	 */
+	List<VideoMetaImpl> getAllVideoResourcesMetadata () {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select meta from videometadata as meta")
+			.append(" order by meta.creationDate asc, meta.id asc");
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(),VideoMetaImpl.class)
+				.getResultList();
+	}
+	
+	/** 
+	 * Gets the all video repo entries by type.
+	 *
+	 * @param typename
+	 * @return all video repo entries
+	 */
+	List<RepositoryEntry> getAllVideoRepoEntries (String typename) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" v ")
+		  .append(" inner join fetch v.olatResource as ores")
+		  .append(" where ores.resName = :type");
+		List<RepositoryEntry> result = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), RepositoryEntry.class)
+				.setParameter("type",typename)
+				.getResultList();
+		return result;
+	}
+	
+	/**
+	 * Delete all video metadata objects for a given video resource
+	 *
+	 * @param videoResource
+	 * @return the number of entities updated or deleted
+	 */
+	int deleteVideoMetadata(OLATResource videoResource) {
+		String deleteQuery = "delete from videometadata where fk_resource_id=:resourceKey";
+		return dbInstance.getCurrentEntityManager().createQuery(deleteQuery)
+				.setParameter("resourceKey", videoResource.getKey()).executeUpdate();
+	}
+	
+	/**
+	 * Creates and persists the video metadata.
+	 *
+	 * @param videoResource
+	 * @param size
+	 * @param filename
+	 * @return metadata
+	 */
+	VideoMetaImpl createVideoMetadata(RepositoryEntry repoEntry, long size, String fileName) {
+		VideoMetaImpl videometa = new VideoMetaImpl();
+		OLATResource videoResource = repoEntry.getOlatResource();
+		videometa.setVideoResource(videoResource);
+		String format = FilenameUtils.getExtension(fileName);
+		videometa.setFormat(format);
+		videometa.setCreationDate(new Date());
+		videometa.setLastModified(new Date());		
+		Size resolution = videoManager.getVideoResolutionFromOLATResource(videoResource);
+		videometa.setHeight(resolution.getHeight());
+		videometa.setWidth(resolution.getWidth());
+		videometa.setSize(size);
+		videometa.setLength(repoEntry.getExpenditureOfWork());
+		dbInstance.getCurrentEntityManager().persist(videometa);
+		return videometa;
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java b/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java
index 3e2a3572620..a4a8091b1bf 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java
@@ -19,13 +19,16 @@
  */
 package org.olat.modules.video.manager;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
 import javax.persistence.EntityNotFoundException;
 
 import org.olat.core.commons.persistence.DB;
+import org.olat.modules.video.VideoManager;
 import org.olat.modules.video.VideoTranscoding;
+import org.olat.modules.video.model.TranscodingCount;
 import org.olat.modules.video.model.VideoTranscodingImpl;
 import org.olat.resource.OLATResource;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -44,6 +47,8 @@ public class VideoTranscodingDAO {
 
 	@Autowired
 	private DB dbInstance;
+	@Autowired 
+	private VideoManager videoManager;
 
 	/**
 	 * Factory method to create and persist new video transcoding objects for a
@@ -117,8 +122,65 @@ public class VideoTranscodingDAO {
 			.append(" inner join fetch trans.videoResource as res")
 			.append(" where res.key=:resourceKey")
 			.append(" order by trans.resolution desc");
-		return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), VideoTranscoding.class)
-				.setParameter("resourceKey", videoResource.getKey()).getResultList();
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), VideoTranscoding.class)
+				.setParameter("resourceKey", videoResource.getKey())
+				.getResultList();
+	}
+	
+	/**
+	 * Gets all video transcodings.
+	 *
+	 * @return all video transcodings
+	 */
+	List<VideoTranscoding> getAllVideoTranscodings() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select trans from videotranscoding as trans")
+			.append(" order by trans.resolution desc");
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), VideoTranscoding.class)
+				.getResultList();
+	}
+	
+	/**
+	 * Gets all transcodings of one video resolution.
+	 *
+	 * @param resolution
+	 * @return all videos of one resolution
+	 */
+	List<VideoTranscoding> getOneVideoResolution (int resolution) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select trans from videotranscoding as trans")
+			.append(" where trans.resolution=:resolution")
+			.append(" order by trans.lastModified desc");
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), VideoTranscoding.class)
+				.setParameter("resolution", resolution)
+				.getResultList();
+	}
+	
+	/**
+	 * Gets the all video transcodings count.
+	 *
+	 * @return the all video transcodings count
+	 */
+	List<TranscodingCount> getAllVideoTranscodingsCount() {
+		StringBuilder sb = new StringBuilder();
+		//[0] count of a distinct [1] resolution
+		sb.append("select count(trans.key), trans.resolution from videotranscoding as trans")
+		  .append(" group by trans.resolution");
+		
+		List<Object[]> rawData = dbInstance.getCurrentEntityManager()
+			.createQuery(sb.toString(), Object[].class)
+			.getResultList();
+		
+		List<TranscodingCount>  allTranscodings = new ArrayList<>();
+		for (Object[] data : rawData) {
+			Long count = (Long) data[0];
+			Integer resolution = (Integer) data[1];
+			allTranscodings.add(new TranscodingCount(count, resolution));
+		}
+		return allTranscodings;
 	}
 
 	/**
diff --git a/src/main/java/org/olat/modules/video/model/TranscodingCount.java b/src/main/java/org/olat/modules/video/model/TranscodingCount.java
new file mode 100644
index 00000000000..96885fa4725
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/model/TranscodingCount.java
@@ -0,0 +1,23 @@
+package org.olat.modules.video.model;
+
+public class TranscodingCount {
+	
+	private Long count;
+	private Integer resolution;
+
+	public TranscodingCount(Long count, Integer resolution) {
+		this.count = count;
+		this.resolution = resolution;
+	}
+
+	public int getCount() {
+		return  Math.toIntExact(count);
+	}
+
+	public int getResolution() {
+		return resolution;
+	}
+	
+	
+
+}
diff --git a/src/main/java/org/olat/modules/video/model/VideoMetaImpl.java b/src/main/java/org/olat/modules/video/model/VideoMetaImpl.java
new file mode 100644
index 00000000000..31b049cd440
--- /dev/null
+++ b/src/main/java/org/olat/modules/video/model/VideoMetaImpl.java
@@ -0,0 +1,190 @@
+/**
+ * <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.model;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.olat.core.id.ModifiedInfo;
+import org.olat.core.id.Persistable;
+import org.olat.modules.video.VideoMeta;
+import org.olat.resource.OLATResource;
+import org.olat.resource.OLATResourceImpl;
+
+/**
+ * The Class VideoMetaImpl.
+ * Initial Date: January 2017
+ * @author fkiefer fabian.kiefer@frentix.com
+ */
+@Entity(name="videometadata")
+@Table(name="o_vid_metadata")
+public class VideoMetaImpl implements VideoMeta, Persistable, ModifiedInfo {
+
+	private static final long serialVersionUID = 8360426958L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="lastmodified", nullable=false, insertable=true, updatable=true)
+	private Date lastModified;
+	
+	@OneToOne(targetEntity=OLATResourceImpl.class,fetch=FetchType.LAZY,optional=false)
+	@JoinColumn(name="fk_resource_id", nullable=false, insertable=true, updatable=false)
+	private OLATResource videoResource;
+	
+	@Column(name="vid_width", nullable=true, insertable=true, updatable=true)
+	private int width;
+	@Column(name="vid_height", nullable=true, insertable=true, updatable=true)
+	private int height;
+	@Column(name="vid_size", nullable=true, insertable=true, updatable=true)
+	private long size;
+	@Column(name="vid_format", nullable=true, insertable=true, updatable=true)
+	private String format;
+	@Column(name="vid_length", nullable=true, insertable=true, updatable=true)
+	private String length;	
+	
+
+
+	public VideoMetaImpl(OLATResource videoResource, int width, int height, long size, String format, String length) {
+		super();
+		this.creationDate = new Date();
+		this.lastModified = new Date();
+		this.videoResource = videoResource;
+		this.width = width;
+		this.height = height;
+		this.size = size;
+		this.format = format;
+		this.length = length;
+	}
+	
+
+	public VideoMetaImpl() {
+		// make JAXB happy
+	}
+	
+	@Override
+	public void setVideoResource(OLATResource videoResource) {
+		this.videoResource = videoResource;
+	}
+
+	@Override 
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	@Override
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	@Override
+	public void setLastModified(Date date) {
+		this.lastModified = date;
+	}
+
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return false;
+	}
+
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	@Override
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	@Override
+	public OLATResource getVideoResource() {
+		return videoResource;
+	}
+
+	@Override
+	public int getWidth() {
+		return width;
+	}
+
+	@Override
+	public void setWidth(int width) {
+		this.width = width;
+	}
+
+	@Override
+	public int getHeight() {
+		return height;
+	}
+
+	@Override
+	public void setHeight(int height) {
+		this.height = height;
+	}
+
+	@Override
+	public long getSize() {
+		return size;
+	}
+
+	@Override
+	public void setSize(long size) {
+		this.size = size;
+	}
+
+	@Override
+	public String getFormat() {
+		return format;
+	}
+
+	@Override
+	public void setFormat(String format) {
+		this.format = format;
+	}
+
+	@Override
+	public String getLength() {
+		return length;
+	}
+
+	@Override
+	public void setLength(String length) {
+		this.length = length;
+	}
+
+
+}
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 0fcc0a9204c..8060dadc148 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoAdminController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoAdminController.java
@@ -107,7 +107,7 @@ public class VideoAdminController extends BasicController  {
 			listenTo(adminTranscodingController);
 		}
 		mainVC.put("segmentCmp", adminTranscodingController.getInitialComponent());
-		adminTranscodingController.setChecks();
+		adminTranscodingController.reloadTable();
 	}
 
 	@Override
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 91d64738283..f0d8d4f29da 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoAdminTranscodingController.java
@@ -41,12 +41,12 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlex
 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.VideoModule;
 import org.olat.modules.video.VideoTranscoding;
+import org.olat.modules.video.model.TranscodingCount;
+import org.olat.modules.video.model.VideoMetaImpl;
 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;
 
 /**
@@ -58,15 +58,11 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 public class VideoAdminTranscodingController extends FormBasicController {
 	
-	private Map<Integer,Set<OLATResource>> availableTranscodings;
-	private List<OLATResource> olatresources;
 	private TranscodingTableModel tableModel;
 	private FlexiTableElement transcodingTable;
 	
-	private List<TranscodingRow> resolutions;
+	private Map<OLATResource,Integer> nativeResolutions;
 	
-	@Autowired
-	private OLATResourceManager olatresourceManager;
 	@Autowired 
 	private VideoManager videoManager;
 	@Autowired
@@ -74,8 +70,13 @@ public class VideoAdminTranscodingController extends FormBasicController {
 
 	public VideoAdminTranscodingController(UserRequest ureq, WindowControl wControl) {
 		super(ureq, wControl, "transcoding_admin");
-		resolutions = new ArrayList<>();
-		generateStatusOfTranscodings();
+		nativeResolutions = new HashMap<>();
+		
+		List<VideoMetaImpl> olatresources = videoManager.getAllVideoResourcesMetadata();
+		//cache native resolutions
+		for (VideoMetaImpl videoResource : olatresources) {
+			nativeResolutions.put(videoResource.getVideoResource(), videoResource.getHeight());
+		}
 
 		initForm(ureq);
 	}
@@ -101,7 +102,7 @@ public class VideoAdminTranscodingController extends FormBasicController {
 		transcodingTable.setCustomizeColumns(false);
 		transcodingTable.setNumOfRowsEnabled(false);
 				
-		setChecks();
+		loadTable();
 	}
 	
 	private boolean mayTranscode(int resolution){
@@ -116,35 +117,37 @@ public class VideoAdminTranscodingController extends FormBasicController {
 		}
 		return false;
 	}
-	
-	private void loadTable(){
-		//Hardcoded same as VideoAdminSetController
-		int[] resolution = {2160, 1080, 720, 480, 360, 240};
-		//FIXME:FK fetch using one single SQL query
-		for (int i = 0; i < resolution.length; i++) {
-			int sizeOfTranscodings = availableTranscodings.get(resolution[i]).size();
+
+	private void loadTable() {
+		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());
+		}
+		for (int i = 0; i < fixresolution.length; i++) {
 			int counter = 0;
-			for (OLATResource videoResource : olatresources) {
-				VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource);
-				if (videoMetadata != null && videoMetadata.getHeight() >= resolution[i]) counter++;
+			for (OLATResource videoResource : nativeResolutions.keySet()) {
+				if (nativeResolutions.get(videoResource) >= fixresolution[i]) counter++;
 			}
-			resolutions.add(new TranscodingRow(resolution[i], sizeOfTranscodings, counter, mayTranscode(resolution[i])));
-		}		
-		if (resolutions != null) tableModel.setObjects(resolutions);
-		transcodingTable.reset(true, true, true);	
+			int rescount = resCount.get(fixresolution[i]) != null ? resCount.get(fixresolution[i]) : 0;
+			resolutions.add(new TranscodingRow(fixresolution[i], rescount, counter, mayTranscode(fixresolution[i])));
+		}
+		if (resolutions != null){
+			tableModel.setObjects(resolutions);
+		}
+		transcodingTable.reset(true, true, true);
 	}
 	
+	
 	/**
 	 * Update Table Content of all available Transcodings
 	 */
-	public void setChecks(){	
-		generateStatusOfTranscodings();
-		resolutions.clear();
+	public void reloadTable(){	
 		loadTable();
 	}
 
-
-	
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if(source == transcodingTable) {
@@ -160,65 +163,40 @@ public class VideoAdminTranscodingController extends FormBasicController {
 				} 
 			}
 		}
-		
-		//refresh checks
-		setChecks();
+		reloadTable();
 	}
 	
-	private void generateStatusOfTranscodings() {
-		//FIXME:FK fetch using one single SQL query
-		availableTranscodings = new HashMap<>();
-		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");
-		//retrieve all resources of type video
-		olatresources = olatresourceManager.findResourceByTypes(types);
-		//go through all video resources
-		for (OLATResource videoResource : olatresources) {
-			//retrieve all transcodings for each video resource
-			List<VideoTranscoding> transcodings = videoManager.getVideoTranscodings(videoResource);
-			//map resource IDs to resolution
-			for (VideoTranscoding videoTranscoding : transcodings) {
-				if (videoTranscoding != null) {
-					Set<OLATResource> oneResolution = availableTranscodings.get(videoTranscoding.getResolution());
-					if (oneResolution != null) {
-						oneResolution.add(videoTranscoding.getVideoResource());						
-					}
-				}
+	
+	// state orders for inexistent transcodings
+	private void queueCreateTranscoding(TranscodingRow source) {
+		List<VideoTranscoding> allVideoTranscodings = videoManager.getOneVideoResolution(source.getResolution());
+		Map<OLATResource, Set<Integer>> availableTranscodings = new HashMap<>();
+		for (VideoTranscoding videoTranscoding : allVideoTranscodings) {
+			if (availableTranscodings.containsKey(videoTranscoding.getVideoResource())) {
+				availableTranscodings.get(videoTranscoding.getVideoResource()).add(videoTranscoding.getResolution());
+			} else {
+				Set<Integer> availableresolutions = new HashSet<>();
+				availableresolutions.add(videoTranscoding.getResolution());
+				availableTranscodings.put(videoTranscoding.getVideoResource(), availableresolutions);
 			}
 		}
-	}
-	
-	
-	//state orders for inexistent transcodings
-	private void queueCreateTranscoding(TranscodingRow source){
-		for (OLATResource videoResource : olatresources) {
-			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");				
+		for (OLATResource videoResource : nativeResolutions.keySet()) {
+			if (availableTranscodings.get(videoResource) == null ||
+					!availableTranscodings.get(videoResource).contains(source.getResolution())) {
+				if (nativeResolutions.get(videoResource) >= source.getResolution()) {
+					videoManager.createTranscoding(videoResource, source.getResolution(), "mp4");
 				}
 			}
 		}
 	}
 	
+	
 	//go through all and delete selection
 	private void queueDeleteTranscoding(TranscodingRow source) {
-		for (OLATResource videoResource : olatresources) {
-			if (availableTranscodings.get(source.getResolution()).contains(videoResource)) {
-				List<VideoTranscoding> videoTranscodings = videoManager.getVideoTranscodings(videoResource);
-
-				for (VideoTranscoding videoTranscoding : videoTranscodings) {
-					if (videoTranscoding.getResolution() == source.getResolution()) {
-						videoManager.deleteVideoTranscoding(videoTranscoding);
-					}
-				}
+		List<VideoTranscoding> allVideoTranscodings = videoManager.getOneVideoResolution(source.getResolution());
+		for (VideoTranscoding videoTranscoding : allVideoTranscodings) {
+			if (videoTranscoding.getResolution() == source.getResolution()) {
+				videoManager.deleteVideoTranscoding(videoTranscoding);
 			}
 		}
 	}
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 9ca6d4ecca0..cd3abd12cb5 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java
@@ -22,6 +22,7 @@ package org.olat.modules.video.ui;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.olat.core.commons.services.commentAndRating.CommentAndRatingDefaultSecurityCallback;
 import org.olat.core.commons.services.commentAndRating.CommentAndRatingSecurityCallback;
@@ -45,7 +46,7 @@ import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSContainerMapper;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.modules.video.VideoManager;
-import org.olat.modules.video.VideoMetadata;
+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;
@@ -115,7 +116,7 @@ public class VideoDisplayController extends BasicController {
 				
 		VFSLeaf video = videoManager.getMasterVideoFile(entry.getOlatResource());
 		if(video != null) {
-			VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(entry.getOlatResource());
+			VideoMeta videoMetadata = videoManager.getVideoMetadata(entry.getOlatResource());
 			if(autoWidth){
 				mainVC.contextPut("height", 480);
 				mainVC.contextPut("width", "100%");
@@ -230,8 +231,8 @@ public class VideoDisplayController extends BasicController {
 			mainVC.contextPut("usePoster", Boolean.valueOf(poster != null && poster.getSize() > 0));
 			
 			// Load the track from config
-			HashMap<String, String> trackfiles = new HashMap<String, String>();
-			HashMap<String, VFSLeaf> configTracks = videoManager.getAllTracks(entry.getOlatResource());
+			Map<String, String> trackfiles = new HashMap<String, String>();
+			Map<String, VFSLeaf> configTracks = videoManager.getAllTracks(entry.getOlatResource());
 			for (HashMap.Entry<String, VFSLeaf> track : configTracks.entrySet()) {
 				trackfiles.put(track.getKey(), track.getValue().getName());
 			}
diff --git a/src/main/java/org/olat/modules/video/ui/VideoMetaDataEditFormController.java b/src/main/java/org/olat/modules/video/ui/VideoMetaDataEditFormController.java
index cb0e145a27f..9e32fcba28f 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoMetaDataEditFormController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoMetaDataEditFormController.java
@@ -27,7 +27,7 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.util.Formatter;
 import org.olat.core.util.StringHelper;
 import org.olat.modules.video.VideoManager;
-import org.olat.modules.video.VideoMetadata;
+import org.olat.modules.video.VideoMeta;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -56,7 +56,7 @@ public class VideoMetaDataEditFormController extends FormBasicController {
 		
 		OLATResource videoResource = repoEntry.getOlatResource();
 		
-		VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource);
+		VideoMeta videoMetadata = videoManager.getVideoMetadata(videoResource);
 		uifactory.addStaticTextElement("video.config.duration", repoEntry.getExpenditureOfWork(), formLayout);
 
 		uifactory.addStaticTextElement("video.config.width", String.valueOf(videoMetadata.getWidth()) + "px", formLayout);
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 e0a95042d37..eff5876ed81 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
@@ -45,7 +45,7 @@ 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.VideoMetadata;
+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;
@@ -103,7 +103,7 @@ public class VideoQualityTableFormController extends FormBasicController {
 	
 	private void initTable(){
 		List<QualityTableRow> rows = new ArrayList<>();
-		VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource);
+		VideoMeta videoMetadata = videoManager.getVideoMetadata(videoResource);
 		// Add master video file
 		FormLink previewMasterLink = uifactory.addFormLink("view", "viewQuality", "quality.master", "quality.master", flc, Link.LINK);
 		rows.add(new QualityTableRow(previewMasterLink, videoMetadata.getWidth() +"x"+ videoMetadata.getHeight(), Formatter.formatBytes(videoManager.getVideoFile(videoResource).length()), "mp4",null));
@@ -200,7 +200,8 @@ public class VideoQualityTableFormController extends FormBasicController {
 			VideoTranscoding videoTranscoding = (VideoTranscoding) link.getUserObject();
 			if (videoTranscoding == null) {
 				// this is the master video
-				VideoMetadata videoMetadata = videoManager.readVideoMetadataFile(videoResource);
+//				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");
diff --git a/src/main/java/org/olat/modules/video/ui/VideoResourceEditController.java b/src/main/java/org/olat/modules/video/ui/VideoResourceEditController.java
index 40c6284bd44..f551b32e398 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoResourceEditController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoResourceEditController.java
@@ -24,6 +24,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.commons.io.FilenameUtils;
+import org.olat.core.commons.services.image.Size;
 import org.olat.core.commons.services.video.MovieService;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -39,9 +41,10 @@ import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSManager;
 import org.olat.modules.video.VideoManager;
-import org.olat.modules.video.VideoMetadata;
+import org.olat.modules.video.VideoMeta;
 import org.olat.modules.video.VideoModule;
 import org.olat.modules.video.VideoTranscoding;
+import org.olat.modules.video.manager.VideoManagerImpl;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -108,19 +111,32 @@ public class VideoResourceEditController extends FormBasicController {
 
 	}
 
-	private void doReplace() {
+	private int doReplaceFileAndUpdateMetadata() {
 		VFSLeaf video = (VFSLeaf) vfsContainer.resolve(VIDEO_RESOURCE);		
 		File uploadFile = uploadFileEl.getUploadFile();
+//		VideoMetadata videoMetadata = videoManager.getMetaDataFromOLATResource(videoResource);
+		VideoMeta meta = videoManager.getVideoMetadata(videoResource);
 		if (uploadFileEl.getUploadSize() > 0 && uploadFile.exists()){
 			video.delete();
 			VFSLeaf uploadVideo = vfsContainer.createChildLeaf(VIDEO_RESOURCE);
 			VFSManager.copyContent(uploadFile, uploadVideo);
+			//update video dimensions
+			Size dimensions = movieService.getSize(uploadVideo, VideoManagerImpl.FILETYPE_MP4);
 			// update video duration
 			long duration = movieService.getDuration(uploadVideo, VideoTranscoding.FORMAT_MP4);
+			// exchange poster
+			videoManager.exchangePoster(videoResource);
 			if (duration != -1) {
-				entry.setExpenditureOfWork(Formatter.formatTimecode(duration));
+				String length = Formatter.formatTimecode(duration);
+				entry.setExpenditureOfWork(length);
+				meta.setSize(uploadFile.length());
+				meta.setWidth(dimensions.getWidth());
+				meta.setHeight(dimensions.getHeight());
+				meta.setFormat(FilenameUtils.getExtension(uploadVideo.getName()));
+				meta.setLength(length);
 			}
 		} 
+		return meta.getHeight();
 	}
 
 	private void queueDeleteTranscoding() {
@@ -130,10 +146,9 @@ public class VideoResourceEditController extends FormBasicController {
 		}
 	}
 	
-	private void queueCreateTranscoding() {
+	private void queueCreateTranscoding(int height) {
 		List<Integer> missingResolutions = videoManager.getMissingTranscodings(videoResource);
-		VideoMetadata videoMetadata = videoManager.getMetaDataFromOLATResource(videoResource);
-		int height = videoMetadata.getHeight();
+
 		if (videoModule.isTranscodingEnabled()) {
 			// 1) setup transcoding job for original file size
 			videoManager.createTranscoding(videoResource, height, VideoTranscoding.FORMAT_MP4);
@@ -150,8 +165,8 @@ public class VideoResourceEditController extends FormBasicController {
 	protected void formOK(UserRequest ureq) {
 		if (uploadFileEl.getUploadFile() != null && uploadFileEl.isUploadSuccess()) {
 			queueDeleteTranscoding();
-			doReplace();
-			queueCreateTranscoding();
+			int height = doReplaceFileAndUpdateMetadata();
+			queueCreateTranscoding(height);
 			typeEl.setValue(translate("admin.menu.title"));
 			typeEl.setVisible(true);
 			showInfo("video.replaced");
diff --git a/src/main/java/org/olat/modules/video/ui/VideoTrackEditController.java b/src/main/java/org/olat/modules/video/ui/VideoTrackEditController.java
index b5f59345900..426c6928592 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoTrackEditController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoTrackEditController.java
@@ -91,7 +91,7 @@ public class VideoTrackEditController extends FormBasicController {
 
 		tableEl = uifactory.addTableElement(getWindowControl(), "tracks", tableModel, getTranslator(), generalCont);
 		tableEl.setCustomizeColumns(false);
-		HashMap<String, VFSLeaf> tracks = videoManager.getAllTracks(videoResource);
+		Map<String, VFSLeaf> tracks = videoManager.getAllTracks(videoResource);
 		rows = new HashMap<String,TrackTableRow>(tracks.size());
 		if (!tracks.isEmpty()) {
 			for (Map.Entry<String, VFSLeaf> entry : tracks.entrySet()) {
@@ -135,7 +135,6 @@ public class VideoTrackEditController extends FormBasicController {
 	@Override
 	public void event(UserRequest ureq, Controller source, Event event) {
 		if(source == trackUploadForm){
-			videoManager.addTrack(videoResource, trackUploadForm.getLang(),(VFSLeaf) ((FolderEvent) event).getItem());
 			rows.put(trackUploadForm.getLang(), new TrackTableRow(trackUploadForm.getLang(), (VFSLeaf) ((FolderEvent) event).getItem(), uifactory.addFormLink(trackUploadForm.getLang(),"deleteTrack", "track.delete", "track.delete", null, Link.BUTTON)));
 			tableModel.setObjects(new ArrayList<TrackTableRow>(rows.values()));
 			tableEl.reset();
diff --git a/src/main/java/org/olat/modules/video/ui/VideoTrackUploadForm.java b/src/main/java/org/olat/modules/video/ui/VideoTrackUploadForm.java
index 72d7451d63a..26cbc5529ca 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoTrackUploadForm.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoTrackUploadForm.java
@@ -27,6 +27,7 @@ import java.util.Locale;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.apache.commons.io.FilenameUtils;
 import org.olat.core.commons.modules.bc.FolderEvent;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -39,6 +40,7 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.util.vfs.Quota;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.manager.VideoManagerImpl;
 import org.olat.resource.OLATResource;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -51,6 +53,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 
 public class VideoTrackUploadForm extends FormBasicController {
+	
 	private FileElement fileEl;
 	private SingleSelection langsItem;
 	private long remainingSpace;
@@ -107,10 +110,13 @@ public class VideoTrackUploadForm extends FormBasicController {
 					fileEl.getUploadFile().delete();
 					return;
 				}
-			}else{
+			} else {
+				String uploadfilename = VideoManagerImpl.TRACK + langsItem.getSelectedKey() + VideoManagerImpl.DOT
+						+ FilenameUtils.getExtension(fileEl.getUploadFileName());
+				fileEl.setUploadFileName(uploadfilename);
 				fireEvent(ureq, new FolderEvent(FolderEvent.UPLOAD_EVENT, fileEl.moveUploadFileTo(mediaContainer)));
 			}
-		}else{
+		} else {
 			fileEl.setErrorKey("track.upload.error.nofile", null);
 		}
 
@@ -124,7 +130,7 @@ public class VideoTrackUploadForm extends FormBasicController {
 
 	@Override
 	protected void doDispose() {
-		// TODO Auto-generated method stub
+		// nothing to dispose
 
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/repository/handlers/VideoHandler.java b/src/main/java/org/olat/repository/handlers/VideoHandler.java
index e0191d3e440..75587262be9 100644
--- a/src/main/java/org/olat/repository/handlers/VideoHandler.java
+++ b/src/main/java/org/olat/repository/handlers/VideoHandler.java
@@ -116,14 +116,15 @@ public class VideoHandler extends FileHandler {
 		OLATResource resource = OLATResourceManager.getInstance().createAndPersistOLATResourceInstance(ores);
 		RepositoryEntry repoEntry = CoreSpringFactory.getImpl(RepositoryService.class).create(initialAuthor, null, "",
 				displayname, description, resource, RepositoryEntry.ACC_OWNERS);
-		DBFactory.getInstance().commit();		
 		
 		if(fileName == null) {
 			fileName = file.getName();
 		}
 		fileName = fileName.toLowerCase();
-		VFSLeaf importFile = new LocalFileImpl(file);
+		VFSLeaf importFile = new LocalFileImpl(file);	
+		long filesize = importFile.getSize();
 		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
+
 		if (fileName.endsWith(".mp4")|| fileName.endsWith(".mov")) {
 			// 2a) import video from raw mp4 master video file
 			videoManager.importFromMasterFile(repoEntry, importFile);
@@ -131,7 +132,12 @@ public class VideoHandler extends FileHandler {
 		} else if (fileName.endsWith(".zip")) {
 			// 2b) import video from archive from another OpenOLAT instance
 			videoManager.importFromExportArchive(repoEntry, importFile);			
-		}
+		}	
+		// 3) Persist Meta data
+		videoManager.createVideoMetadata(repoEntry, filesize, fileName);
+		DBFactory.getInstance().commit();	
+		// 4) start transcoding process if enabled
+		videoManager.startTranscodingProcessIfEnabled(resource);
 		
 		return repoEntry;
 	}
@@ -231,6 +237,8 @@ public class VideoHandler extends FileHandler {
 			// remove transcodings
 			VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
 			success = videoManager.deleteVideoTranscodings(entry.getOlatResource());
+			//remove metadata
+			success &= videoManager.deleteVideoMetadata(entry.getOlatResource());
 		}
 		return success;
 	}
diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_11_3_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_11_3_0.java
index 74fb1295578..01fb9951136 100644
--- a/src/main/java/org/olat/upgrade/OLATUpgrade_11_3_0.java
+++ b/src/main/java/org/olat/upgrade/OLATUpgrade_11_3_0.java
@@ -19,11 +19,25 @@
  */
 package org.olat.upgrade;
 
-import java.util.ArrayList;
+import java.io.File;
+import java.util.Date;
 import java.util.List;
+import java.util.Map.Entry;
 
+import org.apache.commons.io.FilenameUtils;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.image.Size;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSItem;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.core.util.vfs.VFSManager;
+import org.olat.fileresource.types.VideoFileResource;
+import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.VideoMetadata;
+import org.olat.modules.video.manager.VideoManagerImpl;
+import org.olat.modules.video.model.VideoMetaImpl;
 import org.olat.repository.RepositoryEntry;
+import org.olat.resource.OLATResource;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -39,7 +53,9 @@ public class OLATUpgrade_11_3_0 extends OLATUpgrade {
 	
 	@Autowired
 	private DB dbInstance;
-
+	@Autowired 
+	private VideoManager videoManager;
+	
 	public OLATUpgrade_11_3_0() {
 		super();
 	}
@@ -81,11 +97,11 @@ public class OLATUpgrade_11_3_0 extends OLATUpgrade {
 		boolean allOk = true;
 		if (!uhd.getBooleanDataValue(VIDEO_XML)) {
 			
-			List<RepositoryEntry> entries = new ArrayList<>();
+			List<RepositoryEntry> entries = videoManager.getAllVideoRepoEntries(VideoFileResource.TYPE_NAME);
 			for(RepositoryEntry entry:entries) {
 				if(entry == null) continue;
 
-				processVideoResource(entry);
+				allOk &= processVideoResource(entry);
 				dbInstance.commitAndCloseSession();
 			}
 			
@@ -95,7 +111,46 @@ public class OLATUpgrade_11_3_0 extends OLATUpgrade {
 		return allOk;
 	}
 	
-	private void processVideoResource(RepositoryEntry entry) {
-		//TODO 
+	private boolean processVideoResource(RepositoryEntry entry) {
+		try {
+			OLATResource videoResource = entry.getOlatResource();
+			// update trackfiles on filesystem
+			VFSContainer masterContainer = videoManager.getMasterContainer(videoResource);
+			VideoMetadata metafromXML = videoManager.readVideoMetadataFile(videoResource);
+			for (Entry<String, String> track : metafromXML.getAllTracks().entrySet()) {
+				VFSItem item = masterContainer.resolve(track.getValue());
+				if (item != null && item instanceof VFSLeaf) {
+					String path = VideoManagerImpl.TRACK + track.getKey() + VideoManagerImpl.DOT
+							+ FilenameUtils.getExtension(track.getValue());
+					//check if modified track file already exists
+					if (masterContainer.resolve(path) == null) {
+						VFSLeaf target = masterContainer.createChildLeaf(path);
+						VFSManager.copyContent((VFSLeaf) item, target);
+					}
+				}
+			}
+			// create entries on database
+			File videoFile = videoManager.getVideoFile(videoResource);
+			String fileName = videoFile.getName();
+			long size = videoFile.length();
+			String format = FilenameUtils.getExtension(fileName);
+			if (videoManager.getVideoMetadata(videoResource) == null) {
+				VideoMetaImpl entity = new VideoMetaImpl();
+				entity.setVideoResource(videoResource);
+				entity.setFormat(format);
+				entity.setCreationDate(new Date());
+				entity.setLastModified(new Date());
+				Size resolution = videoManager.getVideoResolutionFromOLATResource(videoResource);
+				entity.setHeight(resolution.getHeight());
+				entity.setWidth(resolution.getWidth());
+				entity.setSize(size);
+				entity.setLength(entry.getExpenditureOfWork());
+				dbInstance.getCurrentEntityManager().persist(entity);
+			}
+			return true;
+		} catch (Exception e) {
+			log.error("Update Metadata failed",e);
+			return false;
+		}
 	}
 }
diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml
index 8dc5fa1791b..f6056eab24c 100644
--- a/src/main/resources/META-INF/persistence.xml
+++ b/src/main/resources/META-INF/persistence.xml
@@ -188,6 +188,7 @@
 		<class>org.olat.modules.reminder.model.ReminderImpl</class>
 		<class>org.olat.modules.reminder.model.SentReminderImpl</class>
 		<class>org.olat.modules.video.model.VideoTranscodingImpl</class>
+		<class>org.olat.modules.video.model.VideoMetaImpl</class>
 		<class>org.olat.ims.lti.model.LTIOutcomeImpl</class>
 		<class>org.olat.portfolio.model.InvitationImpl</class>
 		<class>org.olat.portfolio.model.structel.EPStructureElementToGroupRelation</class>
diff --git a/src/main/resources/database/mysql/alter_11_2_x_to_11_3_0.sql b/src/main/resources/database/mysql/alter_11_2_x_to_11_3_0.sql
index e69de29bb2d..c021aae0b4f 100644
--- a/src/main/resources/database/mysql/alter_11_2_x_to_11_3_0.sql
+++ b/src/main/resources/database/mysql/alter_11_2_x_to_11_3_0.sql
@@ -0,0 +1,16 @@
+create table o_vid_metadata (
+  id bigint not null auto_increment,
+  creationdate timestamp not null,
+  lastmodified timestamp not null,
+  vid_width bigint default null,
+  vid_height bigint default null,
+  vid_size bigint default null,
+  vid_format varchar(32) default null,
+  vid_length varchar(32) default null,
+  fk_resource_id bigint not null,
+  primary key (id)
+);
+
+alter table o_vid_metadata ENGINE = InnoDB;
+
+alter table o_vid_metadata add constraint vid_meta_rsrc_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index dc7212ceb78..1d9106e9683 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -1312,6 +1312,19 @@ create table o_vid_transcoding (
    primary key (id)
 );
 
+create table o_vid_metadata (
+  id bigint not null auto_increment,
+  creationdate timestamp not null,
+  lastmodified timestamp not null,
+  vid_width bigint default null,
+  vid_height bigint default null,
+  vid_size bigint default null,
+  vid_format varchar(32) default null,
+  vid_length varchar(32) default null,
+  fk_resource_id bigint not null,
+  primary key (id)
+);
+
 -- calendar
 create table o_cal_use_config (
    id bigint not null,
@@ -2208,6 +2221,7 @@ alter table o_goto_organizer ENGINE = InnoDB;
 alter table o_goto_meeting ENGINE = InnoDB;
 alter table o_goto_registrant ENGINE = InnoDB;
 alter table o_vid_transcoding ENGINE = InnoDB;
+alter table o_vid_metadata ENGINE = InnoDB;
 alter table o_pf_category_relation ENGINE = InnoDB;
 alter table o_pf_category ENGINE = InnoDB;
 alter table o_pf_media ENGINE = InnoDB;
@@ -2549,6 +2563,8 @@ alter table o_goto_registrant add constraint goto_regis_ident_idx foreign key (f
 alter table o_vid_transcoding add constraint fk_resource_id_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
 create index vid_status_trans_idx on o_vid_transcoding(vid_status);
 create index vid_transcoder_trans_idx on o_vid_transcoding(vid_transcoder);
+alter table o_vid_metadata add constraint vid_meta_rsrc_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
+
 
 -- calendar
 alter table o_cal_use_config add constraint cal_u_conf_to_ident_idx foreign key (fk_identity) references o_bs_identity (id);
diff --git a/src/main/resources/database/oracle/alter_11_2_x_to_11_3_0.sql b/src/main/resources/database/oracle/alter_11_2_x_to_11_3_0.sql
index e69de29bb2d..9bbd58a54bd 100644
--- a/src/main/resources/database/oracle/alter_11_2_x_to_11_3_0.sql
+++ b/src/main/resources/database/oracle/alter_11_2_x_to_11_3_0.sql
@@ -0,0 +1,15 @@
+create table o_vid_metadata (
+  id number(20) GENERATED ALWAYS AS IDENTITY,
+  creationdate date not null,
+  lastmodified date not null,
+  vid_width number(20) default null,
+  vid_height number(20) default null,
+  vid_size number(20) default null,
+  vid_format varchar2(32 char) default null,
+  vid_length varchar2(32 char) default null,
+  fk_resource_id number(20) not null,
+  primary key (id)
+);
+
+alter table o_vid_metadata add constraint vid_meta_rsrc_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
+create index idx_vid_meta_rsrc_idx on o_vid_metadata(fk_resource_id);
\ No newline at end of file
diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql
index e725348ec6a..9aaa09011fb 100644
--- a/src/main/resources/database/oracle/setupDatabase.sql
+++ b/src/main/resources/database/oracle/setupDatabase.sql
@@ -1341,6 +1341,19 @@ create table o_vid_transcoding (
    primary key (id)
 );
 
+create table o_vid_metadata (
+  id number(20) GENERATED ALWAYS AS IDENTITY,
+  creationdate date not null,
+  lastmodified date not null,
+  vid_width number(20) default null,
+  vid_height number(20) default null,
+  vid_size number(20) default null,
+  vid_format varchar2(32 char) default null,
+  vid_length varchar2(32 char) default null,
+  fk_resource_id number(20) not null,
+  primary key (id)
+);
+
 -- calendar
 create table o_cal_use_config (
    id number(20) not null,
@@ -2668,6 +2681,8 @@ alter table o_vid_transcoding add constraint fk_resource_id_idx foreign key (fk_
 create index idx_vid_trans_resource_idx on o_vid_transcoding(fk_resource_id);
 create index vid_status_trans_idx on o_vid_transcoding(vid_status);
 create index vid_transcoder_trans_idx on o_vid_transcoding(vid_transcoder);
+alter table o_vid_metadata add constraint vid_meta_rsrc_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
+create index idx_vid_meta_rsrc_idx on o_vid_metadata(fk_resource_id);
 
 -- calendar
 alter table o_cal_use_config add constraint cal_u_conf_to_ident_idx foreign key (fk_identity) references o_bs_identity (id);
diff --git a/src/main/resources/database/postgresql/alter_11_2_x_to_11_3_0.sql b/src/main/resources/database/postgresql/alter_11_2_x_to_11_3_0.sql
index e69de29bb2d..8d495aff18d 100644
--- a/src/main/resources/database/postgresql/alter_11_2_x_to_11_3_0.sql
+++ b/src/main/resources/database/postgresql/alter_11_2_x_to_11_3_0.sql
@@ -0,0 +1,15 @@
+create table o_vid_metadata (
+  id bigserial not null,
+  creationdate timestamp not null,
+  lastmodified timestamp not null,
+  vid_width bigint default null,
+  vid_height bigint default null,
+  vid_size bigint default null,
+  vid_format varchar(32) default null,
+  vid_length varchar(32) default null,
+  fk_resource_id bigint not null,
+  primary key (id)
+);
+
+alter table o_vid_metadata add constraint vid_meta_rsrc_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
+create index idx_vid_meta_rsrc_idx on o_vid_metadata(fk_resource_id);
\ No newline at end of file
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index be6b731f600..3488c69638a 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -1311,6 +1311,19 @@ create table o_vid_transcoding (
    primary key (id)
 );
 
+create table o_vid_metadata (
+  id bigserial not null,
+  creationdate timestamp not null,
+  lastmodified timestamp not null,
+  vid_width bigint default null,
+  vid_height bigint default null,
+  vid_size bigint default null,
+  vid_format varchar(32) default null,
+  vid_length varchar(32) default null,
+  fk_resource_id bigint not null,
+  primary key (id)
+);
+
 -- calendar
 create table o_cal_use_config (
    id int8 not null,
@@ -2546,6 +2559,8 @@ alter table o_vid_transcoding add constraint fk_resource_id_idx foreign key (fk_
 create index idx_vid_trans_resource_idx on o_vid_transcoding(fk_resource_id);
 create index vid_status_trans_idx on o_vid_transcoding(vid_status);
 create index vid_transcoder_trans_idx on o_vid_transcoding(vid_transcoder);
+alter table o_vid_metadata add constraint vid_meta_rsrc_idx foreign key (fk_resource_id) references o_olatresource (resource_id);
+create index idx_vid_meta_rsrc_idx on o_vid_metadata(fk_resource_id);
 
 -- mapper
 create index o_mapper_uuid_idx on o_mapper (mapper_uuid);
diff --git a/src/test/java/org/olat/modules/video/manager/VideoMetadataDAOTest.java b/src/test/java/org/olat/modules/video/manager/VideoMetadataDAOTest.java
new file mode 100644
index 00000000000..f43aaa38176
--- /dev/null
+++ b/src/test/java/org/olat/modules/video/manager/VideoMetadataDAOTest.java
@@ -0,0 +1,102 @@
+/**
+ * <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.manager;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.core.commons.persistence.DB;
+import org.olat.modules.video.VideoMeta;
+import org.olat.modules.video.model.VideoMetaImpl;
+import org.olat.repository.RepositoryEntry;
+import org.olat.test.JunitTestHelper;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: January 2017<br>
+ * @author fkiefer, fabian.kiefer@frentix.com, http://www.frentix.com
+ *
+ */
+public class VideoMetadataDAOTest extends OlatTestCase {
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private VideoMetadataDAO videoMetadataDao;
+	
+		
+	@Test 
+	public void createVideoMetadata () {
+		RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry entry1 = JunitTestHelper.createAndPersistRepositoryEntry();
+		//create metadata entries
+		VideoMeta meta = videoMetadataDao.createVideoMetadata(entry, 1500, "vid.mp4");
+		Assert.assertNotNull(meta);
+		VideoMeta meta1 = videoMetadataDao.createVideoMetadata(entry1, 5500, "vid.mov");
+		Assert.assertNotNull(meta1);
+		dbInstance.commitAndCloseSession();
+		//retrieve by olatresource
+		VideoMeta meta2 = videoMetadataDao.getVideoMetadata(entry.getOlatResource());
+		Assert.assertNotNull(meta2);
+		Assert.assertTrue(meta2.getSize() == 1500);
+		//update value
+		meta2.setSize(2500);
+		Assert.assertTrue(meta2.getSize() == 2500);
+		dbInstance.commitAndCloseSession();
+	}
+	
+	@Test
+	public void deleteVideoMetadata () {
+		RepositoryEntry entry0 = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry entry1 = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry entry2 = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry entry3 = JunitTestHelper.createAndPersistRepositoryEntry();
+		//create metadata entries
+		VideoMeta meta0 = videoMetadataDao.createVideoMetadata(entry0, 1500, "vid.mp4");
+		VideoMeta meta1 = videoMetadataDao.createVideoMetadata(entry1, 1100, "vide.mp4");
+		VideoMeta meta2 = videoMetadataDao.createVideoMetadata(entry2, 1200, "video.mov");
+		VideoMeta meta3 = videoMetadataDao.createVideoMetadata(entry3, 4500, "videos.mp4");
+		Assert.assertNotNull(meta1);
+		Assert.assertNotNull(meta3);
+		Assert.assertNotNull(meta2);
+		Assert.assertNotNull(meta0);
+		dbInstance.commitAndCloseSession();
+		//retrieve list of entries
+		List<VideoMetaImpl> metadata = videoMetadataDao.getAllVideoResourcesMetadata();
+		Assert.assertEquals(4, metadata.size());
+		dbInstance.commitAndCloseSession();
+		//delete entries
+		int deleted0 = videoMetadataDao.deleteVideoMetadata(entry0.getOlatResource());
+		int deleted1 = videoMetadataDao.deleteVideoMetadata(entry3.getOlatResource());
+		Assert.assertEquals(1, deleted0);
+		Assert.assertNotEquals(0, deleted1);
+		dbInstance.commitAndCloseSession();
+		//retrieve new list
+		List<VideoMetaImpl> metadata1 = videoMetadataDao.getAllVideoResourcesMetadata();
+		Assert.assertEquals(2, metadata1.size());
+		Assert.assertEquals("mov", metadata1.get(1).getFormat());
+		Assert.assertEquals(1100, metadata1.get(0).getSize());
+		dbInstance.commitAndCloseSession();
+	}
+	
+}
diff --git a/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java b/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java
index 0cb92ab0366..1f67d5d8750 100644
--- a/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java
+++ b/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java
@@ -90,11 +90,11 @@ public class VideoTranscodingDAOTest extends OlatTestCase {
 			videoTranscodingDao.deleteVideoTranscoding(vTranscoding1);
 			dbInstance.commitAndCloseSession();
 			List<VideoTranscoding> results = videoTranscodingDao.getVideoTranscodings(resource);
-			Assert.assertTrue(results.size() == 3);
-			// delte all transcodings of resource
+			Assert.assertEquals(3,results.size());
+			// delete all transcodings of resource
 			videoTranscodingDao.deleteVideoTranscodings(resource);
 			dbInstance.commitAndCloseSession();
 			results = videoTranscodingDao.getVideoTranscodings(resource);
-			Assert.assertTrue(results.size() == 0);
+			Assert.assertEquals(0,results.size());
 	}
 }
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index 4ee2bf8ccd3..26bd9fd12a1 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -170,6 +170,7 @@ import org.junit.runners.Suite;
 	org.olat.modules.reminder.manager.ReminderDAOTest.class,
 	org.olat.modules.reminder.manager.ReminderRuleEngineTest.class,
 	org.olat.modules.video.manager.VideoTranscodingDAOTest.class,
+	org.olat.modules.video.manager.VideoMetadataDAOTest.class,
 	org.olat.properties.PropertyTest.class,
 	org.olat.search.service.document.file.FileDocumentFactoryTest.class,
 	org.olat.search.service.indexer.repository.course.SPCourseNodeIndexerTest.class,
-- 
GitLab