musora-content-services 1.0.148 → 1.0.152

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.d.ts CHANGED
@@ -12,24 +12,56 @@ 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
+ deletePlaylistItem,
28
+ deletePlaylistLike,
29
+ duplicatePlaylist,
15
30
  fetchAllCompletedStates,
31
+ fetchChallengeIndexMetadata,
16
32
  fetchChallengeLessonData,
17
33
  fetchChallengeMetadata,
18
34
  fetchCompletedContent,
19
35
  fetchCompletedState,
20
36
  fetchContentInProgress,
21
37
  fetchContentPageUserData,
38
+ fetchContentProgress,
22
39
  fetchHandler,
40
+ fetchPlaylist,
41
+ fetchPlaylistItem,
42
+ fetchPlaylistItems,
23
43
  fetchSongsInProgress,
24
44
  fetchUserAward,
25
45
  fetchUserChallengeProgress,
46
+ fetchUserLikes,
26
47
  fetchUserPermissions,
48
+ fetchUserPlaylists,
49
+ likePlaylist,
27
50
  postChallengesCommunityNotification,
28
51
  postChallengesEnroll,
29
52
  postChallengesEnrollmentNotification,
30
53
  postChallengesLeave,
31
54
  postChallengesSetStartDate,
32
- postChallengesUnlock
55
+ postChallengesUnlock,
56
+ postCompleteLesson,
57
+ postContentCompleted,
58
+ postContentLiked,
59
+ postContentReset,
60
+ postContentStarted,
61
+ postContentUnliked,
62
+ postRecordWatchSession,
63
+ updatePlaylist,
64
+ updatePlaylistItem
33
65
  } from './services/railcontent.js';
34
66
 
