musora-content-services 1.0.147 → 1.0.150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -12,6 +12,20 @@ import {
12
12
  } from './services/contentLikes.js';
13
13
 
14
14
  import {
15
+ contentStatusCompleted,
16
+ contentStatusReset,
17
+ contentStatusStarted,
18
+ getProgressPercentage,
19
+ getProgressState,
20
+ getResumeTimeSeconds,
21
+ recordWatchSession
22
+ } from './services/contentProgress.js';
23
+
24
+ import {
25
+ createPlaylist,
26
+ deletePlaylist,
27
+ deletePlaylistLike,
28
+ duplicatePlaylist,
15
29
  fetchAllCompletedStates,
16
30
  fetchChallengeLessonData,
17
31
  fetchChallengeMetadata,
@@ -19,17 +33,31 @@ import {
19
33
  fetchCompletedState,
20
34
  fetchContentInProgress,
21
35
  fetchContentPageUserData,
36
+ fetchContentProgress,
22
37
  fetchHandler,
38
+ fetchPlaylist,
39
+ fetchPlaylistItems,
23
40
  fetchSongsInProgress,
24
41
  fetchUserAward,
25
42
  fetchUserChallengeProgress,
43
+ fetchUserLikes,
26
44
  fetchUserPermissions,
45
+ fetchUserPlaylists,
46
+ likePlaylist,
27
47
  postChallengesCommunityNotification,
28
48
  postChallengesEnroll,
29
49
  postChallengesEnrollmentNotification,
30
50
  postChallengesLeave,
31
51
  postChallengesSetStartDate,
32
- postChallengesUnlock
52
+ postChallengesUnlock,
53
+ postContentCompleted,
54
+ postContentLiked,
55
+ postContentReset,
56
+ postContentStarted,
57
+ postContentUnliked,
58
+ postRecordWatchSession,
59
+ updatePlaylist,
60
+ updatePlaylistItem
33
61
  } from './services/railcontent.js';
34
62
 
35
63
  import {
@@ -49,6 +77,7 @@ import {
49
77
  fetchCourseOverview,
50
78
  fetchFoundation,
51
79
  fetchGenreLessons,
80
+ fetchHierarchy,
52
81
  fetchLessonContent,
53
82
  fetchLiveEvent,
54
83
  fetchMetadata,
@@ -64,6 +93,7 @@ import {
64
93
  fetchPackChildren,
65
94
  fetchPackData,
66
95
  fetchParentByRailContentId,
96
+ fetchParentForDownload,
67
97
  fetchRelatedLessons,
68
98
  fetchRelatedMethodLessons,
69
99
  fetchRelatedSongs,
@@ -74,13 +104,20 @@ import {
74
104
  fetchSongById,
75
105
  fetchSongCount,
76
106
  fetchSongFilterOptions,
107
+ fetchTopLevelParentId,
77
108
  fetchUpcomingEvents,
78
109
  fetchWorkouts,
79
- getSortOrder,
80
- fetchParentForDownload,
110
+ getSortOrder
81
111
  } from './services/sanity.js';
82
112
 
83
113
  export {
114
+ contentStatusCompleted,
115
+ contentStatusReset,
116
+ contentStatusStarted,
117
+ createPlaylist,
118
+ deletePlaylist,
119
+ deletePlaylistLike,
120
+ duplicatePlaylist,
84
121
  fetchAll,
85
122
  fetchAllCompletedStates,
86
123
  fetchAllFilterOptions,
@@ -101,10 +138,12 @@ export {
101
138
  fetchCompletedState,
102
139
  fetchContentInProgress,
103
140
  fetchContentPageUserData,
141
+ fetchContentProgress,
104
142
  fetchCourseOverview,
105
143
  fetchFoundation,
106
144
  fetchGenreLessons,
107
145
  fetchHandler,
146
+ fetchHierarchy,
108
147
  fetchLessonContent,
109
148
  fetchLiveEvent,
110
149
  fetchMetadata,
@@ -120,6 +159,9 @@ export {
120
159
  fetchPackChildren,
121
160
  fetchPackData,
122
161
  fetchParentByRailContentId,
162
+ fetchParentForDownload,
163
+ fetchPlaylist,
164
+ fetchPlaylistItems,
123
165
  fetchRelatedLessons,
124
166
  fetchRelatedMethodLessons,
125
167
  fetchRelatedSongs,
@@ -131,22 +173,37 @@ export {
131
173
  fetchSongCount,
132
174
  fetchSongFilterOptions,
133
175
  fetchSongsInProgress,
176
+ fetchTopLevelParentId,
134
177
  fetchUpcomingEvents,
135
178
  fetchUserAward,
136
179
  fetchUserChallengeProgress,
180
+ fetchUserLikes,
137
181
  fetchUserPermissions,
182
+ fetchUserPlaylists,
138
183
  fetchWorkouts,
184
+ getProgressPercentage,
185
+ getProgressState,
186
+ getResumeTimeSeconds,
139
187
  getSortOrder,
140
188
  globalConfig,
141
189
  initializeService,
142
190
  isContentLiked,
143
191
  likeContent,
192
+ likePlaylist,
144
193
  postChallengesCommunityNotification,
145
194
  postChallengesEnroll,
146
195
  postChallengesEnrollmentNotification,
147
196
  postChallengesLeave,
148
197
  postChallengesSetStartDate,
149
198
  postChallengesUnlock,
199
+ postContentCompleted,
200
+ postContentLiked,
201
+ postContentReset,
202
+ postContentStarted,
203
+ postContentUnliked,
204
+ postRecordWatchSession,
205
+ recordWatchSession,
150
206
  unlikeContent,
151
- fetchParentForDownload,
207
+ updatePlaylist,
208
+ updatePlaylistItem,
152
209
  };
@@ -1,5 +1,5 @@
1
1
  import {fetchUserLikes, postContentLiked, postContentUnliked} from "./railcontent";
2
- import {DataContext, ContentVersionKey} from "./dataContext";
2
+ import {DataContext, ContentLikesVersionKey} from "./dataContext";
3
3
 
4
4
  /**
5
5
  * Exported functions that are excluded from index generation.
@@ -8,7 +8,7 @@ import {DataContext, ContentVersionKey} from "./dataContext";
8
8
  */
9
9
  const excludeFromGeneratedIndex = [];
10
10
 
11
- export let dataContext = new DataContext(ContentVersionKey, fetchUserLikes);
11
+ export let dataContext = new DataContext(ContentLikesVersionKey, fetchUserLikes);
12
12
 
13
13
  export async function isContentLiked(contentId) {
14
14
  contentId = parseInt(contentId);
@@ -19,12 +19,12 @@ export async function isContentLiked(contentId) {
19
19
  export async function likeContent(contentId) {
20
20
  contentId = parseInt(contentId);
21
21
  await dataContext.update(
22
- function (context) {
23
- if (!context.data.includes(contentId)) {
24
- context.data.push(contentId);
22
+ function (localContext) {
23
+ if (!localContext.data.includes(contentId)) {
24
+ localContext.data.push(contentId);
25
25
  }
26
26
  },
27
- async function(){
27
+ async function () {
28
28
  return postContentLiked(contentId);
29
29
  }
30
30
  );
@@ -33,15 +33,15 @@ export async function likeContent(contentId) {
33
33
  export async function unlikeContent(contentId) {
34
34
  contentId = parseInt(contentId);
35
35
  await dataContext.update(
36
- function (context) {
37
- if (context.data.includes(contentId)) {
38
- const index = context.data.indexOf(contentId);
36
+ function (localContext) {
37
+ if (localContext.data.includes(contentId)) {
38
+ const index = localContext.data.indexOf(contentId);
39
39
  if (index > -1) { // only splice array when item is found
40
- context.data.splice(index, 1); // 2nd parameter means remove one item only
40
+ localContext.data.splice(index, 1); // 2nd parameter means remove one item only
41
41
  }
42
42
  }
43
43
  },
44
- async function(){
44
+ async function () {
45
45
  return postContentUnliked(contentId);
46
46
  }
47
47
  );
@@ -0,0 +1,153 @@
1
+ import {
2
+ fetchContentProgress,
3
+ postContentCompleted,
4
+ postContentReset,
5
+ postContentStarted,
6
+ postRecordWatchSession
7
+ } from "./railcontent";
8
+ import {DataContext, ContentProgressVersionKey} from "./dataContext";
9
+ import {fetchHierarchy, fetchParentByRailContentId} from "./sanity";
10
+
11
+ const STATE_STARTED = 'started';
12
+ const STATE_COMPLETED = 'completed';
13
+ const DATA_KEY_STATUS = 's';
14
+ const DATA_KEY_PROGRESS = 'p';
15
+ const DATA_KEY_RESUME_TIME = 't';
16
+ export let dataContext = new DataContext(ContentProgressVersionKey, fetchContentProgress());
17
+
18
+ export async function getProgressPercentage(contentId) {
19
+ let data = await dataContext.getData();
20
+ return data[contentId]?.[DATA_KEY_PROGRESS] ?? 0;
21
+ }
22
+
23
+ export async function getProgressState(contentId) {
24
+ let data = await dataContext.getData();
25
+ return data[contentId]?.[DATA_KEY_STATUS] ?? 0;
26
+ }
27
+
28
+ export async function getResumeTimeSeconds(contentId) {
29
+ let data = await dataContext.getData();
30
+ return data[contentId]?.[DATA_KEY_RESUME_TIME] ?? 0;
31
+ }
32
+
33
+ export async function contentStatusStarted(contentId) {
34
+ await dataContext.update(
35
+ function (localContext) {
36
+ let data = localContext.data[contentId] ?? [];
37
+ let progress = data?.[DATA_KEY_PROGRESS] ?? 0;
38
+ let status = data?.[DATA_KEY_STATUS] ?? 0;
39
+
40
+ if (status !== STATE_COMPLETED && progress !== 100) {
41
+ status = STATE_STARTED;
42
+ }
43
+
44
+ data[DATA_KEY_STATUS] = status;
45
+ localContext.data[contentId] = data;
46
+ },
47
+ async function () {
48
+ return postContentStarted(contentId);
49
+ });
50
+ }
51
+
52
+
53
+ export async function contentStatusCompleted(contentId) {
54
+ await dataContext.update(
55
+ function (localContext) {
56
+ let hierarchy = fetchHierarchy(contentId);
57
+ completeStatusInLocalContext(contentId, localContext, hierarchy);
58
+ },
59
+ async function () {
60
+ return postContentCompleted(contentId);
61
+ });
62
+ }
63
+
64
+ function completeStatusInLocalContext(contentId, localContext, hierarchy) {
65
+ let data = localContext.data[contentId] ?? [];
66
+ data[DATA_KEY_STATUS] = STATE_COMPLETED;
67
+ data[DATA_KEY_PROGRESS] = 100;
68
+ localContext.data[contentId] = data;
69
+
70
+ let children = hierarchy.children[contentId] ?? [];
71
+ for(let i = 0; i < children.length; i++) {
72
+ let childId = children[i];
73
+ completeStatusInLocalContext(childId, localContext, hierarchy);
74
+ }
75
+ }
76
+
77
+ export async function contentStatusReset(contentId) {
78
+ await dataContext.update(
79
+ function (localContext) {
80
+ const index = localContext.data.indexOf(contentId);
81
+ if (index > -1) { // only splice array when item is found
82
+ localContext.data.splice(index, 1); // 2nd parameter means remove one item only
83
+ }
84
+ },
85
+ async function () {
86
+ return postContentReset(contentId);
87
+ });
88
+ }
89
+
90
+
91
+ export async function recordWatchSession({
92
+ mediaId,
93
+ mediaType,
94
+ mediaCategory,
95
+ watchPositionSeconds,
96
+ totalDurationSeconds,
97
+ sessionToken,
98
+ brand,
99
+ contentId = null
100
+ }) {
101
+ await dataContext.update(
102
+ async function (localContext) {
103
+ if (contentId) {
104
+ let data = localContext.data[contentId] ?? [];
105
+ let progress = data?.[DATA_KEY_PROGRESS] ?? 0;
106
+ let status = data?.[DATA_KEY_STATUS] ?? 0;
107
+
108
+ if (status !== STATE_COMPLETED && progress !== 100) {
109
+ status = STATE_STARTED;
110
+ progress = Math.min(99, Math.round(watchPositionSeconds ?? 0 / Math.max(1, totalDurationSeconds ?? 0) * 100));
111
+ }
112
+
113
+ data[DATA_KEY_PROGRESS] = progress;
114
+ data[DATA_KEY_STATUS] = status;
115
+ data[DATA_KEY_RESUME_TIME] = watchPositionSeconds;
116
+ localContext.data[contentId] = data;
117
+
118
+ let hierarchy = await fetchHierarchy(contentId);
119
+ bubbleProgress(hierarchy, contentId, localContext);
120
+ }
121
+ },
122
+ async function () {
123
+ return postRecordWatchSession({
124
+ mediaId,
125
+ mediaType,
126
+ mediaCategory,
127
+ watchPositionSeconds,
128
+ totalDurationSeconds,
129
+ sessionToken,
130
+ brand,
131
+ contentId
132
+ });
133
+ }
134
+ );
135
+ }
136
+
137
+ function bubbleProgress(hierarchy, contentId, localContext) {
138
+ let parentId = hierarchy.parents[contentId];
139
+ if (!parentId) return;
140
+ let data = localContext.data[parentId] ?? [];
141
+ let progress = data[DATA_KEY_PROGRESS];
142
+ let status = data[DATA_KEY_STATUS];
143
+ if (status !== STATE_COMPLETED && progress !== 100) {
144
+ let childProgress = hierarchy.children[parentId].map(function (childId) {
145
+ return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0;
146
+ });
147
+ progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length);
148
+ data[DATA_KEY_PROGRESS] = progress;
149
+ localContext.data[parentId] = data;
150
+ }
151
+ bubbleProgress(hierarchy, parentId, localContext);
152
+ }
153
+
@@ -8,7 +8,8 @@ import {globalConfig} from "./config";
8
8
  const excludeFromGeneratedIndex = [];
9
9
 
10
10
  //These constants need to match MWP UserDataVersionKeyEnum enum
11
- export const ContentVersionKey = 0;
11
+ export const ContentLikesVersionKey = 0;
12
+ export const ContentProgressVersionKey = 1;
12
13
 
13
14
  let cache = null;
14
15
 
@@ -78,7 +79,7 @@ export class DataContext {
78
79
  async update(localUpdateFunction, serverUpdateFunction) {
79
80
  await this.ensureLocalContextLoaded();
80
81
  if (this.context) {
81
- localUpdateFunction(this.context);
82
+ await localUpdateFunction(this.context);
82
83
  this.context.version++;
83
84
  let data = JSON.stringify(this.context);
84
85
  cache.setItem(this.localStorageKey, data);