@internetarchive/radio-player 0.0.4-alpha.1 → 0.1.1-alpha.1

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.
Files changed (69) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +142 -142
  3. package/index.d.ts +15 -15
  4. package/index.js +28 -28
  5. package/lib/src/assets/img/left.d.ts +2 -2
  6. package/lib/src/assets/img/left.js +15 -15
  7. package/lib/src/assets/img/right.d.ts +2 -2
  8. package/lib/src/assets/img/right.js +15 -15
  9. package/lib/src/models/music-zone.d.ts +12 -12
  10. package/lib/src/models/music-zone.js +15 -15
  11. package/lib/src/models/radio-player-config.d.ts +26 -26
  12. package/lib/src/models/radio-player-config.js +27 -27
  13. package/lib/src/radio-player.d.ts +589 -589
  14. package/lib/src/radio-player.js +1337 -1338
  15. package/lib/src/radio-player.js.map +1 -1
  16. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-backend.d.ts +82 -82
  17. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-backend.js +107 -107
  18. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-delegate.d.ts +9 -0
  19. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-delegate.js +1 -0
  20. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-delegate.js.map +1 -0
  21. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-response.d.ts +48 -48
  22. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-response.js +58 -58
  23. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-service-interface.d.ts +16 -16
  24. package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-service-interface.js +1 -1
  25. package/lib/src/search-handler/search-backends/local-search-backend/local-search-backend.d.ts +27 -27
  26. package/lib/src/search-handler/search-backends/local-search-backend/local-search-backend.js +42 -42
  27. package/lib/src/search-handler/search-backends/search-backend-interface.d.ts +9 -9
  28. package/lib/src/search-handler/search-backends/search-backend-interface.js +1 -1
  29. package/lib/src/search-handler/search-handler-interface.d.ts +17 -17
  30. package/lib/src/search-handler/search-handler-interface.js +1 -1
  31. package/lib/src/search-handler/search-handler.d.ts +83 -83
  32. package/lib/src/search-handler/search-handler.js +177 -177
  33. package/lib/src/search-handler/search-helper.d.ts +12 -12
  34. package/lib/src/search-handler/search-helper.js +22 -22
  35. package/lib/src/search-handler/search-index.d.ts +53 -0
  36. package/lib/src/search-handler/search-index.js +118 -0
  37. package/lib/src/search-handler/search-index.js.map +1 -0
  38. package/lib/src/search-handler/search-models.d.ts +45 -45
  39. package/lib/src/search-handler/search-models.js +49 -49
  40. package/lib/src/search-handler/transcript-index-interface.d.ts +38 -38
  41. package/lib/src/search-handler/transcript-index-interface.js +1 -1
  42. package/lib/src/search-handler/transcript-index.d.ts +53 -53
  43. package/lib/src/search-handler/transcript-index.js +80 -80
  44. package/lib/src/search-results-switcher.d.ts +58 -58
  45. package/lib/src/search-results-switcher.js +133 -133
  46. package/package.json +93 -93
  47. package/src/assets/img/left.ts +17 -17
  48. package/src/assets/img/right.ts +17 -17
  49. package/src/models/music-zone.ts +17 -17
  50. package/src/models/radio-player-config.ts +47 -47
  51. package/src/radio-player.ts +1398 -1402
  52. package/src/search-handler/search-backends/full-text-search-backend/full-text-search-backend.ts +140 -140
  53. package/src/search-handler/search-backends/full-text-search-backend/full-text-search-response.ts +110 -110
  54. package/src/search-handler/search-backends/full-text-search-backend/full-text-search-service-interface.ts +17 -17
  55. package/src/search-handler/search-backends/local-search-backend/local-search-backend.ts +50 -50
  56. package/src/search-handler/search-backends/search-backend-interface.ts +10 -10
  57. package/src/search-handler/search-handler-interface.ts +18 -18
  58. package/src/search-handler/search-handler.ts +217 -217
  59. package/src/search-handler/search-helper.ts +25 -25
  60. package/src/search-handler/search-models.ts +69 -69
  61. package/src/search-handler/transcript-index-interface.ts +42 -42
  62. package/src/search-handler/transcript-index.ts +93 -93
  63. package/src/search-results-switcher.ts +131 -131
  64. package/lib/demo/full-text-search-service.d.ts +0 -14
  65. package/lib/demo/full-text-search-service.js +0 -34
  66. package/lib/demo/full-text-search-service.js.map +0 -1
  67. package/lib/demo/radio-player-controller.d.ts +0 -41
  68. package/lib/demo/radio-player-controller.js +0 -200
  69. package/lib/demo/radio-player-controller.js.map +0 -1
