Cloud Bookmarks
Bookmarks is a personalization library that provides bookmarking service i.e. save the last watched position to enable Continue Watching feature. The library facilitates maintaining the last watched position of any VOD content and provides APIs to retrieve the bookmarked position to allow starting playback from the last watched position. Recording the last watched position can be enforced either manually by the application or can be automated by the library.
Bookmarks is not applicable for Live content. It is also not applicable for Promo / Ad with VOD content.
BookmarksService
BookmarksService
declares all the APIs required to record, remove or retrieve the last watched position of a VOD content.
Since all the cloud bookmark network calls have to be authorized, the corresponding PlatformAuthorizer
object has to be provided for building BookmarksService
.
// endpoint of the Bookmarks microservice. will be provided while on-boarding onto Quickplay platform.
// the backslash at the end should be included.
val bookmarkEndPointURL = "https://example.com/"
val bookmarksService: BookmarksService = BookmarksFactory.createBookmarksService(
bookmarkEndPointURL,
platformAuthorizer // pass the corresponding object
)
Retrieve last watched position
As the application might need the last watched position before initiating playback (to display on content details page, etc.),
retrieving the bookmark position should be done by the application itself with the help of BookmarksService
. All these
calls rely on the PlatformAuthorizer
provided to BookmarksService
for authorization and user identification.
For a singular content
Use BookmarksService.getBookmark
for retrieving the last watched position of a singular piece of content.
The API returns a BookmarksRecord
object that encapsulates all the relevant information if the call is successful, and an Error
otherwise.
val contentID = "1234-5678-90ABC" // unique identifier of the content
val result = bookmarksService.getBookmark(contentID)
when (result) {
is Result.Success -> {
val bookmarksRecord = result.value
val contentId = bookmarksRecord.contentId // unique ID of the content
val bookmarkPosition = bookmarksRecord.offset // the last watched position in milliseconds, -1L for no records
val seasonId = bookmarksRecord.seasonId // the season ID of the content, if applicable
val episodeId = bookmarksRecord.episodeId // the episode ID of the content, if applicable
val timestamp = bookmarksRecord.timestamp // the timestamp of when the position was recorded, -1L if not applicable
}
is Result.Failure -> {
val error = result.value
logger.error { "getBookmark failed with error $error" }
TODO("Handle getBookmark call failure as required")
}
}
For an episodic content (series)
Use BookmarksService.getSeriesBookmarks
for retrieving the last watched position of an episodic piece of content.
The response returns a List
of BookmarksRecord
objects that each accurately identify the bookmark positions for each episode and season.
val seriesId = "098-765-4321" // unique identifier of the series content
val pageNumber = 1 // the page number of the bookmarks to be retrieved.
val pageSize = 10 // the page size of the bookmarks to be retrieved.
val sortByType = SortByType.TIMESTAMP // indicates to sort all the bookmarks by timestamp.
val sortOrderType = SortByType.DESC // indicates to sort all the bookmarks in descending order i.e. latest to oldest. can be reversed by passing SortByType.ASC.
val result = bookmarksService.getSeriesBookmarks(
seriesId,
pageNumber, //optional. Default value is 1.
pageSize, //optional. Default value is 10.
sortByType, //optional. Default value is SortByType.TIMESTAMP.
sortOrderType //optional. Default value is SortOrderType.DESC.
)
when (result) {
is Result.Success -> {
val bookmarksRecords = result.value
for (bookmarksRecord in bookmarksRecords) {
val contentId = bookmarksRecord.contentId // unique ID of the content
val bookmarkPosition = bookmarksRecord.offset // the last watched position in milliseconds, -1L for no records
val seasonId = bookmarksRecord.seasonId // the season ID of the content, if applicable
val episodeId = bookmarksRecord.episodeId // the episode ID of the content, if applicable
val timestamp = bookmarksRecord.timestamp // the timestamp of when the position was recorded, -1L if not applicable
}
}
is Result.Failure -> {
logger.error { "getSeriesBookmarks failed with error ${result.value}" }
TODO("Handle getSeriesBookmarks call failure as required")
}
}
For a specific user
Use BookmarksService.getBookmarks
API for retrieving all the incompletely watched content positions of a specific user.
The response returns a list of BookmarksRecord
objects that each accurately identify the content and the position.
val pageNumber = 1 // the page number of the bookmarks to be retrieved.
val pageSize = 10 // the page size of the bookmarks to be retrieved.
val sortByType = SortByType.TIMESTAMP // indicates to sort all the bookmarks by timestamp.
val sortOrderType = SortByType.DESC // indicates to sort all the bookmarks in descending order i.e. latest to oldest. can be reversed by passing SortByType.ASC.
val result = bookmarksService.getBookmarks(
pageNumber, //optional. Default value is 1.
pageSize, //optional. Default value is 10.
sortByType, //optional. Default value is SortByType.TIMESTAMP.
sortOrderType //optional. Default value is SortOrderType.DESC.
)
when (result) {
is Result.Success -> {
val bookmarksRecords = result.value
for (bookmarksRecord in bookmarksRecords) {
val contentId = bookmarksRecord.contentId // unique ID of the content
val bookmarkPosition = bookmarksRecord.offset // the last watched position in milliseconds, -1L for no records
val seasonId = bookmarksRecord.seasonId // the season ID of the content, if applicable
val episodeId = bookmarksRecord.episodeId // the episode ID of the content, if applicable
val timestamp = bookmarksRecord.timestamp // the timestamp of when the position was recorded, -1L if not applicable
}
}
is Result.Failure -> {
logger.error { "getBookmarks failed with error ${result.value}" }
TODO("Handle getBookmarks call failure as required")
}
}
Automatic enforcement
Maintaining the last played position of a content can be automatically enforced by the library using BookmarksManager
.
The application should create a BookmarksManager
which relies on the ComposablePlayer
provided by the application to
automatically update the last played position at appropriate intervals.
Create BookmarksConfiguration
BookmarksConfiguration
encapsulates all the necessary information required to automate bookmarks.
Property Name | Type | Default Value | Description |
---|---|---|---|
assetID | String | NA | The unique identifier of the content. |
bookmarksEndPointURL | String | NA | The endpoint of Bookmarks microservice. |
bookmarkSyncIntervalMs | Long | 10000L | The preferred time interval, in milliseconds, to periodically sync the bookmark position. The bookmark position is updated every 10 seconds by default. |
bookmarkDeleteThreshold | Float | 1.0F | The threshold factor used to calculate the current asset's playback position which determines if the asset's bookmark should be purged from the Bookmark Microservice or not. Valid value should be in the range (0, 1]. By default, Bookmark is removed only if the content is watched till the very end of the content's duration. |
seasonId | Int? | null | (Applicable only for episodic content) The unique identifier of the season of a content. |
episodeId | Int? | null | (Applicable only for episodic content) The unique identifier of the episode of a content. |
mode | Mode? | null | (Applicable only for episodic content) The unique identifier of the Mode of a content. |
val bookmarkEndPoint = "https://bookmarksEndpoint.url" //Bookmarks Microservice URL
val contentID = "123ABC-LC131-PLOJ45" // Unique identifier of the content
val bookmarksSyncIntervalMs = 15_000L // pass preferred value
val bookmarksDeleteThreshold = 0.98F // pass preferred value
val seasonId = "mySeasonId" // pass preferred value
val episodeId = "myEpisodeId" // pass preferred value
val mode = Mode.SERIES // pass preferred value - either Mode.SERIES or Mode.EPISODE
val bookmarksConfiguration = BookmarksManager.BookmarksConfiguration(
contentID,
bookmarkEndPoint,
bookmarksSyncIntervalMs, // Optional, default value is 10000L
bookmarksDeleteThreshold, // Optional, default value is 1.0F
seasonId, // Optional, default value is null
episodeId, // Optional, default value is null
mode // Optional, default value is null
)
Create BookmarksManager
Once a BookmarksManager
is created, it takes care of automating all the API calls required to maintain and delete the bookmark position of a content.
A new BookmarkManager
has to be created before the start of any new VOD playback to maintain accuracy.
BookmarksFactory.createBookmarksManager(
composablePlayer, // the relevant ComposablePlayer instance
platformAuthorizer, // the relevant PlatformAuthorizer instance
bookmarksConfiguration, // the relevant BookmarksConfiguration instance
httpClient // (Optional) the HTTP client to be used
)
A normal Player
can be converted into a ComposablePlayer
using the composablePlayerWith
function.
Once BookmarksManager
is created, it makes sure that the content position is updated periodically and also on applicable
player state changes. It also takes care of deleting the bookmark when the
content can be considered to be watched entirely (Use BookmarksConfiguration.bookmarkDeleteThreshold
to tweak this behaviour).
Manual enforcement
Maintaining the last played position of a content can be manually enforced by the application using BookmarksService
.
val contentID = "123ABC-LC131-PLOJ45" // Unique ID of the content
// Record the bookmark position of a content
val latestPositionMs = 99999L
val putBookmarkResult = bookmarksService.putBookmark(contentID, latestPositionMs)
when (putBookmarkResult) {
is Result.Success -> {
logger.info { "Latest bookmark at position $latestPositionMs recorded for content ID $contentID"}
}
is Result.Failure -> {
logger.error { "putBookmark failed with error ${result.value}" }
TODO("Handle putBookmark call failure, if required")
}
}
// Remove the recorded bookmark position of a content
val deleteBookmarkResult = bookmarksService.deleteBookmark(contentID)
when (deleteBookmarkResult) {
is Result.Success -> {
logger.info { "Bookmark deleted for content ID $contentID"}
}
is Result.Failure -> {
logger.error { "deleteBookmark failed with error ${result.value}" }
TODO("Handle deleteBookmark call failure, if required")
}
}