From 3e8da51872a61fdcc40358d1adcf09acca3d5e24 Mon Sep 17 00:00:00 2001
From: tobias <thinkdifferent055@gmail.com>
Date: Tue, 17 Nov 2020 20:25:05 +0100
Subject: [PATCH] Add basic player pad content type

---
 .idea/encodings.xml                           |   2 +
 .idea/modules.xml                             |   1 +
 .../pad/content/AudioPadContentFactory.java   |  37 +---
 .../playpad/pad/preview/PadTextPreview.java   |  40 ++++
 .../PlayWallPluginContentPlayer/pom.xml       | 131 +++++++++++++
 .../src/main/resources/PadContent.xml         |   3 +
 .../src/main/resources/lang/base.properties   |   0
 .../main/resources/lang/base_de.properties    |   1 +
 .../src/main/resources/plugin.yml             |   6 +
 .../plugin/content/ContentPluginMain.scala    |  26 +++
 .../content/player/PlayerPadContent.scala     | 180 ++++++++++++++++++
 .../player/PlayerPadContentFactory.scala      |  20 ++
 .../media/video/VideoPadContentFactory.java   |  36 +---
 PlayWallPlugins/pom.xml                       |   1 +
 14 files changed, 415 insertions(+), 69 deletions(-)
 create mode 100644 PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base.properties
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContent.scala
 create mode 100644 PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContentFactory.scala

diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 573656f6..c1500a33 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -16,6 +16,7 @@
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/src/main/java" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources" charset="UTF-8" />
@@ -32,6 +33,7 @@
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/java" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/resources" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginWebAPI" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources" charset="UTF-8" />
     <file url="PROJECT" charset="UTF-8" />
   </component>
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 71a1574f..ae20783c 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -7,6 +7,7 @@
       <module fileurl="file://$PROJECT_DIR$/PlayWallCore/PlayWallCore.iml" filepath="$PROJECT_DIR$/PlayWallCore/PlayWallCore.iml" />
       <module fileurl="file://$PROJECT_DIR$/PlayWallDesktop.iml" filepath="$PROJECT_DIR$/PlayWallDesktop.iml" />
       <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/PlayWallPluginAwake.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/PlayWallPluginAwake.iml" />
+      <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/PlayWallPluginContentPlayer.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/PlayWallPluginContentPlayer.iml" />
       <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/PlayWallPluginEqualizer.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/PlayWallPluginEqualizer.iml" />
       <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad/PlayWallPluginLaunchpad.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad/PlayWallPluginLaunchpad.iml" />
       <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia/PlayWallPluginMedia.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia/PlayWallPluginMedia.iml" />
diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java b/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java
index d60a59d9..8e5f4339 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java
@@ -1,16 +1,11 @@
 package de.tobias.playpad.pad.content;
 
 import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.preview.PadTextPreview;
 import de.tobias.playpad.pad.view.IPadContentView;
 import de.tobias.playpad.viewcontroller.option.ProfileSettingsTabViewController;
 import de.tobias.playpad.viewcontroller.option.profile.AudioTabViewController;
-import javafx.geometry.Pos;
-import javafx.scene.Node;
-import javafx.scene.control.Label;
 import javafx.scene.layout.Pane;