@@ -1,178 +1,178 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { TranscriptConfig, TranscriptEntryConfig } from '@internetarchive/transcript-view';
11
- import { Range, SearchResult } from './search-models';
12
- import { SearchHelper } from './search-helper';
13
- /**
14
- * This is the main entrypoint into transcript searching. It has a single
15
- * public method as defined by `SearchHandlerInterface`: `search(term: string)`,
16
- * that returns a `Promise<TranscriptConfig>` with a search-modified transcript.
17
- * This means the original transcript entries get merged with search results since
18
- * search results can span across transcript entries.
19
- *
20
- * It offloads the searching work to a `SearchBackendInterface` object that
21
- * is responsible for returning the search result `Range` objects based on some
22
- * backend search like the `LocalSearchBackend` or `FullTextSearchBackend`.
23
- *
24
- * It also uses a `TranscriptIndexInterface` object that is responsible for indexing
25
- * the transcipt to make reassembly much more efficient.
26
- */
27
- export class SearchHandler {
28
- constructor(searchBackend, transcriptIndex) {
29
- this.searchBackend = searchBackend;
30
- this.transcriptIndex = transcriptIndex;
31
- }
32
- /**
33
- * This is the main method in here. It takes a search term and returns a TranscriptConfig
34
- * that has been modified to insert the search results.
35
- *
36
- * @param {string} query
37
- * @returns {TranscriptConfig}
38
- * @memberof SearchHandler
39
- */
40
- search(query) {
41
- return __awaiter(this, void 0, void 0, function* () {
42
- const searchSeparatedTranscript = yield this.getSearchSeparatedTranscript(query);
43
- const newTranscriptEntries = [];
44
- let searchResultIndex = 0;
45
- let entryIdentifier = 1;
46
- searchSeparatedTranscript.forEach((entry) => {
47
- // If we encounter a match, just create a new transcript entry from it and append it.
48
- // We don't care if it crosses over multiple transcript entries since we want one match,
49
- // not multiple broken up by transcript entry.
50
- if (entry.isSearchMatch) {
51
- // find the closest source transcript to this entry
52
- const startEntry = this.transcriptIndex.getTranscriptEntryAt(entry.range.startIndex);
53
- if (!startEntry) {
54
- return;
55
- }
56
- const { range } = entry;
57
- const { endIndex } = range;
58
- const endEntry = this.transcriptIndex.getTranscriptEntryAt(endIndex) || startEntry;
59
- const newTranscriptEntry = this.createBlankTranscriptEntryConfig(startEntry.entry);
60
- newTranscriptEntry.searchMatchIndex = searchResultIndex;
61
- searchResultIndex += 1;
62
- newTranscriptEntry.rawText = entry.text;
63
- newTranscriptEntry.id = entryIdentifier;
64
- entryIdentifier += 1;
65
- newTranscriptEntry.end = endEntry.entry.end;
66
- newTranscriptEntries.push(newTranscriptEntry);
67
- return;
68
- }
69
- // Next loop through all of the source transcript entries to find the ones that intersect
70
- // with this search result. If it intersects, we take the intersected characters from the
71
- // merged transcript and make a new entry from that.
72
- this.transcriptIndex.transcriptEntryRanges.forEach((indexMap) => {
73
- const intersection = SearchHelper.getIntersection(entry.range, indexMap.range);
74
- if (!intersection || intersection.length === 0) {
75
- return;
76
- }
77
- const newTranscriptEntry = this.createBlankTranscriptEntryConfig(indexMap.entry);
78
- const text = this.transcriptIndex.mergedTranscript.substring(intersection.startIndex, intersection.endIndex);
79
- newTranscriptEntry.rawText = text.trim();
80
- newTranscriptEntry.id = entryIdentifier;
81
- entryIdentifier += 1;
82
- newTranscriptEntries.push(newTranscriptEntry);
83
- });
84
- });
85
- const newTranscript = new TranscriptConfig(newTranscriptEntries);
86
- return new Promise(resolve => resolve(newTranscript));
87
- });
88
- }
89
- /**
90
- * Copy a transcript entry but leave the text and search result index empty.
91
- *
92
- * @private
93
- * @param {TranscriptEntryConfig} sourceTranscriptConfig
94
- * @returns {TranscriptEntryConfig}
95
- * @memberof SearchHandler
96
- */
97
- /* eslint-disable-next-line class-methods-use-this */
98
- createBlankTranscriptEntryConfig(sourceTranscriptConfig) {
99
- return new TranscriptEntryConfig(sourceTranscriptConfig.id, sourceTranscriptConfig.start, sourceTranscriptConfig.end, '', sourceTranscriptConfig.isMusic);
100
- }
101
- /**
102
- * Search the full transcript and split up by search results and non-results. For instance,
103
- * if the full transcript is `foo bar baz boop bump snap pop` and you search for `bump`,
104
- * you'll get an array of 3 results back:
105
- * 1. `foo bar baz boop `
106
- * 2. `bump` <-- the match
107
- * 3. ` snap pop`
108
- *
109
- * This is helpful when rebuilding the transcript later to be able to identify search results.
110
- *
111
- * @private
112
- * @param {string} query
113
- * @returns {SearchResult[]}
114
- * @memberof SearchHandler
115
- */
116
- getSearchSeparatedTranscript(query) {
117
- return __awaiter(this, void 0, void 0, function* () {
118
- const searchRanges = yield this.searchBackend.getSearchRanges(query);
119
- const { mergedTranscript } = this.transcriptIndex;
120
- // if there's no search results, just return a single SearchResult that is the full
121
- // transcript marked as not a match.
122
- if (searchRanges.length === 0) {
123
- const range = new Range(0, mergedTranscript.length);
124
- return new Promise(resolve => resolve([new SearchResult(range, mergedTranscript, false)]));
125
- }
126
- // Sort the search range results from start to end
127
- // The search engine may return results in any order but while processing the results here,
128
- // we are going from start to finish. If the search results are out of order, it rebuilds
129
- // incorrect blocks of search results.
130
- searchRanges.sort((a, b) => (a.startIndex - b.startIndex));
131
- const transcriptEntries = [];
132
- let startIndex = 0;
133
- // Loop through all of the search result ranges and construct an array
134
- // of SearchResult entries.
135
- // For instance, if the full transcript is `foo bar baz boop bump snip snap`
136
- // and you search for `baz`, we'll only have a single search range: Range(8, 11).
137
- // We then take the text leading up to that range and store it as the "non result"
138
- // so we now have Range(0, 7) and Range(8, 11).
139
- // If there were more search results, we would continue from index 12 to build
140
- // the next range to the next match.
141
- // Finally once all the matches are finished, tack on the remaining text to the end.
142
- searchRanges.forEach((searchRange) => {
143
- // first find the "non-result" range, which is the current `startIndex` up to
144
- // `range.startIndex` (the start of the search result)
145
- const nonResultRange = new Range(startIndex, searchRange.startIndex);
146
- const nonResultEntry = this.getSearchResult(nonResultRange, false);
147
- transcriptEntries.push(nonResultEntry);
148
- // get the search result match from the merged transcript
149
- // using the current search range
150
- const searchResultEntry = this.getSearchResult(searchRange, true);
151
- transcriptEntries.push(searchResultEntry);
152
- startIndex = searchRange.endIndex;
153
- });
154
- // add any remaining characters as the last final entry in the results
155
- const finalResultRange = new Range(startIndex, mergedTranscript.length);
156
- const finalResultEntry = this.getSearchResult(finalResultRange, false);
157
- transcriptEntries.push(finalResultEntry);
158
- return new Promise(resolve => resolve(transcriptEntries));
159
- });
160
- }
161
- /**
162
- * Generate a SearchResult entry from a given Range and annotate it
163
- * with whether or not it is a search result match
164
- *
165
- * @private
166
- * @param {Range} range
167
- * @param {boolean} isSearchResult
168
- * @returns {SearchResult}
169
- * @memberof SearchHandler
170
- */
171
- getSearchResult(range, isSearchResult) {
172
- const { mergedTranscript } = this.transcriptIndex;
173
- const text = mergedTranscript.substring(range.startIndex, range.endIndex);
174
- const searchResult = new SearchResult(range, text, isSearchResult);
175
- return searchResult;
176
- }
177
- }
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { TranscriptConfig, TranscriptEntryConfig } from '@internetarchive/transcript-view';
11
+ import { Range, SearchResult } from './search-models';
12
+ import { SearchHelper } from './search-helper';
13
+ /**
14
+ * This is the main entrypoint into transcript searching. It has a single
15
+ * public method as defined by `SearchHandlerInterface`: `search(term: string)`,
16
+ * that returns a `Promise<TranscriptConfig>` with a search-modified transcript.
17
+ * This means the original transcript entries get merged with search results since
18
+ * search results can span across transcript entries.
19
+ *
20
+ * It offloads the searching work to a `SearchBackendInterface` object that
21
+ * is responsible for returning the search result `Range` objects based on some
22
+ * backend search like the `LocalSearchBackend` or `FullTextSearchBackend`.
23
+ *
24
+ * It also uses a `TranscriptIndexInterface` object that is responsible for indexing
25
+ * the transcipt to make reassembly much more efficient.
26
+ */
27
+ export class SearchHandler {
28
+ constructor(searchBackend, transcriptIndex) {
29
+ this.searchBackend = searchBackend;
30
+ this.transcriptIndex = transcriptIndex;
31
+ }
32
+ /**
33
+ * This is the main method in here. It takes a search term and returns a TranscriptConfig
34
+ * that has been modified to insert the search results.
35
+ *
36
+ * @param {string} query
37
+ * @returns {TranscriptConfig}
38
+ * @memberof SearchHandler
39
+ */
40
+ search(query) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ const searchSeparatedTranscript = yield this.getSearchSeparatedTranscript(query);
43
+ const newTranscriptEntries = [];
44
+ let searchResultIndex = 0;
45
+ let entryIdentifier = 1;
46
+ searchSeparatedTranscript.forEach((entry) => {
47
+ // If we encounter a match, just create a new transcript entry from it and append it.
48
+ // We don't care if it crosses over multiple transcript entries since we want one match,
49
+ // not multiple broken up by transcript entry.
50
+ if (entry.isSearchMatch) {
51
+ // find the closest source transcript to this entry
52
+ const startEntry = this.transcriptIndex.getTranscriptEntryAt(entry.range.startIndex);
53
+ if (!startEntry) {
54
+ return;
55
+ }
56
+ const { range } = entry;
57
+ const { endIndex } = range;
58
+ const endEntry = this.transcriptIndex.getTranscriptEntryAt(endIndex) || startEntry;
59
+ const newTranscriptEntry = this.createBlankTranscriptEntryConfig(startEntry.entry);
60
+ newTranscriptEntry.searchMatchIndex = searchResultIndex;
61
+ searchResultIndex += 1;
62
+ newTranscriptEntry.rawText = entry.text;
63
+ newTranscriptEntry.id = entryIdentifier;
64
+ entryIdentifier += 1;
65
+ newTranscriptEntry.end = endEntry.entry.end;
66
+ newTranscriptEntries.push(newTranscriptEntry);
67
+ return;
68
+ }
69
+ // Next loop through all of the source transcript entries to find the ones that intersect
70
+ // with this search result. If it intersects, we take the intersected characters from the
71
+ // merged transcript and make a new entry from that.
72
+ this.transcriptIndex.transcriptEntryRanges.forEach((indexMap) => {
73
+ const intersection = SearchHelper.getIntersection(entry.range, indexMap.range);
74
+ if (!intersection || intersection.length === 0) {
75
+ return;
76
+ }
77
+ const newTranscriptEntry = this.createBlankTranscriptEntryConfig(indexMap.entry);
78
+ const text = this.transcriptIndex.mergedTranscript.substring(intersection.startIndex, intersection.endIndex);
79
+ newTranscriptEntry.rawText = text.trim();
80
+ newTranscriptEntry.id = entryIdentifier;
81
+ entryIdentifier += 1;
82
+ newTranscriptEntries.push(newTranscriptEntry);
83
+ });
84
+ });
85
+ const newTranscript = new TranscriptConfig(newTranscriptEntries);
86
+ return new Promise(resolve => resolve(newTranscript));
87
+ });
88
+ }
89
+ /**
90
+ * Copy a transcript entry but leave the text and search result index empty.
91
+ *
92
+ * @private
93
+ * @param {TranscriptEntryConfig} sourceTranscriptConfig
94
+ * @returns {TranscriptEntryConfig}
95
+ * @memberof SearchHandler
96
+ */
97
+ /* eslint-disable-next-line class-methods-use-this */
98
+ createBlankTranscriptEntryConfig(sourceTranscriptConfig) {
99
+ return new TranscriptEntryConfig(sourceTranscriptConfig.id, sourceTranscriptConfig.start, sourceTranscriptConfig.end, '', sourceTranscriptConfig.isMusic);
100
+ }
101
+ /**
102
+ * Search the full transcript and split up by search results and non-results. For instance,
103
+ * if the full transcript is `foo bar baz boop bump snap pop` and you search for `bump`,
104
+ * you'll get an array of 3 results back:
105
+ * 1. `foo bar baz boop `
106
+ * 2. `bump` <-- the match
107
+ * 3. ` snap pop`
108
+ *
109
+ * This is helpful when rebuilding the transcript later to be able to identify search results.
110
+ *
111
+ * @private
112
+ * @param {string} query
113
+ * @returns {SearchResult[]}
114
+ * @memberof SearchHandler
115
+ */
116
+ getSearchSeparatedTranscript(query) {
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ const searchRanges = yield this.searchBackend.getSearchRanges(query);
119
+ const { mergedTranscript } = this.transcriptIndex;
120
+ // if there's no search results, just return a single SearchResult that is the full
121
+ // transcript marked as not a match.
122
+ if (searchRanges.length === 0) {
123
+ const range = new Range(0, mergedTranscript.length);
124
+ return new Promise(resolve => resolve([new SearchResult(range, mergedTranscript, false)]));
125
+ }
126
+ // Sort the search range results from start to end
127
+ // The search engine may return results in any order but while processing the results here,
128
+ // we are going from start to finish. If the search results are out of order, it rebuilds
129
+ // incorrect blocks of search results.
130
+ searchRanges.sort((a, b) => (a.startIndex - b.startIndex));
131
+ const transcriptEntries = [];
132
+ let startIndex = 0;
133
+ // Loop through all of the search result ranges and construct an array
134
+ // of SearchResult entries.
135
+ // For instance, if the full transcript is `foo bar baz boop bump snip snap`
136
+ // and you search for `baz`, we'll only have a single search range: Range(8, 11).
137
+ // We then take the text leading up to that range and store it as the "non result"
138
+ // so we now have Range(0, 7) and Range(8, 11).
139
+ // If there were more search results, we would continue from index 12 to build
140
+ // the next range to the next match.
141
+ // Finally once all the matches are finished, tack on the remaining text to the end.
142
+ searchRanges.forEach((searchRange) => {
143
+ // first find the "non-result" range, which is the current `startIndex` up to
144
+ // `range.startIndex` (the start of the search result)
145
+ const nonResultRange = new Range(startIndex, searchRange.startIndex);
146
+ const nonResultEntry = this.getSearchResult(nonResultRange, false);
147
+ transcriptEntries.push(nonResultEntry);
148
+ // get the search result match from the merged transcript
149
+ // using the current search range
150
+ const searchResultEntry = this.getSearchResult(searchRange, true);
151
+ transcriptEntries.push(searchResultEntry);
152
+ startIndex = searchRange.endIndex;
153
+ });
154
+ // add any remaining characters as the last final entry in the results
155
+ const finalResultRange = new Range(startIndex, mergedTranscript.length);
156
+ const finalResultEntry = this.getSearchResult(finalResultRange, false);
157
+ transcriptEntries.push(finalResultEntry);
158
+ return new Promise(resolve => resolve(transcriptEntries));
159
+ });
160
+ }
161
+ /**
162
+ * Generate a SearchResult entry from a given Range and annotate it
163
+ * with whether or not it is a search result match
164
+ *
165
+ * @private
166
+ * @param {Range} range
167
+ * @param {boolean} isSearchResult
168
+ * @returns {SearchResult}
169
+ * @memberof SearchHandler
170
+ */
171
+ getSearchResult(range, isSearchResult) {
172
+ const { mergedTranscript } = this.transcriptIndex;
173
+ const text = mergedTranscript.substring(range.startIndex, range.endIndex);
174
+ const searchResult = new SearchResult(range, text, isSearchResult);
175
+ return searchResult;
176
+ }
177
+ }
178
178
  //# sourceMappingURL=search-handler.js.map
