diff --git a/docs/README.md b/docs/README.md
index f1aa8a1..059c5ff 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -24,7 +24,8 @@ A wide list of parameters are available.
* `urn` (string): an unique identifier.
* `recommendedList` (object): a recommended result list with proterties:
* `recommendationId` (string): the recommendation identifer from the service.
- * `urns`(array): array of `urn`.
+ * `urns` (array): array of `urn`.
+ * `title` (string, optional): title of the playlist.
* `package` (string): Android package name or iOS bundle identifier.
* `version` (string): mobile application version.
@@ -43,16 +44,22 @@ A wide list of parameters are available.
* `/api/v1/whatisnew/text?package={package}&version={version}` : get WhatIsNewResult object.
* `/api/v1/whatisnew/html?package={package}&version={version}` : get What's new html format.
-#### Recommendation
+#### Recommendation for a media
* `/api/v2/playlist/recommendation/continuousPlayback/{urn}` : get media list object.
* `standalone` (optional, boolean): Recommendation for the playback mode. Default is `false`.
* Returns a `recommendedList` object.
-* `/api/v1/playlist/recommendation/continuousPlayback/{urn}` : get media list object.
+* *Deprecated* `/api/v1/playlist/recommendation/continuousPlayback/{urn}` : get media list object.
* `standalone` (optional, boolean): Recommendation for the playback mode. Default is `false`.
* `format` (optional, string): If set to `urn`, it returns an URN list. Default is `media` and redirects to an IL media list response.
+#### Personnal recommendation for a user
+
+* `/api/v2/playlist/recommendation/personalRecommendation` : get media list object.
+ * `user` (optional, string): `UserId` to use for a personal recommendation.
+ * Returns a `recommendedList` object.
+
## Private APIs
Private APIs need a user authentification.
diff --git a/pom.xml b/pom.xml
index c5cdd1d..eaf286f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,58 +1,58 @@
- 4.0.0
-
-
-
- Pfff repository
- https://raw.github.com/SRGSSR/pfff/mvn-repo/
-
-
-
- com.example
- pfff
- 9
- jar
-
- pfff
- Play Features and Functionalities with Flair
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 1.5.9.RELEASE
-
-
-
-
- UTF-8
- UTF-8
- 1.8
-
-
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
- org.postgresql
- postgresql
- 9.4-1201-jdbc4
-
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+
+
+
+ Pfff repository
+ https://raw.github.com/SRGSSR/pfff/mvn-repo/
+
+
+
+ com.example
+ pfff
+ 9
+ jar
+
+ pfff
+ Play Features and Functionalities with Flair
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.5.9.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.postgresql
+ postgresql
+ 9.4-1201-jdbc4
+
com.h2database
@@ -64,117 +64,111 @@
spring-boot-starter-thymeleaf
-
- org.springframework.boot
- spring-boot-starter-security
-
-
-
- org.springframework.security
- spring-security-test
- test
-
-
-
- org.webjars
- jquery
- 3.3.1
-
-
- org.webjars
- bootstrap
- 3.3.7
-
-
- org.webjars
- webjars-locator
-
-
- org.webjars
- font-awesome
- 5.4.1
-
-
- org.assertj
- assertj-core
-
-
- ch.srf.integrationlayer
- integrationlayer-domain-objects
- 1.20.272
-
-
-
- com.google.guava
- guava
- 27.0.1-jre
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
-
- maven-clean-plugin
- 2.5
-
-
-
- src/main/resources/static
-
- *
-
-
-
-
-
-
-
- com.github.eirslett
- frontend-maven-plugin
- 1.6
-
- portal-app
-
-
-
- install node and npm
-
- install-node-and-npm
-
-
- v6.9.5
- 3.10.10
-
-
-
-
- npm install
-
- npm
-
-
- install
-
-
-
-
- prod
-
- npm
-
-
- run-script prod
-
- generate-resources
-
-
-
-
-
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+ org.webjars
+ jquery
+ 3.3.1
+
+
+ org.webjars
+ bootstrap
+ 3.3.7
+
+
+ org.webjars
+ webjars-locator
+
+
+ org.webjars
+ font-awesome
+ 5.4.1
+
+
+ org.assertj
+ assertj-core
+
+
+ ch.srf.integrationlayer
+ integrationlayer-domain-objects
+ 1.20.272
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+ maven-clean-plugin
+ 2.5
+
+
+
+ src/main/resources/static
+
+ *
+
+
+
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ 1.6
+
+ portal-app
+
+
+
+ install node and npm
+
+ install-node-and-npm
+
+
+ v6.9.5
+ 3.10.10
+
+
+
+
+ npm install
+
+ npm
+
+
+ install
+
+
+
+
+ prod
+
+ npm
+
+
+ run-script prod
+
+ generate-resources
+
+
+
+
+
diff --git a/src/main/java/com/example/pfff/config/AuthenticationConfig.java b/src/main/java/com/example/pfff/config/AuthenticationConfig.java
index 3134cdf..a1dbad3 100644
--- a/src/main/java/com/example/pfff/config/AuthenticationConfig.java
+++ b/src/main/java/com/example/pfff/config/AuthenticationConfig.java
@@ -38,7 +38,7 @@ public AuthenticationConfig(
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
- .antMatchers("/api/v1/update/check", "/api/v1/whatisnew/text", "/api/v1/whatisnew/html", "/api/v1/version", "/api/v*/playlist/recommendation/**", "/webjars/bootstrap/**", "/webjars/bootstrap/**", "/webjars/jquery/**", "/webjars/font-awesome/**").permitAll()
+ .antMatchers("/api/v1/update/check", "/api/v1/whatisnew/text", "/api/v1/whatisnew/html", "/api/v1/version", "/api/v*/playlist/**", "/webjars/bootstrap/**", "/webjars/bootstrap/**", "/webjars/jquery/**", "/webjars/font-awesome/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
diff --git a/src/main/java/com/example/pfff/controller/RecommendationController.java b/src/main/java/com/example/pfff/controller/RecommendationController.java
index 3d1ca99..58cefa7 100644
--- a/src/main/java/com/example/pfff/controller/RecommendationController.java
+++ b/src/main/java/com/example/pfff/controller/RecommendationController.java
@@ -4,10 +4,7 @@
import com.example.pfff.service.RecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.UriComponentsBuilder;
@@ -24,6 +21,7 @@ public class RecommendationController {
@Autowired
RecommendationService service;
+ @Deprecated
@RequestMapping("/api/v1/playlist/recommendation/{purpose}/{urn}")
@ResponseBody
Object recommendationV1(
@@ -47,7 +45,7 @@ Object recommendationV1(
@RequestMapping("/api/v2/playlist/recommendation/{purpose}/{urn}")
@ResponseBody
- Object recommendationV2(
+ RecommendedList recommendationV2(
HttpServletRequest request,
@PathVariable("purpose") String purpose,
@PathVariable("urn") String urn,
@@ -60,4 +58,13 @@ private RecommendedList getRecommendationList(@PathVariable("purpose") String pu
recommendedList.addUrn(0, urn);
return recommendedList;
}
+
+ @RequestMapping("/api/v2/playlist/personalRecommendation")
+ @ResponseBody
+ RecommendedList personalRecommendation(
+ HttpServletRequest request,
+ @RequestParam(value = "user", required = false, defaultValue = "unknown") String userId) {
+ return service.rtsPlayHomePersonalRecommendation(userId);
+ }
+
}
diff --git a/src/main/java/com/example/pfff/model/RecommendedList.java b/src/main/java/com/example/pfff/model/RecommendedList.java
index 2578ba9..e34a100 100644
--- a/src/main/java/com/example/pfff/model/RecommendedList.java
+++ b/src/main/java/com/example/pfff/model/RecommendedList.java
@@ -17,6 +17,12 @@ public class RecommendedList {
private String recommendationId;
private List urns;
+ private String title;
+
+ public RecommendedList(String title, String host, String recommendationId, List urns) {
+ this(host, recommendationId, urns);
+ this.title = title;
+ }
public RecommendedList(String host, String recommendationId, List urns) {
if (host != null && recommendationId != null) {
@@ -42,6 +48,10 @@ public List getUrns() {
return urns;
}
+ public String getTitle() {
+ return title;
+ }
+
public void addUrn(int index, String urn) {
this.urns.add(index, urn);
}
diff --git a/src/main/java/com/example/pfff/model/peach/PersonalRecommendationResult.java b/src/main/java/com/example/pfff/model/peach/PersonalRecommendationResult.java
new file mode 100644
index 0000000..d9ba56c
--- /dev/null
+++ b/src/main/java/com/example/pfff/model/peach/PersonalRecommendationResult.java
@@ -0,0 +1,56 @@
+package com.example.pfff.model.peach;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class PersonalRecommendationResult {
+ public String status;
+ public String codops;
+ public Result result;
+
+ public static class Result {
+
+ @JsonProperty("fallback_used")
+ public boolean fallbackUsed;
+ public String title;
+ public List- items;
+ public String id;
+ }
+ public static class Item {
+
+ public String urn;
+ //TODO Implement once explanation semantics has been implemented in peach backend
+ public Explanation explanation;
+
+ }
+ /**
+ * Explanation has been defined in Jan. 2019 but not yet used.
+ */
+ public static class Explanation {
+
+ public String text;
+ @JsonProperty("media_reference_urn")
+ public String mediaReferenceUrn;
+ }
+
+ public String getTitle() {
+ return result != null ? result.title : null;
+ }
+
+ public String getRecommendationId() {
+ return result == null ? null : result.id;
+ }
+
+ public List getUrns() {
+ if (result != null && result.items != null) {
+ return result.items.stream().map((i) -> i.urn).collect(Collectors.toList());
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/pfff/service/RecommendationService.java b/src/main/java/com/example/pfff/service/RecommendationService.java
index b0dfb34..868dd94 100644
--- a/src/main/java/com/example/pfff/service/RecommendationService.java
+++ b/src/main/java/com/example/pfff/service/RecommendationService.java
@@ -5,9 +5,9 @@
import ch.srg.il.domain.v2_0.Media;
import ch.srg.il.domain.v2_0.MediaType;
import com.example.pfff.model.Environment;
+import com.example.pfff.model.peach.PersonalRecommendationResult;
import com.example.pfff.model.RecommendedList;
import com.example.pfff.model.peach.RecommendationResult;
-import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
@@ -16,6 +16,7 @@
import org.springframework.web.util.UriComponentsBuilder;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -60,7 +61,8 @@ private RecommendedList rtsAudioRecommendedList(String urn) {
return new RecommendedList();
}
- List episodes = Lists.reverse(episodeComposition.getList());
+ List episodes = new ArrayList<>(episodeComposition.getList());
+ Collections.reverse(episodes);
List fullLengthUrns = episodes.stream().map(EpisodeWithMedias::getFullLengthUrn).collect(Collectors.toList());
List clipUrns = episodes.stream().flatMap(e -> e.getMediaList().stream().filter(m -> m.getMediaType() == MediaType.AUDIO)).map(Media::getUrn).collect(Collectors.toList());
clipUrns.removeAll(fullLengthUrns);
@@ -97,7 +99,8 @@ private RecommendedList rtsAudioRecommendedList(String urn) {
urns.remove(urn);
if (episodeComposition.getNext() != null) {
- recommendationResult.addAll(Lists.reverse(urns));
+ Collections.reverse(urns);
+ recommendationResult.addAll(urns);
} else {
recommendationResult.addAll(urns);
}
@@ -128,4 +131,18 @@ private RecommendedList rtsVideoRecommendedList(String purpose, String urn, bool
return new RecommendedList(url.getHost(), recommendationResult.getRecommendationId(),
recommendationResult.getUrns());
}
+
+ public RecommendedList rtsPlayHomePersonalRecommendation(String userId) {
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance().scheme("http")
+ .host("peach.ebu.io").path("api/v1/chrts/play_home_personal_rec");
+ uriComponentsBuilder.queryParam("user_id", userId);
+ UriComponents url = uriComponentsBuilder.build();
+
+ System.out.println(url.toUriString());
+
+ PersonalRecommendationResult result = restTemplate
+ .exchange(url.toUriString(), HttpMethod.GET, null, PersonalRecommendationResult.class).getBody();
+
+ return new RecommendedList(result.getTitle(), url.getHost(), result.getRecommendationId(), result.getUrns());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/example/pfff/controller/RecommendationServiceTests.java b/src/test/java/com/example/pfff/controller/RecommendationServiceTests.java
index a6974b6..73c45eb 100644
--- a/src/test/java/com/example/pfff/controller/RecommendationServiceTests.java
+++ b/src/test/java/com/example/pfff/controller/RecommendationServiceTests.java
@@ -9,8 +9,6 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
-import java.util.List;
-
@RunWith(SpringRunner.class)
@SpringBootTest
public class RecommendationServiceTests {
@@ -27,9 +25,7 @@ public void getRecommendedUrnsContinuousplaybackRTSVideoTest() {
Assert.assertNotNull(recommendedList.getRecommendationId());
Assert.assertTrue(recommendedList.getRecommendationId().startsWith("io.ebu.peach:"));
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertTrue(recommendedList.getUrns().size() > 0);
- Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ assertValidList(recommendedList);
}
@Test
@@ -41,9 +37,7 @@ public void getRecommendedUrnsContinuousplaybackStandaloneRTSVideoTest() {
Assert.assertNotNull(recommendedList.getRecommendationId());
Assert.assertTrue(recommendedList.getRecommendationId().startsWith("io.ebu.peach:"));
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertTrue(recommendedList.getUrns().size() > 0);
- Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ assertValidList(recommendedList);
}
@Test
@@ -57,9 +51,7 @@ public void getRecommendedUrnsContinuousplaybackRTSAudioFullTest() {
Assert.assertNotNull(recommendedList.getRecommendationId());
Assert.assertTrue(recommendedList.getRecommendationId().startsWith("ch.srgssr.playfff:EpisodeComposition/LatestByShow/" + showURN + "/FullLength/"));
Assert.assertTrue(recommendedList.getRecommendationId().contains(mediaURN));
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertTrue(recommendedList.getUrns().size() > 0);
- Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ assertValidList(recommendedList);
}
@Test
@@ -73,9 +65,7 @@ public void getRecommendedUrnsContinuousplaybackStandaloneRTSAudioFullTest() {
Assert.assertNotNull(recommendedList.getRecommendationId());
Assert.assertTrue(recommendedList.getRecommendationId().startsWith("ch.srgssr.playfff:EpisodeComposition/LatestByShow/" + showURN + "/FullLength/"));
Assert.assertTrue(recommendedList.getRecommendationId().contains(mediaURN));
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertTrue(recommendedList.getUrns().size() > 0);
- Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ assertValidList(recommendedList);
}
@Test
@@ -89,9 +79,7 @@ public void getRecommendedUrnsContinuousplaybackRTSAudioClipTest() {
Assert.assertNotNull(recommendedList.getRecommendationId());
Assert.assertTrue(recommendedList.getRecommendationId().startsWith("ch.srgssr.playfff:EpisodeComposition/LatestByShow/" + showURN + "/Clip/"));
Assert.assertTrue(recommendedList.getRecommendationId().contains(mediaURN));
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertTrue(recommendedList.getUrns().size() > 0);
- Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ assertValidList(recommendedList);
}
@Test
@@ -105,9 +93,7 @@ public void getRecommendedUrnsContinuousplaybackStandaloneRTSAudioClipTest() {
Assert.assertNotNull(recommendedList.getRecommendationId());
Assert.assertTrue(recommendedList.getRecommendationId().startsWith("ch.srgssr.playfff:EpisodeComposition/LatestByShow/" + showURN + "/Clip/"));
Assert.assertTrue(recommendedList.getRecommendationId().contains(mediaURN));
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertTrue(recommendedList.getUrns().size() > 0);
- Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ assertValidList(recommendedList);
}
@Test
@@ -117,9 +103,7 @@ public void getRecommendedUrnsContinuousplaybackSRFTest() {
boolean standalone = false;
RecommendedList recommendedList = recommendationService.getRecommendedUrns(purpose, mediaURN, standalone);
- Assert.assertNull(recommendedList.getRecommendationId());
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertEquals(recommendedList.getUrns().size(), 0);
+ assertInvalidList(recommendedList);
}
@Test
@@ -129,9 +113,7 @@ public void getRecommendedUrnsContinuousplaybackStandaloneSRFTest() {
boolean standalone = true;
RecommendedList recommendedList = recommendationService.getRecommendedUrns(purpose, mediaURN, standalone);
- Assert.assertNull(recommendedList.getRecommendationId());
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertEquals(recommendedList.getUrns().size(), 0);
+ assertInvalidList(recommendedList);
}
@Test
@@ -141,9 +123,7 @@ public void getRecommendedUrnsContinuousplaybackSwissTxtTest() {
boolean standalone = false;
RecommendedList recommendedList = recommendationService.getRecommendedUrns(purpose, mediaURN, standalone);
- Assert.assertNull(recommendedList.getRecommendationId());
- Assert.assertNotNull(recommendedList.getUrns());
- Assert.assertEquals(recommendedList.getUrns().size(), 0);
+ assertInvalidList(recommendedList);
}
@Test
@@ -153,6 +133,36 @@ public void getRecommendedUrnsContinuousplaybackStandaloneSwisstxtTest() {
boolean standalone = true;
RecommendedList recommendedList = recommendationService.getRecommendedUrns(purpose, mediaURN, standalone);
+ assertInvalidList(recommendedList);
+ }
+
+ @Test
+ public void playHomeTestInvalidUser() {
+ RecommendedList recommendedList = recommendationService.rtsPlayHomePersonalRecommendation("invalid user");
+
+ assertValidList(recommendedList);
+ }
+
+ @Test
+ public void playHomeTestUserUnknown() {
+ RecommendedList recommendedList = recommendationService.rtsPlayHomePersonalRecommendation("unknown");
+
+ assertValidList(recommendedList);
+ }
+
+ @Test
+ public void playHomeTestUser9() {
+ RecommendedList recommendedList = recommendationService.rtsPlayHomePersonalRecommendation("9");
+
+ assertValidList(recommendedList);
+ }
+
+ private void assertValidList(RecommendedList recommendedList) {
+ Assert.assertTrue(recommendedList.getUrns().size() > 0);
+ Assert.assertTrue(recommendedList.getUrns().size() < 50);
+ }
+
+ private void assertInvalidList(RecommendedList recommendedList) {
Assert.assertNull(recommendedList.getRecommendationId());
Assert.assertNotNull(recommendedList.getUrns());
Assert.assertEquals(recommendedList.getUrns().size(), 0);