From 238debe28cef2bb8acab1a4e61785420d2f37f7e Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Wed, 26 Feb 2020 14:24:17 +0100
Subject: [PATCH] OO-4541: Show info if stream is not broadcasted yet.

---
 .../nodes/livestream/LiveStreamService.java   | 16 ++++++
 .../manager/LiveStreamServiceImpl.java        | 57 +++++++++++++++++++
 .../livestream/paella/PaellaFactory.java      | 14 +----
 .../ui/LiveStreamVideoController.java         | 43 ++++++++++----
 .../nodes/livestream/ui/_content/video.html   | 18 ++++--
 .../ui/_i18n/LocalStrings_de.properties       |  2 +
 .../ui/_i18n/LocalStrings_en.properties       |  2 +
 .../ui/_i18n/LocalStrings_fr.properties       |  2 +
 .../ui/_i18n/LocalStrings_pt_BR.properties    |  2 +
 9 files changed, 127 insertions(+), 29 deletions(-)

diff --git a/src/main/java/org/olat/course/nodes/livestream/LiveStreamService.java b/src/main/java/org/olat/course/nodes/livestream/LiveStreamService.java
index e9d16eea850..31209f23055 100644
--- a/src/main/java/org/olat/course/nodes/livestream/LiveStreamService.java
+++ b/src/main/java/org/olat/course/nodes/livestream/LiveStreamService.java
@@ -65,4 +65,20 @@ public interface LiveStreamService {
 	 */
 	Long getLaunchers(RepositoryEntryRef courseEntry, String subIdent, Date from, Date to);
 
+	/**
+	 * Split the url to separate urls
+	 *
+	 * @param url
+	 * @return
+	 */
+	String[] splitUrl(String url);
+
+	/**
+	 * Checks whether at the urls are currently streaming and returns the streaming urls
+	 *
+	 * @param urls
+	 * @return
+	 */
+	String[] getStreamingUrls(String[] urls);
+
 }
diff --git a/src/main/java/org/olat/course/nodes/livestream/manager/LiveStreamServiceImpl.java b/src/main/java/org/olat/course/nodes/livestream/manager/LiveStreamServiceImpl.java
index c4826a717d7..19bc4eaf66c 100644
--- a/src/main/java/org/olat/course/nodes/livestream/manager/LiveStreamServiceImpl.java
+++ b/src/main/java/org/olat/course/nodes/livestream/manager/LiveStreamServiceImpl.java
@@ -30,13 +30,23 @@ import java.util.concurrent.ThreadFactory;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import org.apache.http.Header;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.logging.log4j.Logger;
 import org.olat.commons.calendar.CalendarManager;
 import org.olat.commons.calendar.CalendarUtils;
 import org.olat.commons.calendar.model.KalendarEvent;
 import org.olat.commons.calendar.ui.components.KalendarRenderWrapper;
 import org.olat.core.id.Identity;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
 import org.olat.course.nodes.cal.CourseCalendars;
 import org.olat.course.nodes.livestream.LiveStreamEvent;
+import org.olat.course.nodes.livestream.LiveStreamModule;
 import org.olat.course.nodes.livestream.LiveStreamService;
 import org.olat.course.nodes.livestream.model.LiveStreamEventImpl;
 import org.olat.repository.RepositoryEntry;