-import javafx.scene.layout.Priority;
-import javafx.scene.layout.VBox;
-import javafx.scene.text.TextAlignment;
 
 public class AudioPadContentFactory extends PadContentFactory {
 
@@ -33,7 +28,7 @@ public class AudioPadContentFactory extends PadContentFactory {
 	@Override
 	public IPadContentView getPadContentPreview(Pad pad, Pane parentNode) {
 		if (pad.getContent() != null) {
-			return new AudioContentView(pad, parentNode);
+			return new PadTextPreview(pad, parentNode);
 		} else {
 			return null;
 		}
@@ -43,32 +38,4 @@ public class AudioPadContentFactory extends PadContentFactory {
 	public ProfileSettingsTabViewController getSettingsTabViewController(boolean activePlayer) {
 		return new AudioTabViewController(activePlayer);
 	}
-
-	private static class AudioContentView implements IPadContentView {
-
-		private Label nameLabel;
-
-		AudioContentView(Pad pad, Pane parentNode) {
-			nameLabel = new Label();
-			nameLabel.textProperty().bind(pad.nameProperty());
-
-			nameLabel.setWrapText(true);
-			nameLabel.setAlignment(Pos.CENTER);
-			nameLabel.setTextAlignment(TextAlignment.CENTER);
-
-			nameLabel.prefWidthProperty().bind(parentNode.widthProperty());
-			nameLabel.setMaxHeight(Double.MAX_VALUE);
-			VBox.setVgrow(nameLabel, Priority.ALWAYS);
-		}
-
-		@Override
-		public Node getNode() {
-			return nameLabel;
-		}
-
-		@Override
-		public void deInit() {
-			nameLabel.textProperty().unbind();
-		}
-	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java
new file mode 100644
index 00000000..c197d2fe
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java
@@ -0,0 +1,40 @@
+package de.tobias.playpad.pad.preview;
+
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.view.IPadContentView;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.TextAlignment;
+
+public class PadTextPreview implements IPadContentView {
+
+	private final Label nameLabel;
+
+	public PadTextPreview(Pad pad, Pane parentNode) {
+		this.nameLabel = new Label();
+		this.nameLabel.textProperty().bind(pad.nameProperty());
+
+		this.nameLabel.setWrapText(true);
+		this.nameLabel.setAlignment(Pos.CENTER);
+		this.nameLabel.setTextAlignment(TextAlignment.CENTER);
+
+		this.nameLabel.prefWidthProperty().bind(parentNode.widthProperty());
+		this.nameLabel.setMaxHeight(Double.MAX_VALUE);
+
+		VBox.setVgrow(nameLabel, Priority.ALWAYS);
+	}
+
+	@Override
+	public Node getNode() {
+		return nameLabel;
+	}
+
+	@Override
+	public void deInit() {
+		nameLabel.textProperty().unbind();
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml
new file mode 100644
index 00000000..f888ba1c
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>PlayWallPlugins</artifactId>
+        <groupId>de.tobias.playpad</groupId>
+        <version>7.0.2</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>PlayWallPluginContentPlayer</artifactId>
+
+    <properties>
+        <project.outputDirectory>../../build/${project.version}</project.outputDirectory>
+        <project.artifactName>${project.artifactId}-v${project.version}</project.artifactName>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>de.tobias.playpad</groupId>
+            <artifactId>PlayWallCore</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>de.thecodelabs</groupId>
+                <artifactId>versionizer-maven-plugin</artifactId>
+                <version>${versionizer-maven-plugin.version}</version>
+                <configuration>
+                    <resourceFile>plugin.yml</resourceFile>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>resource-fill</id>
+                        <goals>
+                            <goal>resource-fill</goal>
+                        </goals>
+                        <phase>compile</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!--Scala Plugin-->
+            <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>scala-compile-first</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                            <goal>compile</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>scala-test-compile</id>
+                        <phase>process-test-resources</phase>
+                        <goals>
+                            <goal>testCompile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>3.1.1</version>
+                <configuration>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                    <outputDirectory>${project.outputDirectory}</outputDirectory>
+                    <finalName>${project.artifactName}</finalName>
+                    <appendAssemblyId>false</appendAssemblyId>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>assemble-all</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <distributionManagement>
+        <repository>
+            <id>release</id>
+            <name>TheCodeLabs-releases</name>
+            <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-release</url>
+        </repository>
+        <snapshotRepository>
+            <id>snapshots</id>
+            <name>TheCodeLabs-snapshots</name>
+            <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-snapshots</url>
+        </snapshotRepository>
+    </distributionManagement>
+
+    <repositories>
+        <repository>
+            <id>release</id>
+            <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-release</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>snapshots</id>
+            <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-snapshots</url>
+            <releases>
+                <enabled>false</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+</project>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml
new file mode 100644
index 00000000..c0931fc3
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml
@@ -0,0 +1,3 @@
+<Actions>
+    <Component id="video" name="plugin.content.Player.name">de.tobias.playpad.plugin.content.player.PlayerPadContentFactory</Component>
+</Actions>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base.properties b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base.properties
new file mode 100644
index 00000000..e69de29b
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties
new file mode 100644
index 00000000..7cd66e7a
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties
@@ -0,0 +1 @@
+plugin.content.Player.name=Content Player
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml
new file mode 100644
index 00000000..70a18e12
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml
@@ -0,0 +1,6 @@
+main: "de.tobias.playpad.plugin.content.ContentPluginMain"
+name: "ContentPlugin"
+artifactId: "${pom.artifactId}"
+groupId: "${pom.groupId}"
+version: "${pom.version}"
+build: 1
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala
new file mode 100644
index 00000000..7fc988e5
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala
@@ -0,0 +1,26 @@
+package de.tobias.playpad.plugin.content
+
+import de.thecodelabs.plugins.PluginDescriptor
+import de.thecodelabs.utils.util.Localization
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.plugin.{Module, PlayPadPluginStub}
+
+class ContentPluginMain extends PlayPadPluginStub {
+
+	private var module: Module = _
+
+	override def startup(descriptor: PluginDescriptor): Unit = {
+		module = new Module(descriptor.getName, descriptor.getArtifactId)
+
+		val localization = Localization.loadBundle("lang/base", getClass.getClassLoader)
+		Localization.addResourceBundle(localization)
+
+		PlayPadPlugin.getRegistries.getPadContents.loadComponentsFromFile("PadContent.xml", getClass.getClassLoader, module, localization)
+	}
+
+	override def shutdown(): Unit = {
+
+	}
+
+	override def getModule: Module = module
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContent.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContent.scala
new file mode 100644
index 00000000..cc7786c2
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContent.scala
@@ -0,0 +1,180 @@
+package de.tobias.playpad.plugin.content.player
+
+import java.nio.file.Files
+
+import de.tobias.playpad.pad.content.PadContent
+import de.tobias.playpad.pad.content.play.Durationable
+import de.tobias.playpad.pad.mediapath.MediaPath
+import de.tobias.playpad.pad.{Pad, PadStatus}
+import de.tobias.playpad.volume.VolumeManager
+import javafx.application.Platform
+import javafx.beans.property.{ReadOnlyObjectProperty, SimpleObjectProperty}
+import javafx.scene.media.{Media, MediaPlayer}
+import javafx.util.Duration
+
+import scala.collection.mutable.ListBuffer
+
+class PlayerPadContent(val pad: Pad, val `type`: String) extends PadContent(pad) with Durationable {
+
+	private class MediaPlayerContainer(val path: MediaPath, val mediaPlayer: MediaPlayer) {
+		def play(): Unit = {
+			_durationProperty.bind(mediaPlayer.totalDurationProperty())
+			_positionProperty.bind(mediaPlayer.currentTimeProperty())
+
+			mediaPlayer.seek(Duration.ZERO)
+			mediaPlayer.play()
+			currentRunningIndex = mediaPlayers.indexOf(this)
+		}
+
+		def next(): Unit = {
+			stop()
+
+			currentRunningIndex = mediaPlayers.indexOf(this)
+			if (currentRunningIndex + 1 < mediaPlayers.length) {
+				mediaPlayers(currentRunningIndex + 1).play()
+			} else if (getPad.getPadSettings.isLoop) {
+				mediaPlayers.head.play()
+			}
+		}
+
+		def stop(): Unit = {
+			mediaPlayer.stop()
+			_durationProperty.unbind()
+			_positionProperty.unbind()
+		}
+	}
+
+	private val mediaPlayers: ListBuffer[MediaPlayerContainer] = ListBuffer.empty
+	private var currentRunningIndex: Int = 0
+
+	private val _durationProperty = new SimpleObjectProperty[Duration]
+	private val _positionProperty = new SimpleObjectProperty[Duration]
+
+	override def getType: String = `type`
+
+	override def play(): Unit = {
+		getPad.setEof(false)
+		mediaPlayers.head.play()
+	}
+
+	override def stop(): Boolean = {
+		if (getPad.isEof) {
+			mediaPlayers(currentRunningIndex).next()
+			return false
+		}
+		mediaPlayers(currentRunningIndex).stop()
+		true
+	}
+
+	/*
+	Durationable
+	 */
+
+	override def getDuration: Duration = _durationProperty.get()
+
+	override def getPosition: Duration = _positionProperty.get()
+
+	override def durationProperty(): ReadOnlyObjectProperty[Duration] = _durationProperty
+
+	override def positionProperty(): ReadOnlyObjectProperty[Duration] = _positionProperty
+
+	override def isPadLoaded: Boolean = {
+		mediaPlayers.nonEmpty
+	}
+
+	/**
+	 * Load media files.
+	 */
+	override def loadMedia(): Unit = {
+	}
+
+	/**
+	 * Load media file.
+	 *
+	 * @param mediaPath specify media path
+	 */
+	override def loadMedia(mediaPath: MediaPath): Unit = {
+		val path = mediaPath.getPath
+		if (Files.notExists(path)) {
+			Platform.runLater(() => getPad.setStatus(PadStatus.NOT_FOUND))
+			return
+		}
+
+		val media = new Media(path.toUri.toString)
+		val mediaPlayer = new MediaPlayer(media)
+
+		mediaPlayer.setOnReady(() => {
+			getPad.setStatus(PadStatus.READY)
+
+			Platform.runLater(() => {
+				if (getPad.isPadVisible) {
+					getPad.getController.getView.showBusyView(false)
+				}
+			})
+		})
+
+		mediaPlayer.setOnError(() => Platform.runLater(() => {
+			if (getPad.isPadVisible) {
+				getPad.getController.getView.showBusyView(false)
+			}
+		}))
+
+		mediaPlayer.setOnEndOfMedia(() => {
+			if (!getPad.getPadSettings.isLoop) {
+				getPad.setEof(true)
+				getPad.setStatus(PadStatus.STOP)
+			}
+			else { // Loop
+				mediaPlayer.seek(Duration.ZERO)
+			}
+		})
+
+		mediaPlayers.addOne(new MediaPlayerContainer(mediaPath, mediaPlayer))
+	}
+
+	/**
+	 * Unload media files.
+	 */
+	override def unloadMedia(): Unit = {
+		if ((getPad.getStatus eq PadStatus.PLAY) || (getPad.getStatus eq PadStatus.PAUSE)) getPad.setStatus(PadStatus.STOP)
+
+		mediaPlayers.clear()
+
+		Platform.runLater(() => {
+			if (getPad != null) {
+				getPad.setStatus(PadStatus.EMPTY)
+			}
+		})
+	}
+
+	/**
+	 * Unload media file.
+	 *
+	 * @param mediaPath specify media path
+	 */
+	override def unloadMedia(mediaPath: MediaPath): Unit = {
+		val index = mediaPlayers.indexWhere(item => item.path.getId == mediaPath.getId)
+
+		val playerContainer = mediaPlayers(index)
+		playerContainer.stop()
+
+		mediaPlayers.remove(index)
+	}
+
+	override def updateVolume(): Unit = {
+		val volume = VolumeManager.getInstance.computeVolume(getPad)
+		mediaPlayers.foreach(player => player.mediaPlayer.setVolume(volume))
+	}
+
+	/**
+	 * Create a copy of the PadContent instance
+	 *
+	 * @param pad target pad
+	 * @return copied content
+	 */
+	override def copy(pad: Pad): PadContent = {
+		val clone = new PlayerPadContent(pad, getType)
+		clone.loadMedia()
+		clone
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContentFactory.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContentFactory.scala
new file mode 100644
index 00000000..4c8e8ab9
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/PlayerPadContentFactory.scala
@@ -0,0 +1,20 @@
+package de.tobias.playpad.plugin.content.player
+
+import de.tobias.playpad.pad.Pad
+import de.tobias.playpad.pad.content.{PadContent, PadContentFactory}
+import de.tobias.playpad.pad.preview.PadTextPreview
+import de.tobias.playpad.pad.view.IPadContentView
+import javafx.scene.layout.Pane
+
+class PlayerPadContentFactory(val `type`: String) extends PadContentFactory(`type`) {
+
+	override def newInstance(pad: Pad): PadContent = new PlayerPadContent(pad, getType)
+
+	override def getPadContentPreview(pad: Pad, parentNode: Pane): IPadContentView = new PadTextPreview(pad, parentNode)
+
+	override def getSupportedTypes: Array[String] = PlayerPadContentFactory.FILE_EXTENSION
+}
+
+object PlayerPadContentFactory {
+	private val FILE_EXTENSION = Array("*.mp4", "*.mov")
+}
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java
index 96ccd6f3..14e2269f 100644
--- a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java
+++ b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java
@@ -3,18 +3,13 @@ package de.tobias.playpad.plugin.media.video;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.content.PadContent;
 import de.tobias.playpad.pad.content.PadContentFactory;
+import de.tobias.playpad.pad.preview.PadTextPreview;
 import de.tobias.playpad.pad.view.IPadContentView;
 import de.tobias.playpad.plugin.media.main.impl.MediaPluginImpl;
 import de.tobias.playpad.plugin.media.main.impl.MediaSettingsTabViewController;
 import de.tobias.playpad.viewcontroller.PadSettingsTabViewController;
 import de.tobias.playpad.viewcontroller.option.ProfileSettingsTabViewController;
-import javafx.geometry.Pos;
-import javafx.scene.Node;
-import javafx.scene.control.Label;
 import javafx.scene.layout.Pane;
-import javafx.scene.layout.Priority;
-import javafx.scene.layout.VBox;
-import javafx.scene.text.TextAlignment;
 
 public class VideoPadContentFactory extends PadContentFactory {
 
@@ -31,7 +26,7 @@ public class VideoPadContentFactory extends PadContentFactory {
 
 	@Override
 	public IPadContentView getPadContentPreview(Pad pad, Pane parentNode) {
-		return new VideoContentView(pad, parentNode);
+		return new PadTextPreview(pad, parentNode);
 	}
 
 	@Override
@@ -50,31 +45,4 @@ public class VideoPadContentFactory extends PadContentFactory {
 		return FILE_EXTENSION;
 	}
 
-	private static class VideoContentView implements IPadContentView {
-
-		private final Label nameLabel;
-
-		VideoContentView(Pad pad, Pane parentNode) {
-			nameLabel = new Label();
-			nameLabel.textProperty().bind(pad.nameProperty());
-
-			nameLabel.setWrapText(true);
-			nameLabel.setAlignment(Pos.CENTER);
-			nameLabel.setTextAlignment(TextAlignment.CENTER);
-
-			nameLabel.prefWidthProperty().bind(parentNode.widthProperty());
-			nameLabel.setMaxHeight(Double.MAX_VALUE);
-			VBox.setVgrow(nameLabel, Priority.ALWAYS);
-		}
-
-		@Override
-		public Node getNode() {
-			return nameLabel;
-		}
-
-		@Override
-		public void deInit() {
-			nameLabel.textProperty().unbind();
-		}
-	}
 }
diff --git a/PlayWallPlugins/pom.xml b/PlayWallPlugins/pom.xml
index e633d8b6..eda2b0ff 100644
--- a/PlayWallPlugins/pom.xml
+++ b/PlayWallPlugins/pom.xml
@@ -20,6 +20,7 @@
         <module>PlayWallPluginNativeAudio</module>
         <module>PlayWallPluginPlayoutLog</module>
         <module>PlayWallPluginWebAPI</module>
+        <module>PlayWallPluginContentPlayer</module>
     </modules>
 
 </project>
\ No newline at end of file
-- 
GitLab