Skip to main content

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.

info

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 NameTypeDefault ValueDescription
assetIDStringNAThe unique identifier of the content.
bookmarksEndPointURLStringNAThe endpoint of Bookmarks microservice.
bookmarkSyncIntervalMsLong10000LThe preferred time interval, in milliseconds, to periodically sync the bookmark position. The bookmark position is updated every 10 seconds by default.
bookmarkDeleteThresholdFloat1.0FThe 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.
seasonIdInt?null(Applicable only for episodic content) The unique identifier of the season of a content.
episodeIdInt?null(Applicable only for episodic content) The unique identifier of the episode of a content.
modeMode?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
)
note

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")
}
}