@@ -1,12 +1,12 @@
1
- import { Range } from './search-models';
2
- export declare class SearchHelper {
3
- /**
4
- * Get the intersection of two ranges and return a range or undefined if they do not intersect
5
- *
6
- * @param range1
7
- * @param range2
8
- *
9
- * @returns Range|undefined
10
- */
11
- static getIntersection(range1: Range, range2: Range): Range | undefined;
12
- }
1
+ import { Range } from './search-models';
2
+ export declare class SearchHelper {
3
+ /**
4
+ * Get the intersection of two ranges and return a range or undefined if they do not intersect
5
+ *
6
+ * @param range1
7
+ * @param range2
8
+ *
9
+ * @returns Range|undefined
10
+ */
11
+ static getIntersection(range1: Range, range2: Range): Range | undefined;
12
+ }
@@ -1,23 +1,23 @@
1
- import { Range } from './search-models';
2
- export class SearchHelper {
3
- /**
4
- * Get the intersection of two ranges and return a range or undefined if they do not intersect
5
- *
6
- * @param range1
7
- * @param range2
8
- *
9
- * @returns Range|undefined
10
- */
11
- static getIntersection(range1, range2) {
12
- // get the range with the smaller starting point (min) and greater start (max)
13
- const minRange = range1.startIndex < range2.startIndex ? range1 : range2;
14
- const maxRange = minRange === range1 ? range2 : range1;
15
- // min ends before max starts -> no intersection
16
- if (minRange.endIndex < maxRange.startIndex) {
17
- return undefined; // the ranges don't intersect
18
- }
19
- const endIndex = minRange.endIndex < maxRange.endIndex ? minRange.endIndex : maxRange.endIndex;
20
- return new Range(maxRange.startIndex, endIndex);
21
- }
22
- }
1
+ import { Range } from './search-models';
2
+ export class SearchHelper {
3
+ /**
4
+ * Get the intersection of two ranges and return a range or undefined if they do not intersect
5
+ *
6
+ * @param range1
7
+ * @param range2
8
+ *
9
+ * @returns Range|undefined
10
+ */
11
+ static getIntersection(range1, range2) {
12
+ // get the range with the smaller starting point (min) and greater start (max)
13
+ const minRange = range1.startIndex < range2.startIndex ? range1 : range2;
14
+ const maxRange = minRange === range1 ? range2 : range1;
15
+ // min ends before max starts -> no intersection
16
+ if (minRange.endIndex < maxRange.startIndex) {
17
+ return undefined; // the ranges don't intersect
18
+ }
19
+ const endIndex = minRange.endIndex < maxRange.endIndex ? minRange.endIndex : maxRange.endIndex;
20
+ return new Range(maxRange.startIndex, endIndex);
21
+ }
22
+ }
23
23
  //# sourceMappingURL=search-helper.js.map
