Skip to content

Commit

Permalink
feat(srgssr-middleware): allow overriding of resource properties
Browse files Browse the repository at this point in the history
Improves the `resource` property overriding mechanism when the `mediaData`
object is provided to `player.src`. In certain scenarios, developers may want
to add a subtitle track not provided by the media composition or even replace
the media being played.

Although this use case is rare, it's essential to offer developers the
flexibility to do so, ensuring a smoother developer experience.

> [!TIP]
> How to override a resource properties

```javascript
const player = pillarbox('player');

// overrides the URL and adds a blocking reason
player.src({
  src: 'urn:bu:media:id',
  type:'srgssr/urn'
  mediaData: {
    url: 'https://fake-url.com/new-media.m3u8',
    mimeType: 'application/x-mpegURL',
    blockedSegments:[{
      blockReason: "ENDDATE",
      markIn: 5_000,
      markOut: 60_000,
    }]
  }
});
```

> [!CAUTION]
> It's the developer's responsibility to ensure data consistency, otherwise
> unexpected errors may occur, preventing the media from playing.

- modify `composeSrcMediaData` to merge the `resource` object with `srcMediaData`
- add a tutorial demonstrating how to override a property
- introduce a mock object representing a `resource`
- modify a test to eliminate dependency on the `dataProvider`
- add a test case illustrating the possibility of overriding a property
  • Loading branch information
amtins committed Apr 15, 2024
1 parent 31724d7 commit 7e12ef7
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 11 deletions.
37 changes: 37 additions & 0 deletions docs/api/tutorials/Player Tips and Tricks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
The **Player Tips and Tricks** section serves as a valuable resource for users seeking guidance on optimizing their experience with Pillarbox.

## Adding or Overriding Resource Properties

There are scenarios where developers need to customize resource properties or override existing ones. This section provides insights into achieving both objectives effectively.

> This mechanism is specific to media content from SRG SSR.
### Example

Consider the following use case:

1. **Adding a Custom Property**:
- Sometimes, you may want to introduce a custom property to a resource. This could involve adding metadata, specifying additional behavior, or enhancing the resource's functionality.

2. **Overriding an Existing Property**:
- In other cases, you might need to modify an existing property. For instance, you could override the media source.

```javascript
const player = pillarbox('player');

// overrides the URL and adds a blocking reason
player.src({
src: 'urn:bu:media:id',
type:'srgssr/urn'
mediaData: {
// override the media URL
url: 'https://fake-url.com/new-media.m3u8',
// override the MIME type. Mandatory if you switch from Dash to HLS
mimeType: 'application/x-mpegURL',
// add a custom property
customProperty: 'Custom value'
}
});
```

