Skip to content

Commit

Permalink
Merge pull request #26 from SRGSSR/develop
Browse files Browse the repository at this point in the history
Release Continuous Playback for RSI, RTR, SRF and SWI
  • Loading branch information
pyby authored Jul 5, 2019
2 parents fc7fd65 + c881608 commit c0f94bb
Show file tree
Hide file tree
Showing 51 changed files with 7,813 additions and 46 deletions.
18 changes: 10 additions & 8 deletions docs/RECOMMENDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ Since July 2018, Play Android (2.0.207 and more) and Play iOS (2.8.3-272 and mor

The API doesn't not support paginations, therefore mobile applications didn't implement pagination. The media recommendation list must have at least 49 items, the 50th is the requested media.

#### RTS `urn.contains(":rts:")`
#### RTS videos

- For videos `urn.contains(":video:")`, it asks Peach recommendation `continuous_playback_mobile` service.
- For audios `urn.contains(":audio:")`, it asks Playfff recommendation. Based on IL requests, without personalization. Here is how it works:
- For RTS videos, it asks Peach recommendation `continuous_playback_mobile` service.

#### RSI, RTR, SRF, SWI videos and RSI, RTR, RTS and SRF audios

- It asks Playfff recommendation. Based on IL requests, without personalization. Here is how it works:
- Get `IL-Media`. It returns an empty list if it's a `LIVESTREAM` or a `SCHEDULED_LIVESTREAM`.
- Get `IL-EpisodeComposition` with last 100 episodes. Sort episodes with a date ascending order.
- Determine if the media is a full length or a clip.
Expand All @@ -29,8 +32,11 @@ The API doesn't not support paginations, therefore mobile applications didn't im
- Then:
- *If* `nextUrl` exists (show has more than 100 episodes), oldest medias in the date descending order.
- *Else* (show has less than 100 episodes), oldest medias in the date ascending order.
- It can get `IL-MediaComposition` if the media urn isn't found, and has not the `CLIP` type.
- It does not return clips if `VIDEO` media type and `standalone == false`.
- If clips are not in `IL-EpisodeComposition`, it fallbacks to full lengths.

#### Other BUs
#### Swisstxt URNs or other MAMs

- No recommendation provided. It returns an empty list.

Expand All @@ -41,7 +47,3 @@ The API doesn't not support paginations, therefore mobile applications didn't im
#### RTS

- With and without an `userId`, it asks Peach recommendation `play_home_personal_rec` service.

## License

To be defined.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<groupId>ch.srgssr</groupId>
<artifactId>playfff</artifactId>
<version>11</version>
<version>12</version>
<packaging>jar</packaging>

<name>pfff</name>
Expand Down
185 changes: 185 additions & 0 deletions src/main/java/ch/srgssr/playfff/model/IlUrn.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package ch.srgssr.playfff.model;

import ch.srg.il.domain.v2_0.MediaType;

import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* This very simple class represents a URN provided by the IL.
*/
public class IlUrn {
private final static Pattern PATTERN_BUMAM = Pattern.compile("^urn:(srf|rts|rsi|rtr|swi|default):(?:[^:]+:)?(video|audio|videoset|show|assetgroup):([^:]+)$", Pattern.CASE_INSENSITIVE);
private final static Pattern PATTERN_SWISSTXT = Pattern.compile("^urn:(swisstxt):(?:[^:]+:)?(video|audio|videoset|show|assetgroup):(srf|rts|rsi|rtr|swi):([^:]+)$", Pattern.CASE_INSENSITIVE);

public static final String ASSET_VIDEO = "video";
public static final String ASSET_VIDEO_SET = "videoset";
public static final String ASSET_AUDIO = "audio";
public static final String ASSET_SHOW = "show";
public static final String ASSET_GROUP = "assetgroup";

private String underlying;

private String bu;
private String assetType;
private String id;
private Mam mam;

public enum Mam {
SWISSTXT,
RTS,
RSI,
RTR,
SRF,
SWI
}

/**
* @param urn urn can be any media identifier, valid or not
* @throws IllegalArgumentException if not a valid urn
*/
public IlUrn(String urn) throws IllegalArgumentException {
if (!parseBuMam(urn)
&& !parseSwissTxt(urn)) {
throw new IllegalArgumentException(String.format("URN '%s' does not match a valid URN pattern.", urn));
}
}

private boolean parseBuMam(String urn) {
Matcher matcher;
matcher = PATTERN_BUMAM.matcher(urn);
if (matcher.matches()) {
bu = matcher.group(1).toLowerCase(Locale.US);
switch (bu) {
case "rtr":
mam = Mam.RTR;
break;
case "swi":
mam = Mam.SWI;
break;
case "rts":
mam = Mam.RTS;
break;
case "srf":
mam = Mam.SRF;
break;
case "rsi":
mam = Mam.RSI;
break;
default:
mam = null;
break;
}
assetType = matcher.group(2).toLowerCase(Locale.US);

if (assetType.equals(ASSET_GROUP)) {
// is a synonym of show (used in search request URN)
assetType = ASSET_SHOW;
}

id = matcher.group(3); // Do not transform ID since it is case sensitive.
underlying = "urn:" + bu + ":" + assetType + ":" + id;
return true;
} else {
return false;
}
}

private boolean parseSwissTxt(String urn) {
Matcher matcher = PATTERN_SWISSTXT.matcher(urn);
if (matcher.matches()) {
String swisstxt = matcher.group(1).toLowerCase(Locale.US);
bu = matcher.group(3).toLowerCase(Locale.US);
mam = Mam.SWISSTXT;
assetType = matcher.group(2).toLowerCase(Locale.US);
if (assetType.equals(ASSET_GROUP)) {
// is a synonym of show (used in search request URN)
assetType = ASSET_SHOW;
}
id = matcher.group(4);
underlying = "urn:" + swisstxt + ":" + assetType + ":" + bu + ":" + id;
return true;
} else {
return false;
}
}

public IlUrn(String bu, String assetType, String id) {
this.bu = bu == null ? "default" : bu;
this.assetType = assetType;
this.id = id;
underlying = "urn:" + this.bu + ":" + this.assetType + ":" + this.id;
}

public static String format(String bu, String assetType, String id) {
return String.format("urn:%s:%s:%s", bu, assetType, id);
}

/**
* Returns Business Unit.
*/
public String getBu() {
return bu;
}

/**
* @return Swisstxt or BU.
*/
public Mam getMam() {
return mam;
}

/**
* Return Asset Type (one of audio or video)
*/
public String getAssetType() {
return assetType;
}

public MediaType getMediaType() {
if (isAudio()) {
return MediaType.AUDIO;
} else if (isVideo()) {
return MediaType.VIDEO;
} else {
return null;
}
}

/**
* Return ID
*/
public String getId() {
return id;
}

/**
* Returns the underlying string representation.
*/
@Override
public String toString() {
return underlying;
}

public boolean isAudio() {
return ASSET_AUDIO.equals(assetType);
}

public boolean isVideo() {
return ASSET_VIDEO.equals(assetType) || ASSET_VIDEO_SET.equals(assetType);
}

public boolean isShow() {
return ASSET_SHOW.equals(assetType);
}


public boolean equalsToString(String o) {
try {
return underlying.equals(new IlUrn(o).toString());
} catch (IllegalArgumentException invalidUrnException) {
return false;
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/ch/srgssr/playfff/model/RecommendedList.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ public RecommendedList(String host, String recommendationId, List<String> urns)
} else {
this.recommendationId = recommendationId;
}
this.urns = (urns != null) ? urns : new ArrayList<String>();
this.urns = (urns != null) ? urns : new ArrayList<>();
}

public RecommendedList() {
this.urns = new ArrayList<String>();
this.urns = new ArrayList<>();
}

public String getRecommendationId() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package ch.srgssr.playfff.service;

import ch.srg.il.domain.v2_0.EpisodeComposition;
import ch.srg.il.domain.v2_0.Media;
import ch.srg.il.domain.v2_0.Show;
import ch.srg.il.domain.v2_0.*;
import ch.srg.jaxb.SrgUnmarshaller;
import ch.srgssr.playfff.model.Environment;
import org.assertj.core.util.VisibleForTesting;
Expand Down Expand Up @@ -77,6 +75,32 @@ public Show getShow(String showURN, Environment environment) {
}
}

public MediaList getRecommendedMediaList(String mediaURN, Environment environment) {
String path = "/integrationlayer/2.0/mediaList/recommended/byUrn/" + mediaURN + ".json";
try {
URI uri = new URI("http", null, environment.getBaseUrl(), PORT, path, null, null);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
SrgUnmarshaller unmarshaller = new SrgUnmarshaller();
return unmarshaller.unmarshal(responseEntity.getBody(), MediaType.APPLICATION_JSON, MediaList.class);
} catch (Exception e) {
logger.warn("http://{}{} : {}", environment.getBaseUrl(), path, e.getMessage());
return null;
}
}

public MediaComposition getMediaComposition(String mediaURN, Environment environment) {
String path = "/integrationlayer/2.0/mediaComposition/byUrn/" + mediaURN + ".json";
try {
URI uri = new URI("http", null, environment.getBaseUrl(), PORT, path, null, null);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
SrgUnmarshaller unmarshaller = new SrgUnmarshaller();
return unmarshaller.unmarshal(responseEntity.getBody(), MediaType.APPLICATION_JSON, MediaComposition.class);
} catch (Exception e) {
logger.warn("http://{}{} : {}", environment.getBaseUrl(), path, e.getMessage());
return null;
}
}

@VisibleForTesting
public RestTemplate getRestTemplate() {
return restTemplate;
Expand Down
Loading

0 comments on commit c0f94bb

Please sign in to comment.