@@ -54,8 +64,12 @@ import org.springframework.stereotype.Service;
 @Service
 public class LiveStreamServiceImpl implements LiveStreamService {
 	
+	private static final Logger log = Tracing.createLoggerFor(LiveStreamServiceImpl.class);
+	
 	private ScheduledExecutorService scheduler;
 	
+	@Autowired
+	private LiveStreamModule liveStreamModule;
 	@Autowired
 	private CalendarManager calendarManager;
 	@Autowired
@@ -177,4 +191,47 @@ public class LiveStreamServiceImpl implements LiveStreamService {
 	public Long getLaunchers(RepositoryEntryRef courseEntry, String subIdent, Date from, Date to) {
 		return launchDao.getLaunchers(courseEntry, subIdent, from, to);
 	}
+
+	@Override
+	public String[] splitUrl(String url) {
+		if (!StringHelper.containsNonWhitespace(url)) return new String[0];
+		
+		String urlSeparator = liveStreamModule.getUrlSeparator();
+		return url.split(urlSeparator);
+	}
+
+	@Override
+	public String[] getStreamingUrls(String[] urls) {
+		if (urls == null || urls.length < 1) return urls;
+		
+		List<String> streamingUrls = new ArrayList<>(urls.length);
+		for (String url : urls) {
+			if (isStreaming(url)) {
+				streamingUrls.add(url);
+			}
+		}
+		
+		String[] streaming = new String[streamingUrls.size()];
+		streamingUrls.toArray(streaming);
+		return streaming;
+	}
+
+	private boolean isStreaming(String url) {
+		HttpGet request = new HttpGet(url);
+		try(CloseableHttpClient httpclient = HttpClients.createDefault();
+			CloseableHttpResponse response = httpclient.execute(request);) {
+			int statusCode = response.getStatusLine().getStatusCode();
+			if (statusCode == HttpStatus.SC_OK) {
+				// Wrong WOWZA urls returns a html message instead of a video.
+				Header contentType = response.getFirstHeader("Content-Type");
+				if (contentType != null && !contentType.getValue().contains("html")) {
+					return true;
+				}
+			}
+		} catch(Exception e) {
+			log.error("", e);
+		}
+		
+		return false;
+	}
 }
diff --git a/src/main/java/org/olat/course/nodes/livestream/paella/PaellaFactory.java b/src/main/java/org/olat/course/nodes/livestream/paella/PaellaFactory.java
index af8c7f3b90a..0465ea425ca 100644
--- a/src/main/java/org/olat/course/nodes/livestream/paella/PaellaFactory.java
+++ b/src/main/java/org/olat/course/nodes/livestream/paella/PaellaFactory.java
@@ -22,10 +22,6 @@ package org.olat.course.nodes.livestream.paella;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.olat.core.CoreSpringFactory;
-import org.olat.core.util.StringHelper;
-import org.olat.course.nodes.livestream.LiveStreamModule;
-
 /**
  * 
  * Initial date: 13 Dec 2019<br>
@@ -34,19 +30,13 @@ import org.olat.course.nodes.livestream.LiveStreamModule;
  */
 public class PaellaFactory {
 	
-	public static Streams createStreams(String url) {
+	public static Streams createStreams(String[] urls) {
 		Streams streams = new Streams();
-		if (StringHelper.containsNonWhitespace(url)) {
-			String[] urls = splitUrls(url);
+		if (urls.length > 0) {
 			addStreams(streams, urls);
 		}
 		return streams;
 	}
-
-	private static String[] splitUrls(String url) {
-		String urlSeparator = CoreSpringFactory.getImpl(LiveStreamModule.class).getUrlSeparator();
-		return url.split(urlSeparator);
-	}
 	
 	private static void addStreams(Streams streams, String[] urls) {
 		List<Stream> streamList = new ArrayList<>(2);
diff --git a/src/main/java/org/olat/course/nodes/livestream/ui/LiveStreamVideoController.java b/src/main/java/org/olat/course/nodes/livestream/ui/LiveStreamVideoController.java
index 04743bf6211..6efce834d2e 100644
--- a/src/main/java/org/olat/course/nodes/livestream/ui/LiveStreamVideoController.java
+++ b/src/main/java/org/olat/course/nodes/livestream/ui/LiveStreamVideoController.java
@@ -26,6 +26,8 @@ import org.olat.core.dispatcher.mapper.MapperService;
 import org.olat.core.dispatcher.mapper.manager.MapperKey;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
@@ -34,6 +36,7 @@ import org.olat.core.util.CodeHelper;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.UserSession;
 import org.olat.course.nodes.livestream.LiveStreamEvent;
+import org.olat.course.nodes.livestream.LiveStreamService;
 import org.olat.course.nodes.livestream.paella.PaellaFactory;
 import org.olat.course.nodes.livestream.paella.PaellaMapper;
 import org.olat.course.nodes.livestream.paella.PlayerProfile;
@@ -49,11 +52,16 @@ import org.springframework.beans.factory.annotation.Autowired;
 public class LiveStreamVideoController extends BasicController {
 
 	private final VelocityContainer mainVC;
+	private Link retryLink;
 	
 	private final PlayerProfile playerProfile;
 	private final List<MapperKey> mappers = new ArrayList<>();
+	private boolean error = false;
 	private String url;
+	private String[] urls;
 	
+	@Autowired
+	private LiveStreamService liveStreamService;
 	@Autowired
 	private MapperService mapperService;
 
@@ -61,35 +69,48 @@ public class LiveStreamVideoController extends BasicController {
 		super(ureq, wControl);
 		this.playerProfile = playerProfile;
 		mainVC = createVelocityContainer("video");
-		updateUI(ureq.getUserSession());
 		putInitialPanel(mainVC);
 	}
 	
 	public void setEvent(UserSession usess, LiveStreamEvent event) {
 		String newUrl = event != null? event.getLiveStreamUrl(): null;
 		if (newUrl == null || !newUrl.equalsIgnoreCase(url)) {
+			error = false;
 			url = newUrl;
+			urls = liveStreamService.splitUrl(newUrl);
 			updateUI(usess);
 		}
 	}
 
 	private void updateUI(UserSession usess) {
-		if (StringHelper.containsNonWhitespace(url)) {
-			mainVC.contextPut("id", CodeHelper.getRAMUniqueID());
-			Streams streams = PaellaFactory.createStreams(url);
-			PaellaMapper paellaMapper = new PaellaMapper(streams, playerProfile);
-			MapperKey mapperKey = mapperService.register(usess, paellaMapper);
-			mappers.add(mapperKey);
-			String baseURI = mapperKey.getUrl();
-			mainVC.contextPut("baseURI", baseURI);
-		}	else {
+		if (!StringHelper.containsNonWhitespace(url)) {
 			mainVC.contextRemove("id");
+			mainVC.contextRemove("error");
+		} else  {
+			String[] streamingUrls = liveStreamService.getStreamingUrls(urls);
+			if (streamingUrls.length > 0) {
+				mainVC.contextRemove("error");
+				mainVC.contextPut("id", CodeHelper.getRAMUniqueID());
+				Streams streams = PaellaFactory.createStreams(streamingUrls);
+				PaellaMapper paellaMapper = new PaellaMapper(streams, playerProfile);
+				MapperKey mapperKey = mapperService.register(usess, paellaMapper);
+				mappers.add(mapperKey);
+				String baseURI = mapperKey.getUrl();
+				mainVC.contextPut("baseURI", baseURI);
+			} else {
+				mainVC.contextRemove("id");
+				mainVC.contextPut("error", Boolean.valueOf(error));
+				retryLink = LinkFactory.createButton("viewer.retry", mainVC, this);
+			}
 		}
 	}
 
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
-		//
+		if (source == retryLink) {
+			error = false;
+			updateUI(ureq.getUserSession());
+		}
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/livestream/ui/_content/video.html b/src/main/java/org/olat/course/nodes/livestream/ui/_content/video.html
index 38148545150..930d261b813 100644
--- a/src/main/java/org/olat/course/nodes/livestream/ui/_content/video.html
+++ b/src/main/java/org/olat/course/nodes/livestream/ui/_content/video.html
@@ -1,8 +1,14 @@
-#if($r.isNotNull($id))
-<div class="o_paella_wrapper">
-	<div class="o_instruction o_paella_error">
-		$r.translate("viewer.error.browser")
+#if($r.isNotNull($error))
+	<div class="o_instruction">
+		$r.translate("viewer.error.stream")
+		<div class="o_button_group">
+			$r.render("viewer.retry")
+		</div>
+#elseif($r.isNotNull($id))
+	<div class="o_paella_wrapper">
+		<div class="o_instruction o_paella_error">
+			$r.translate("viewer.error.browser")
+		</div>
+		<iframe id="${id}" allowfullscreen="true" src="${baseURI}" frameborder="0"></iframe>
 	</div>
-	<iframe id="${id}" allowfullscreen="true" src="${baseURI}" frameborder="0"></iframe>
-</div>
 #end
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_de.properties
index bc2a803cfa8..0c3cdbd16fa 100644
--- a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_de.properties
@@ -42,4 +42,6 @@ table.header.location=$org.olat.commons.calendar\:cal.form.location
 table.header.subject=$org.olat.commons.calendar\:cal.form.subject
 table.header.viewers=Zuschauer
 viewer.error.browser=Der Livestream kann in diesem Browser nicht angezeigt werden. Bitte verwenden Sie einen anderen Browser.
+viewer.error.stream=Der Livestream kann nicht angezeigt werden. Vermutlich wird der Livestream noch nicht ausgestrahlt.
 viewer.no.stream=Aktuell wird kein Livestream ausgestrahlt.
+viewer.retry=Erneut starten
diff --git a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_en.properties
index a4e187294fb..b1ad594cc4f 100644
--- a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_en.properties
@@ -42,4 +42,6 @@ table.header.location=$org.olat.commons.calendar\:cal.form.location
 table.header.subject=$org.olat.commons.calendar\:cal.form.subject
 table.header.viewers=Viewers
 viewer.error.browser=The livestream cannot be displayed in this browser. Please use a different browser.
+viewer.error.stream=It is not possible to show the live stream. Probably is the live stream not broadcasted yet.
 viewer.no.stream=Currently no live stream is broadcasted.
+viewer.retry=Try again
diff --git a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_fr.properties
index 6176341e0d1..4965c241373 100644
--- a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_fr.properties
@@ -43,4 +43,6 @@ table.header.location=$org.olat.commons.calendar\:cal.form.location
 table.header.subject=$org.olat.commons.calendar\:cal.form.subject
 table.header.viewers=Spectateurs
 viewer.error.browser=Le flux ne peut \u00EAtre pas \u00EAtre affich\u00E9 dans ce navigateur. Choisissez s'il vous pla\u00EEt un autre navigateur.
+viewer.error.stream=Il n'est pas possible d'afficher la vid\u00E9o. La diffusion en direct n'a probablement pas encore commenc\u00E9.
 viewer.no.stream=Pas de diffusion pour l'instant.
+viewer.retry=Essayer \u00E0 nouveau
diff --git a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_pt_BR.properties
index c8d4bced76d..ca4dca335ec 100644
--- a/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_pt_BR.properties
+++ b/src/main/java/org/olat/course/nodes/livestream/ui/_i18n/LocalStrings_pt_BR.properties
@@ -30,4 +30,6 @@ table.header.description=$org.olat.commons.calendar\:cal.form.description
 table.header.end=$org.olat.commons.calendar\:cal.form.end
 table.header.location=$org.olat.commons.calendar\:cal.form.location
 table.header.subject=$org.olat.commons.calendar\:cal.form.subject
+viewer.error.stream=N\u00E3o \u00E9 poss\u00EDvel mostrar a transmiss\u00E3o ao vivo. Provavelmente a transmiss\u00E3o n\u00E3o come\u00E7ou ainda.
 viewer.no.stream=Atualmente, nenhuma transmiss\u00E3o ao vivo est\u00E1 sendo transmitida.
+viewer.retry=Tente novamente
-- 
GitLab