@@ -0,0 +1,53 @@
1
+ import { TranscriptConfig } from '@internetarchive/transcript-view';
2
+ import { Range, TranscriptEntryRange } from './search-models';
3
+ export declare class SearchIndex {
4
+ /**
5
+ * Make these properties readonly. We don't want outside classes touching the private storage.
6
+ *
7
+ * @readonly
8
+ * @type {string}
9
+ * @memberof SearchIndex
10
+ */
11
+ get mergedTranscript(): string;
12
+ get transcriptEntryRanges(): TranscriptEntryRange[];
13
+ /**
14
+ * This gets populated as part of the search index build. It merges all of the transcript
15
+ * entries together so we can search it as a single document instead of a bunch of
16
+ * individual entries. This allows searches to cross over transcript entries.
17
+ *
18
+ * It also indexes the entries so we can quickly look them up later.
19
+ *
20
+ * @private
21
+ * @memberof SearchIndex
22
+ */
23
+ private searchIndexCache;
24
+ /**
25
+ * Find the closes TranscriptEntryRange to the given overallCharIndex
26
+ *
27
+ * @param overallCharIndex
28
+ * @returns {TranscriptEntryRange | undefined}
29
+ */
30
+ getTranscriptEntryAt(overallCharIndex: number): TranscriptEntryRange | undefined;
31
+ /**
32
+ * Finds all of the ranges of all the search results across the entire transcript.
33
+ *
34
+ * @private
35
+ * @param {string} term
36
+ * @returns {Range[]}
37
+ * @memberof SearchIndex
38
+ */
39
+ getSearchRanges(term: string): Range[];
40
+ constructor(transcriptConfig: TranscriptConfig);
41
+ /**
42
+ * Build the search index. This method does two things:
43
+ * 1. Builds the "merged transcript" with all of the text combined into one long transcript.
44
+ * This allows us to search the entire thing at once instead of entry by entry.
45
+ * 2. For each entry, get the start and end index of that particular entry within the overall
46
+ * transcript. This allows us to reconstruct the transcript later from the original transcript
47
+ * and the search results.
48
+ *
49
+ * @private
50
+ * @memberof SearchHandler
51
+ */
52
+ private buildIndex;
53
+ }
@@ -0,0 +1,118 @@
1
+ import { Range, TranscriptEntryRange } from './search-models';
2
+ class SearchIndexCache {
3
+ constructor(mergedTranscript = '', transcriptEntryRanges = []) {
4
+ /**
5
+ * This gets populated as part of the search index build. It merges all of the transcript
6
+ * entries together so we can search it as a single document instead of a bunch of
7
+ * individual entries. This allows searches to cross over transcript entries.
8
+ *
9
+ * NOTE: When the mergedTranscript gets created, spaces are put between each transcript
10
+ * entry, otherwise the words would run into each other. We account for this in the
11
+ * indices above.
12
+ *
13
+ * @type {string}
14
+ * @memberof SearchIndexCache
15
+ */
16
+ this.mergedTranscript = '';
17
+ this.mergedTranscriptLowercased = '';
18
+ /**
19
+ * This gets populated as part of the search index build. It maps the start and end indicies
20
+ * of all of the transcript entries so we can quickly look up where an entry is in the
21
+ * overall transcript.
22
+ *
23
+ * @type {TranscriptEntryRange[]}
24
+ * @memberof SearchIndexCache
25
+ */
26
+ this.transcriptEntryRanges = [];
27
+ this.mergedTranscript = mergedTranscript;
28
+ this.transcriptEntryRanges = transcriptEntryRanges;
29
+ this.mergedTranscriptLowercased = mergedTranscript.toLowerCase();
30
+ }
31
+ }
32
+ export class SearchIndex {
33
+ constructor(transcriptConfig) {
34
+ /**
35
+ * This gets populated as part of the search index build. It merges all of the transcript
36
+ * entries together so we can search it as a single document instead of a bunch of
37
+ * individual entries. This allows searches to cross over transcript entries.
38
+ *
39
+ * It also indexes the entries so we can quickly look them up later.
40
+ *
41
+ * @private
42
+ * @memberof SearchIndex
43
+ */
44
+ this.searchIndexCache = new SearchIndexCache();
45
+ this.buildIndex(transcriptConfig);
46
+ }
47
+ /**
48
+ * Make these properties readonly. We don't want outside classes touching the private storage.
49
+ *
50
+ * @readonly
51
+ * @type {string}
52
+ * @memberof SearchIndex
53
+ */
54
+ get mergedTranscript() {
55
+ return this.searchIndexCache.mergedTranscript;
56
+ }
57
+ get transcriptEntryRanges() {
58
+ return this.searchIndexCache.transcriptEntryRanges;
59
+ }
60
+ /**
61
+ * Find the closes TranscriptEntryRange to the given overallCharIndex
62
+ *
63
+ * @param overallCharIndex
64
+ * @returns {TranscriptEntryRange | undefined}
65
+ */
66
+ getTranscriptEntryAt(overallCharIndex) {
67
+ return this.searchIndexCache.transcriptEntryRanges.find(entry => {
68
+ const { range } = entry;
69
+ return range.endIndex > overallCharIndex && range.startIndex <= overallCharIndex;
70
+ });
71
+ }
72
+ /**
73
+ * Finds all of the ranges of all the search results across the entire transcript.
74
+ *
75
+ * @private
76
+ * @param {string} term
77
+ * @returns {Range[]}
78
+ * @memberof SearchIndex
79
+ */
80
+ getSearchRanges(term) {
81
+ const ranges = [];
82
+ const termLowerCased = term.toLowerCase();
83
+ let index = -1;
84
+ /* eslint-disable-next-line no-cond-assign */
85
+ while ((index = this.searchIndexCache.mergedTranscriptLowercased.indexOf(termLowerCased, index + 1)) !== -1) {
86
+ const newRange = new Range(index, index + termLowerCased.length);
87
+ ranges.push(newRange);
88
+ }
89
+ return ranges;
90
+ }
91
+ /**
92
+ * Build the search index. This method does two things:
93
+ * 1. Builds the "merged transcript" with all of the text combined into one long transcript.
94
+ * This allows us to search the entire thing at once instead of entry by entry.
95
+ * 2. For each entry, get the start and end index of that particular entry within the overall
96
+ * transcript. This allows us to reconstruct the transcript later from the original transcript
97
+ * and the search results.
98
+ *
99
+ * @private
100
+ * @memberof SearchHandler
101
+ */
102
+ buildIndex(transcriptConfig) {
103
+ let startIndex = 0;
104
+ const transcriptEntryRanges = [];
105
+ let mergedTranscript = '';
106
+ transcriptConfig.entries.forEach((entry) => {
107
+ const { displayText } = entry;
108
+ const indexMapRange = new Range(startIndex, startIndex + displayText.length);
109
+ const indexMap = new TranscriptEntryRange(entry, indexMapRange);
110
+ transcriptEntryRanges.push(indexMap);
111
+ mergedTranscript += `${entry.displayText} `;
112
+ startIndex = mergedTranscript.length;
113
+ });
114
+ mergedTranscript = mergedTranscript.trim();
115
+ this.searchIndexCache = new SearchIndexCache(mergedTranscript, transcriptEntryRanges);
116
+ }
117
+ }
118
+ //# sourceMappingURL=search-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-index.js","sourceRoot":"","sources":["../../../src/search-handler/search-index.ts"],"names":[],"mappings":"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"}