35
67
  import {
@@ -49,6 +81,7 @@ import {
49
81
  fetchCourseOverview,
50
82
  fetchFoundation,
51
83
  fetchGenreLessons,
84
+ fetchHierarchy,
52
85
  fetchLessonContent,
53
86
  fetchLiveEvent,
54
87
  fetchMetadata,
@@ -64,6 +97,7 @@ import {
64
97
  fetchPackChildren,
65
98
  fetchPackData,
66
99
  fetchParentByRailContentId,
100
+ fetchParentForDownload,
67
101
  fetchRelatedLessons,
68
102
  fetchRelatedMethodLessons,
69
103
  fetchRelatedSongs,
@@ -74,14 +108,22 @@ import {
74
108
  fetchSongById,
75
109
  fetchSongCount,
76
110
  fetchSongFilterOptions,
111
+ fetchTopLevelParentId,
77
112
  fetchUpcomingEvents,
78
113
  fetchWorkouts,
79
- getSortOrder,
80
- fetchParentForDownload,
114
+ getSortOrder
81
115
  } from './services/sanity.js';
82
116
 
83
117
  declare module 'musora-content-services' {
84
118
  export {
119
+ contentStatusCompleted,
120
+ contentStatusReset,
121
+ contentStatusStarted,
122
+ createPlaylist,
123
+ deletePlaylist,
124
+ deletePlaylistItem,
125
+ deletePlaylistLike,
126
+ duplicatePlaylist,
85
127
  fetchAll,
86
128
  fetchAllCompletedStates,
87
129
  fetchAllFilterOptions,
@@ -93,6 +135,7 @@ declare module 'musora-content-services' {
93
135
  fetchByRailContentIds,
94
136
  fetchByReference,
95
137
  fetchCatalogMetadata,
138
+ fetchChallengeIndexMetadata,
96
139
  fetchChallengeLessonData,
97
140
  fetchChallengeMetadata,
98
141
  fetchChallengeOverview,
@@ -102,10 +145,12 @@ declare module 'musora-content-services' {
102
145
  fetchCompletedState,
103
146
  fetchContentInProgress,
104
147
  fetchContentPageUserData,
148
+ fetchContentProgress,
105
149
  fetchCourseOverview,
106
150
  fetchFoundation,
107
151
  fetchGenreLessons,
108
152
  fetchHandler,
153
+ fetchHierarchy,
109
154
  fetchLessonContent,
110
155
  fetchLiveEvent,
111
156
  fetchMetadata,
@@ -121,6 +166,10 @@ declare module 'musora-content-services' {
121
166
  fetchPackChildren,
122
167
  fetchPackData,
123
168
  fetchParentByRailContentId,
169
+ fetchParentForDownload,
170
+ fetchPlaylist,
171
+ fetchPlaylistItem,
172
+ fetchPlaylistItems,
124
173
  fetchRelatedLessons,
125
174
  fetchRelatedMethodLessons,
126
175
  fetchRelatedSongs,
@@ -132,23 +181,39 @@ declare module 'musora-content-services' {
132
181
  fetchSongCount,
133
182
  fetchSongFilterOptions,
134
183
  fetchSongsInProgress,
184
+ fetchTopLevelParentId,
135
185
  fetchUpcomingEvents,
136
186
  fetchUserAward,
137
187
  fetchUserChallengeProgress,
188
+ fetchUserLikes,
138
189
  fetchUserPermissions,
190
+ fetchUserPlaylists,
139
191
  fetchWorkouts,
192
+ getProgressPercentage,
193
+ getProgressState,
194
+ getResumeTimeSeconds,
140
195
  getSortOrder,
141
196
  globalConfig,
142
197
  initializeService,
143
198
  isContentLiked,
144
199
  likeContent,
200
+ likePlaylist,
145
201
  postChallengesCommunityNotification,
146
202
  postChallengesEnroll,
147
203
  postChallengesEnrollmentNotification,
148
204
  postChallengesLeave,
149
205
  postChallengesSetStartDate,
150
206
  postChallengesUnlock,
207
+ postCompleteLesson,
208
+ postContentCompleted,
209
+ postContentLiked,
210
+ postContentReset,
211
+ postContentStarted,
212
+ postContentUnliked,
213
+ postRecordWatchSession,
214
+ recordWatchSession,
151
215
  unlikeContent,
152
- fetchParentForDownload,
216
+ updatePlaylist,
217
+ updatePlaylistItem,
153
218
  }
154
219
  }
package/src/index.js CHANGED
@@ -12,24 +12,56 @@ 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
+ deletePlaylistItem,
28
+ deletePlaylistLike,
29
+ duplicatePlaylist,
15
30
  fetchAllCompletedStates,
31
+ fetchChallengeIndexMetadata,
16
32
  fetchChallengeLessonData,
17
33
  fetchChallengeMetadata,
18
34
  fetchCompletedContent,
19
35
  fetchCompletedState,
20
36
  fetchContentInProgress,
21
37
  fetchContentPageUserData,
38
+ fetchContentProgress,
22
39
  fetchHandler,
40
+ fetchPlaylist,
41
+ fetchPlaylistItem,
42
+ fetchPlaylistItems,
23
43
  fetchSongsInProgress,
24
44
  fetchUserAward,
25
45
  fetchUserChallengeProgress,
46
+ fetchUserLikes,
26
47
  fetchUserPermissions,
48
+ fetchUserPlaylists,
49
+ likePlaylist,
27
50
  postChallengesCommunityNotification,
28
51
  postChallengesEnroll,
29
52
  postChallengesEnrollmentNotification,
30
53
  postChallengesLeave,
31
54
  postChallengesSetStartDate,
32
- postChallengesUnlock
55
+ postChallengesUnlock,
56
+ postCompleteLesson,
57
+ postContentCompleted,
58
+ postContentLiked,
59
+ postContentReset,
60
+ postContentStarted,
61
+ postContentUnliked,
62
+ postRecordWatchSession,
63
+ updatePlaylist,
64
+ updatePlaylistItem
33
65
  } from './services/railcontent.js';
34
66
 
35
67
  import {
@@ -49,6 +81,7 @@ import {
49
81
  fetchCourseOverview,
50
82
  fetchFoundation,
51
83
  fetchGenreLessons,
84
+ fetchHierarchy,
52
85
  fetchLessonContent,
53
86
  fetchLiveEvent,
54
87
  fetchMetadata,
@@ -64,6 +97,7 @@ import {
64
97
  fetchPackChildren,
65
98
  fetchPackData,
66
99
  fetchParentByRailContentId,
100
+ fetchParentForDownload,
67
101
  fetchRelatedLessons,
68
102
  fetchRelatedMethodLessons,
69
103
  fetchRelatedSongs,
@@ -74,13 +108,21 @@ import {
74
108
  fetchSongById,
75
109
  fetchSongCount,
76
110
  fetchSongFilterOptions,
111
+ fetchTopLevelParentId,
77
112
  fetchUpcomingEvents,
78
113
  fetchWorkouts,
79
- getSortOrder,
80
- fetchParentForDownload,
114
+ getSortOrder
81
115
  } from './services/sanity.js';
82
116
 
83
117
  export {
118
+ contentStatusCompleted,
119
+ contentStatusReset,
120
+ contentStatusStarted,
121
+ createPlaylist,
122
+ deletePlaylist,
123
+ deletePlaylistItem,
124
+ deletePlaylistLike,
125
+ duplicatePlaylist,
84
126
  fetchAll,
85
127
  fetchAllCompletedStates,
86
128
  fetchAllFilterOptions,
@@ -92,6 +134,7 @@ export {
92
134
  fetchByRailContentIds,
93
135
  fetchByReference,
94
136
  fetchCatalogMetadata,
137
+ fetchChallengeIndexMetadata,
95
138
  fetchChallengeLessonData,
96
139
  fetchChallengeMetadata,
97
140
  fetchChallengeOverview,
@@ -101,10 +144,12 @@ export {
101
144
  fetchCompletedState,
102
145
  fetchContentInProgress,
103
146
  fetchContentPageUserData,
147
+ fetchContentProgress,
104
148
  fetchCourseOverview,
105
149
  fetchFoundation,
106
150
  fetchGenreLessons,
107
151
  fetchHandler,
152
+ fetchHierarchy,
108
153
  fetchLessonContent,
109
154
  fetchLiveEvent,
110
155
  fetchMetadata,
@@ -120,6 +165,10 @@ export {
120
165
  fetchPackChildren,
121
166
  fetchPackData,
122
167
  fetchParentByRailContentId,
168
+ fetchParentForDownload,
169
+ fetchPlaylist,
170
+ fetchPlaylistItem,
171
+ fetchPlaylistItems,
123
172
  fetchRelatedLessons,
124
173
  fetchRelatedMethodLessons,
125
174
  fetchRelatedSongs,
@@ -131,22 +180,38 @@ export {
131
180
  fetchSongCount,
132
181
  fetchSongFilterOptions,
133
182
  fetchSongsInProgress,
183
+ fetchTopLevelParentId,
134
184
  fetchUpcomingEvents,
135
185
  fetchUserAward,
136
186
  fetchUserChallengeProgress,
187
+ fetchUserLikes,
137
188
  fetchUserPermissions,
189
+ fetchUserPlaylists,
138
190
  fetchWorkouts,
191
+ getProgressPercentage,
192
+ getProgressState,
193
+ getResumeTimeSeconds,
139
194
  getSortOrder,
140
195
  globalConfig,
141
196
  initializeService,
142
197
  isContentLiked,
143
198
  likeContent,
199
+ likePlaylist,
144
200
  postChallengesCommunityNotification,
145
201
  postChallengesEnroll,
146
202
  postChallengesEnrollmentNotification,
147
203
  postChallengesLeave,
148
204
  postChallengesSetStartDate,
149
205
  postChallengesUnlock,
206
+ postCompleteLesson,
207
+ postContentCompleted,
208
+ postContentLiked,
209
+ postContentReset,
210
+ postContentStarted,
211
+ postContentUnliked,
212
+ postRecordWatchSession,
213
+ recordWatchSession,
150
214
  unlikeContent,
151
- fetchParentForDownload,
215
+ updatePlaylist,
216
+ updatePlaylistItem,
152
217
  };
File without changes
@@ -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);