> It's the developer's responsibility to ensure data consistency, otherwise unexpected errors may occur, preventing the media from playing.
11 changes: 9 additions & 2 deletions src/middleware/srgssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,21 @@ class SrgSsr {
*/
static composeSrcMediaData(
{ mediaData: srcMediaData, disableTrackers },
{ url, mimeType, keySystems, ...mediaData }
resource
) {
const {
url,
mimeType,
keySystems,
...mediaData
} = Pillarbox.obj.merge(resource, srcMediaData);

return {
src: url,
type: mimeType,
keySystems,
disableTrackers,
mediaData: Pillarbox.obj.merge(mediaData, srcMediaData),
mediaData,
};
}

Expand Down
235 changes: 235 additions & 0 deletions test/__mocks__/mainResource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
{
"analyticsData": {
"srg_pr_id": "10313468",
"srg_plid": "9931005",
"ns_st_pl": "Les beaux parleurs",
"ns_st_pr": "Les beaux parleurs du 24.03.2019",
"ns_st_dt": "2019-03-24",
"ns_st_ddt": "2019-03-24",
"ns_st_tdt": "*null",
"ns_st_tm": "*null",
"ns_st_tep": "500087771",
"ns_st_li": "0",
"ns_st_stc": "0867",
"ns_st_st": "RTS Online",
"ns_st_tpr": "9931005",
"ns_st_en": "*null",
"ns_st_ge": "*null",
"ns_st_ia": "*null",
"ns_st_ce": "1",
"ns_st_cdm": "eo",
"ns_st_cmt": "ec",
"srg_unit": "RTS",
"srg_c1": "full",
"srg_c2": "video_la-1ere_les-beaux-parleurs",
"srg_c3": "RTS.ch",
"srg_tv_id": "500087771",
"srg_aod_prid": "10313468",
"ns_st_ep": "Les beaux parleurs - Y'a pas de fumée sans feu (vidéo)",
"ns_st_ty": "Video",
"ns_st_ci": "10313496",
"ns_st_el": "4827120",
"ns_st_cl": "4827120",
"ns_st_sl": "4827120",
"srg_mgeobl": "false",
"ns_st_tp": "1",
"ns_st_cn": "1",
"ns_st_ct": "vc12",
"ns_st_pn": "1",
"srg_mqual": "HD",
"srg_mpres": "DEFAULT"
},
"analyticsMetadata": {
"media_episode_id": "10313468",
"media_show_id": "9931005",
"media_show": "Les beaux parleurs",
"media_episode": "Les beaux parleurs du 24.03.2019",
"media_is_livestream": "false",
"media_full_length": "full",
"media_enterprise_units": "RTS",
"media_joker1": "full",
"media_joker2": "video_la-1ere_les-beaux-parleurs",
"media_joker3": "RTS.ch",
"media_is_web_only": "true",
"media_production_source": "produced.for.web",
"media_tv_id": "500087771",
"media_thumbnail": "https://www.rts.ch/2019/03/24/14/03/10313495.image/16x9/scale/width/344",
"media_publication_date": "2019-03-24",
"media_publication_time": "11:04:00",
"media_publication_datetime": "2019-03-24T11:04:00+01:00",
"media_content_group": "Les beaux parleurs,La 1ère",
"media_since_publication_d": "1796",
"media_since_publication_h": "43109",
"media_segment": "Les beaux parleurs - Y'a pas de fumée sans feu (vidéo)",
"media_type": "Video",
"media_segment_id": "10313496",
"media_episode_length": "4827",
"media_segment_length": "4827",
"media_number_of_segment_selected": "1",
"media_number_of_segments_total": "1",
"media_duration_category": "long",
"media_is_geoblocked": "false",
"media_urn": "urn:rts:video:10313496",
"media_streaming_quality": "HD",
"media_special_format": "DEFAULT",
"media_url": "https://rts-vod-amd.akamaized.net/ww/10313496/264fac24-70a6-34bc-a37e-751493d10e20/master.m3u8"
},
"blockedSegments": [],
"imageUrl": "https://www.rts.ch/2019/03/24/14/03/10313495.image/16x9",
"chapters": [
{
"id": "10313496",
"mediaType": "VIDEO",
"vendor": "RTS",
"urn": "urn:rts:video:10313496",
"title": "Les beaux parleurs - Y'a pas de fumée sans feu (vidéo)",
"description": "Jonas Schneiter est entouré pour cette émission de Coline de Senarclens, Micheline Calmy-Rey, Michel Audétat, et de l'humoriste Benjamin Décosterd. L'équipe débat sur la tendance mondiale à la légalisation du cannabis, parle du rapport des Suisses à l'argent et fait le point sur la dernière session du Conseil des droits de l'homme à Genève. En deuxième partie d'émission, Claude-Inga Barbey nous présente à sa manière la philosophe et économiste, Sophie Swaton.",
"imageUrl": "https://www.rts.ch/2019/03/24/14/03/10313495.image/16x9",
"imageTitle": "Les beaux parleurs - Y'a pas de fumée sans feu [RTS]",
"type": "EPISODE",
"date": "2019-03-24T11:04:00+01:00",
"duration": 4827120,
"validFrom": "2019-03-24T11:04:00+01:00",
"playableAbroad": true,
"timeIntervalList": [
null
],
"socialCountList": [
null
],
"displayable": true,
"position": 0,
"noEmbed": false,
"analyticsData": [
null
],
"analyticsMetadata": [
null
],
"eventData": "$9db05194d7854399$80484ccbcc3b0481db9c5ee344196397e72e218449a7cf769782062d650c6363ec5787f4fa538e7bcbc2f7ccfaf9f70f5fdc50bde3986541b04285c7125bc0a93731c18e6afea7b0154f874accaf319abb738541e8634ce09c91a499df2faf6d0281e66c6e3ab9f5c77f9c75cfff9297caab58c1894c70551ddbf7f1aa591d5e52a928cfb1055181d9ed6f339fd713abc5f4eae42c299d3411ead600cec4dd927e4b8b8493c0ab1ab67106c12918636b5d4dd0206de8f465b754390ea5c90a1cb3c47228f3421c8152fe9aef74599aca84c2082b2dee4475423e9543b89d0310720ed491f3c5a73ae688c285a4e37fed9fdffe1beb6a33ad94f37ddf48cb3f469d0f43d0df290cd7bac18e66479aa7e23319efa7345f41adc9e8a3c4bb595a0342ebed072b2358abb1d2844889fe37be04b52a072532b95d4378ee5a1ccec3ad",
"resourceList": [
null
],
"aspectRatio": "16:9"
},
{
"id": "10313478",
"mediaType": "VIDEO",
"vendor": "RTS",
"urn": "urn:rts:video:10313478",
"title": "La chronique de Benjamin Décosterd - Les élections vaudoises",
"imageUrl": "https://www.rts.ch/2019/03/24/14/03/10313469.image/16x9",
"imageTitle": "La chronique de Benjamin Décosterd - Les élections vaudoises [RTS]",
"type": "CLIP",
"date": "2019-03-24T14:03:03+01:00",
"duration": 294720,
"validFrom": "2019-03-24T11:04:00+01:00",
"playableAbroad": true,
"displayable": true,
"fullLengthUrn": "urn:rts:video:10313496",
"position": 1,
"noEmbed": false,
"analyticsData": [
null
],
"analyticsMetadata": [
null
],
"eventData": "$1ca8de52767edc7c$b53972610b30bdea9d4359fc4a078387badc1f3d8426d8636ed83e5a91122627e958e4d483a567a26623211ca61da22316e61b18e967af9f0f1387153dc42f76dc023d7a09693ee942bd7ab35c5b8e39e4bc1b120264b4829887d57906cf629a2d9b52f9628f20f491bd8d2c3a089af78e0358223b383f4af1b98ee4c805956f59ce413194e7784f8bb76f30bbd1890bf35bccfc921b3e9548ce6d6b800ce201ab5aebc7cb30a15eb348d5fbed49d227fc7e9c56918d1ed20e865dedd396cc3c9d61ec96e2053c8363410706218f846aed42f0db7bee0265ad65a95bbac9c3b71bc92b7825db754d00276493337cc316d3bf638d6c79760731a158a4d17d0d54b64cae3c04ee433db2d93c0fce82a9da8c41a4f5970a7baf46cbfd5af903dc7049f1bc1890b4dff524b6dffef34fbfda",
"fullLengthMarkIn": 105000,
"fullLengthMarkOut": 399720,
"resourceList": [
null
],
"aspectRatio": "16:9"
},
{
"id": "10313492",
"mediaType": "VIDEO",
"vendor": "RTS",
"urn": "urn:rts:video:10313492",
"title": "Le portrait de Sophie Swaton par Claude-Inga Barbey",
"description": "Le portrait de Sophie Swaton est brossé avec humour par Claude-Inga Barbey à travers l'un de ses personnages.",
"imageUrl": "https://www.rts.ch/2019/03/24/14/03/10313491.image/16x9",
"imageTitle": "Le portrait de Sophie Swaton par Claude-Inga Barbey [RTS]",
"type": "CLIP",
"date": "2019-03-24T14:03:10+01:00",
"duration": 314480,
"validFrom": "2019-03-24T11:04:00+01:00",
"playableAbroad": true,
"displayable": true,
"fullLengthUrn": "urn:rts:video:10313496",
"position": 2,
"noEmbed": false,
"analyticsData": [
null
],
"analyticsMetadata": [
null
],
"eventData": "$6eabc1a0debc4468$58f6fe4a463bdfaed3002421b89d621bfdf317941437ea4938085440bc69ad0bf903374a872acd6b43150231479dc804e8e2ab8ac8542ddee99cbee06683a2f262175c5e4464a0dfaaa6d9d0b2154ebcce3ccf5962b1fea46b274d551b88d568f9be511e91b78511726ad1043c49fed7c636630191c24a70ff8e78c122e717021cf8dc2fa018352e32a5aa5e7946cef535eef7f9b8edb5824bab1e595ae896948ab6b21366eb5cff04f4e41131279eb26efbf6a7b653400531a1248f6ff5d73527c24d25fab839373fc6bc74dce8fddc83ab23f9498a06c94aa86885c18b0c72d86f4b463d4146a3fa27a1c8136ab124546e0690c24a9a6a0d61b427cdf0a1704e30be95955dfb06e089a5caa3a80a2abfc5864d2b0b12a05c1d5481a4200ef0f15b2cef1996592112ac84656af6f3d6",
"fullLengthMarkIn": 3336120,
"fullLengthMarkOut": 3650600,
"resourceList": [
null
],
"aspectRatio": "16:9"
},
{
"id": "10277526",
"mediaType": "AUDIO",
"vendor": "RTS",
"urn": "urn:rts:audio:10277526",
"title": "Les beaux parleurs - Y'a pas de fumée sans feu (audio)",
"description": "Jonas Schneiter est entouré pour cette émission de Coline de Senarclens, Micheline Calmy-Rey, Michel Audétat, et de l'humoriste Benjamin Décosterd. L'équipe débat sur la tendance mondiale à la légalisation du cannabis, parle du rapport des Suisses à l'argent et fait le point sur la dernière session du Conseil des droits de l'homme à Genève. En deuxième partie d'émission, Claude-Inga Barbey nous présente à sa manière la philosophe et économiste, Sophie Swaton.",
"imageUrl": "https://www.rts.ch/2019/03/24/16/10/10313604.image/16x9",
"imageTitle": "Les beaux parleurs. [Julien Audemars/Philippe Christin - RTS]",
"imageCopyright": "Julien Audemars/Philippe Christin - RTS",
"type": "CLIP",
"date": "2019-03-24T11:03:50+01:00",
"duration": 5170000,
"podcastHdUrl": "https://rts-aod-dd.akamaized.net/ww/10277526/dba37188-f708-326d-803a-c2cfe229bb82.mp3",
"playableAbroad": true,
"displayable": true,
"fullLengthUrn": "urn:rts:video:10313496",
"position": 3,
"noEmbed": false,
"analyticsMetadata": [
null
],
"eventData": "$8448618a46137271$2345a49d01fecfd8e365920836847642fdbb344395c09cb03ba124bc565a7db127fa2590dcf518fa2c5005c6fe8f8dc080e5089ddecf442fb6861ba4d6c5c5c0576f7e800badd46fff89abadebbc5a20c602b00e5030b8e75275c67a8dfa3babce94b173aba9b8cbd93764e08b2a3fcf70a29f4942c8021518752c4773b148a87f60c17750a75d775e5769d26986bdad1e3dee33a2c30c0969a0250f18e84d818052b80e63bea28d285b35ffe5fafc402d67f61da8c1e666c5e82451de3b09c244f1fdd0fb30d985e1c6e78dc696ee959d866f618e965d76328320fd1ff9dd57b08941fc4545b9598107de3d36ee5fcc1e1e848a433579befffc9e8aba70187b12c8bc481dadd046b54a8614b0e1cf6f9408aa5a19c1941f0e65b10e14faeb4dc96f7d1b120420ed4d515523cf9c5d1c",
"fullLengthMarkIn": 0,
"fullLengthMarkOut": 0,
"resourceList": [
null
]
}
],
"dvr": false,
"eventData": "$9db05194d7854399$80484ccbcc3b0481db9c5ee344196397e72e218449a7cf769782062d650c6363ec5787f4fa538e7bcbc2f7ccfaf9f70f5fdc50bde3986541b04285c7125bc0a93731c18e6afea7b0154f874accaf319abb738541e8634ce09c91a499df2faf6d0281e66c6e3ab9f5c77f9c75cfff9297caab58c1894c70551ddbf7f1aa591d5e52a928cfb1055181d9ed6f339fd713abc5f4eae42c299d3411ead600cec4dd927e4b8b8493c0ab1ab67106c12918636b5d4dd0206de8f465b754390ea5c90a1cb3c47228f3421c8152fe9aef74599aca84c2082b2dee4475423e9543b89d0310720ed491f3c5a73ae688c285a4e37fed9fdffe1beb6a33ad94f37ddf48cb3f469d0f43d0df290cd7bac18e66479aa7e23319efa7345f41adc9e8a3c4bb595a0342ebed072b2358abb1d2844889fe37be04b52a072532b95d4378ee5a1ccec3ad",
"id": "10313496",
"intervals": [
{
"type": "OPENING_CREDITS",
"markIn": 0,
"markOut": 12000
},
{
"type": "CLOSING_CREDITS",
"markIn": 1836000,
"markOut": 1906000
}
],
"live": false,
"mediaType": "VIDEO",
"mimeType": "application/x-mpegURL",
"presentation": "DEFAULT",
"quality": "HD",
"streaming": "HLS",
"subtitles": [],
"title": "Les beaux parleurs - Y'a pas de fumée sans feu (vidéo)",
"tokenType": "NONE",
"url": "https://rts-vod-amd.akamaized.net/ww/10313496/264fac24-70a6-34bc-a37e-751493d10e20/master.m3u8",
"urn": "urn:rts:video:10313496",
"vendor": "RTS"
}
35 changes: 26 additions & 9 deletions test/middleware/srgssr.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import MediaComposition from '../../src/dataProvider/model/MediaComposition.js';
import urnCredits from '../__mocks__/urn:rts:video:10313496-credits.json';
import urnRtsAudio from '../__mocks__/urn:rts:audio:3262320.json';
import srcMediaObj from '../__mocks__/srcMediaObj.json';
import mainResource from '../__mocks__/mainResource.json';
import Pillarbox from '../../src/pillarbox.js';
import AkamaiTokenService from '../../src/utils/AkamaiTokenService.js';

Expand All @@ -18,6 +19,11 @@ describe('SrgSsr', () => {
let player;

beforeAll(() => {
Pillarbox.obj.merge.mockImplementation((...params) => {
// does not handle video.js deep merge
return Object.assign(...params);
});

DataProvider.mockImplementation(() => {
return {
handleRequest: (fn) => {
Expand Down Expand Up @@ -481,17 +487,28 @@ describe('SrgSsr', () => {
*/
describe('composeSrcMediaData', () => {
it('should return a source object', async () => {
const mockDataProvider = new DataProvider();
const mediaComposition =
await mockDataProvider.handleRequest()('urn:fake');
const [mainSource] = mediaComposition.getMainResources();
expect(SrgSsr.composeSrcMediaData({}, mainResource)).toMatchObject({
src: mainResource.url,
type: mainResource.mimeType,
keySystems: undefined,
disableTrackers: undefined,
mediaData: expect.any(Object),
});
});

it('should override the resource URL', async () => {
const url = 'https://fake-url.com/resource.m3u8';

expect(SrgSsr.composeSrcMediaData({}, mainSource)).toMatchObject({
src: mainSource.url,
type: mainSource.mimeType,
expect(SrgSsr.composeSrcMediaData({
mediaData: {
url
}
}, mainResource)).toMatchObject({
src: url,
type: mainResource.mimeType,
keySystems: undefined,
disableTrackers: undefined,
mediaData: undefined,
mediaData: expect.any(Object),
});
});
});
Expand Down Expand Up @@ -819,7 +836,7 @@ describe('SrgSsr', () => {
*****************************************************************************
*/
describe('getSrcMediaObj', () => {
it('should do something', async () => {
it('should return a value', async () => {
const result = await SrgSsr.getSrcMediaObj(player, { src: 'urn:fake' });

expect(result).toEqual(expect.any(Object));
Expand Down

0 comments on commit 7e12ef7

Please sign in to comment.