@internetarchive/radio-player 0.0.3 → 0.0.4-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.
- package/LICENSE +661 -661
- package/README.md +142 -142
- package/index.d.ts +15 -15
- package/index.js +28 -28
- package/lib/demo/full-text-search-service.d.ts +14 -14
- package/lib/demo/full-text-search-service.js +33 -33
- package/lib/demo/radio-player-controller.d.ts +41 -41
- package/lib/demo/radio-player-controller.js +199 -199
- package/lib/src/assets/img/left.d.ts +2 -2
- package/lib/src/assets/img/left.js +15 -15
- package/lib/src/assets/img/right.d.ts +2 -2
- package/lib/src/assets/img/right.js +15 -15
- package/lib/src/models/music-zone.d.ts +12 -12
- package/lib/src/models/music-zone.js +15 -15
- package/lib/src/models/radio-player-config.d.ts +26 -26
- package/lib/src/models/radio-player-config.js +27 -27
- package/lib/src/radio-player.d.ts +589 -589
- package/lib/src/radio-player.js +1338 -1338
- package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-backend.d.ts +82 -82
- package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-backend.js +107 -107
- package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-response.d.ts +48 -48
- package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-response.js +58 -58
- package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-service-interface.d.ts +16 -16
- package/lib/src/search-handler/search-backends/full-text-search-backend/full-text-search-service-interface.js +1 -1
- package/lib/src/search-handler/search-backends/local-search-backend/local-search-backend.d.ts +27 -27
- package/lib/src/search-handler/search-backends/local-search-backend/local-search-backend.js +42 -42
- package/lib/src/search-handler/search-backends/search-backend-interface.d.ts +9 -9
- package/lib/src/search-handler/search-backends/search-backend-interface.js +1 -1
- package/lib/src/search-handler/search-handler-interface.d.ts +17 -17
- package/lib/src/search-handler/search-handler-interface.js +1 -1
- package/lib/src/search-handler/search-handler.d.ts +83 -83
- package/lib/src/search-handler/search-handler.js +177 -177
- package/lib/src/search-handler/search-helper.d.ts +12 -12
- package/lib/src/search-handler/search-helper.js +22 -22
- package/lib/src/search-handler/search-models.d.ts +45 -45
- package/lib/src/search-handler/search-models.js +49 -49
- package/lib/src/search-handler/transcript-index-interface.d.ts +38 -38
- package/lib/src/search-handler/transcript-index-interface.js +1 -1
- package/lib/src/search-handler/transcript-index.d.ts +53 -53
- package/lib/src/search-handler/transcript-index.js +80 -80
- package/lib/src/search-results-switcher.d.ts +58 -58
- package/lib/src/search-results-switcher.js +133 -133
- package/package.json +93 -93
- package/src/assets/img/left.ts +17 -17
- package/src/assets/img/right.ts +17 -17
- package/src/models/music-zone.ts +17 -17
- package/src/models/radio-player-config.ts +47 -47
- package/src/radio-player.ts +1402 -1402
- package/src/search-handler/search-backends/full-text-search-backend/full-text-search-backend.ts +140 -140
- package/src/search-handler/search-backends/full-text-search-backend/full-text-search-response.ts +110 -110
- package/src/search-handler/search-backends/full-text-search-backend/full-text-search-service-interface.ts +17 -17
- package/src/search-handler/search-backends/local-search-backend/local-search-backend.ts +50 -50
- package/src/search-handler/search-backends/search-backend-interface.ts +10 -10
- package/src/search-handler/search-handler-interface.ts +18 -18
- package/src/search-handler/search-handler.ts +217 -217
- package/src/search-handler/search-helper.ts +25 -25
- package/src/search-handler/search-models.ts +69 -69
- package/src/search-handler/transcript-index-interface.ts +42 -42
- package/src/search-handler/transcript-index.ts +93 -93
- package/src/search-results-switcher.ts +131 -131
- package/.eslintrc +0 -37
- package/.gitignore +0 -11
- package/.storybook/.babelrc +0 -15
- package/.storybook/addons.js +0 -7
- package/.storybook/config.js +0 -11
- package/.storybook/webpack.config.js +0 -5
- package/demo/full-text-search-service.ts +0 -32
- package/demo/index.html +0 -53
- package/demo/radio-player-controller.ts +0 -237
- package/karma.bs.config.js +0 -16
- package/karma.conf.js +0 -66
- package/package-lock.json +0 -51719
- package/stories/index.stories.js +0 -40
- package/stories/transcript.js +0 -683
- package/test/assets/arrow.mp3 +0 -0
- package/test/music-zone.test.js +0 -13
- package/test/promised-sleep.js +0 -3
- package/test/radio-player-config.test.js +0 -42
- package/test/radio-player.test.js +0 -1072
- package/test/sample_transcript.js +0 -1493
- package/test/search-handler/search-backends/full-text-sample-response-brackets.js +0 -60
- package/test/search-handler/search-backends/full-text-sample-response-em.js +0 -60
- package/test/search-handler/search-backends/full-text-search-backend.test.js +0 -74
- package/test/search-handler/search-backends/full-text-search-response.test.js +0 -14
- package/test/search-handler/search-backends/local-search-backend.test.js +0 -39
- package/test/search-handler/search-handler.test.js +0 -550
- package/test/search-handler/search-helper.test.js +0 -63
- package/test/search-handler/search-model.test.js +0 -56
- package/test/search-handler/transcript-index.test.js +0 -66
- package/test/search-results-switcher.test.js +0 -97
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -21
- package/yarn.lock +0 -14450
|
@@ -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
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { TranscriptEntryConfig } from '@internetarchive/transcript-view';
|
|
2
|
-
/**
|
|
3
|
-
* A model to store the start and end indices for a given context.
|
|
4
|
-
* This is used for augmenting the source transcript entries (`TranscriptEntryRange`)
|
|
5
|
-
* as well as the search result entries (`SearchResult`) below.
|
|
6
|
-
*
|
|
7
|
-
* @class Range
|
|
8
|
-
*/
|
|
9
|
-
declare class Range {
|
|
10
|
-
startIndex: number;
|
|
11
|
-
endIndex: number;
|
|
12
|
-
get length(): number;
|
|
13
|
-
constructor(startIndex: number, endIndex: number);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* This class augments the transcript entry with the range of the characters
|
|
17
|
-
* in the merged transcript. This is useful for more rapidly splitting up and
|
|
18
|
-
* restoring the transcript entries from the transforms that happen during the search.
|
|
19
|
-
*
|
|
20
|
-
* @class TranscriptIndexMap
|
|
21
|
-
*/
|
|
22
|
-
declare class TranscriptEntryRange {
|
|
23
|
-
entry: TranscriptEntryConfig;
|
|
24
|
-
range: Range;
|
|
25
|
-
constructor(entry: TranscriptEntryConfig, range: Range);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* The first step in converting the search results back to a usable transcript
|
|
29
|
-
* is to break up the overall merged transcript into search results and non-search-results
|
|
30
|
-
* sections. This will allow us to more easily break it up into its original transcript
|
|
31
|
-
* chunks in a subsequent process.
|
|
32
|
-
*
|
|
33
|
-
* This class is a container to hold those chunks. They each have a start and end index
|
|
34
|
-
* from their spot in the merged transcript, the text from that chunk, and whether that
|
|
35
|
-
* chunk was a search match or just regular text.
|
|
36
|
-
*
|
|
37
|
-
* @class SearchSeparatedTranscriptEntry
|
|
38
|
-
*/
|
|
39
|
-
declare class SearchResult {
|
|
40
|
-
range: Range;
|
|
41
|
-
text: string;
|
|
42
|
-
isSearchMatch: boolean;
|
|
43
|
-
constructor(range: Range, text: string, isSearchMatch: boolean);
|
|
44
|
-
}
|
|
45
|
-
export { Range, TranscriptEntryRange, SearchResult };
|
|
1
|
+
import { TranscriptEntryConfig } from '@internetarchive/transcript-view';
|
|
2
|
+
/**
|
|
3
|
+
* A model to store the start and end indices for a given context.
|
|
4
|
+
* This is used for augmenting the source transcript entries (`TranscriptEntryRange`)
|
|
5
|
+
* as well as the search result entries (`SearchResult`) below.
|
|
6
|
+
*
|
|
7
|
+
* @class Range
|
|
8
|
+
*/
|
|
9
|
+
declare class Range {
|
|
10
|
+
startIndex: number;
|
|
11
|
+
endIndex: number;
|
|
12
|
+
get length(): number;
|
|
13
|
+
constructor(startIndex: number, endIndex: number);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* This class augments the transcript entry with the range of the characters
|
|
17
|
+
* in the merged transcript. This is useful for more rapidly splitting up and
|
|
18
|
+
* restoring the transcript entries from the transforms that happen during the search.
|
|
19
|
+
*
|
|
20
|
+
* @class TranscriptIndexMap
|
|
21
|
+
*/
|
|
22
|
+
declare class TranscriptEntryRange {
|
|
23
|
+
entry: TranscriptEntryConfig;
|
|
24
|
+
range: Range;
|
|
25
|
+
constructor(entry: TranscriptEntryConfig, range: Range);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The first step in converting the search results back to a usable transcript
|
|
29
|
+
* is to break up the overall merged transcript into search results and non-search-results
|
|
30
|
+
* sections. This will allow us to more easily break it up into its original transcript
|
|
31
|
+
* chunks in a subsequent process.
|
|
32
|
+
*
|
|
33
|
+
* This class is a container to hold those chunks. They each have a start and end index
|
|
34
|
+
* from their spot in the merged transcript, the text from that chunk, and whether that
|
|
35
|
+
* chunk was a search match or just regular text.
|
|
36
|
+
*
|
|
37
|
+
* @class SearchSeparatedTranscriptEntry
|
|
38
|
+
*/
|
|
39
|
+
declare class SearchResult {
|
|
40
|
+
range: Range;
|
|
41
|
+
text: string;
|
|
42
|
+
isSearchMatch: boolean;
|
|
43
|
+
constructor(range: Range, text: string, isSearchMatch: boolean);
|
|
44
|
+
}
|
|
45
|
+
export { Range, TranscriptEntryRange, SearchResult };
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A model to store the start and end indices for a given context.
|
|
3
|
-
* This is used for augmenting the source transcript entries (`TranscriptEntryRange`)
|
|
4
|
-
* as well as the search result entries (`SearchResult`) below.
|
|
5
|
-
*
|
|
6
|
-
* @class Range
|
|
7
|
-
*/
|
|
8
|
-
class Range {
|
|
9
|
-
constructor(startIndex, endIndex) {
|
|
10
|
-
this.startIndex = startIndex;
|
|
11
|
-
this.endIndex = endIndex;
|
|
12
|
-
}
|
|
13
|
-
get length() {
|
|
14
|
-
return Math.abs(this.endIndex - this.startIndex);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* This class augments the transcript entry with the range of the characters
|
|
19
|
-
* in the merged transcript. This is useful for more rapidly splitting up and
|
|
20
|
-
* restoring the transcript entries from the transforms that happen during the search.
|
|
21
|
-
*
|
|
22
|
-
* @class TranscriptIndexMap
|
|
23
|
-
*/
|
|
24
|
-
class TranscriptEntryRange {
|
|
25
|
-
constructor(entry, range) {
|
|
26
|
-
this.entry = entry;
|
|
27
|
-
this.range = range;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* The first step in converting the search results back to a usable transcript
|
|
32
|
-
* is to break up the overall merged transcript into search results and non-search-results
|
|
33
|
-
* sections. This will allow us to more easily break it up into its original transcript
|
|
34
|
-
* chunks in a subsequent process.
|
|
35
|
-
*
|
|
36
|
-
* This class is a container to hold those chunks. They each have a start and end index
|
|
37
|
-
* from their spot in the merged transcript, the text from that chunk, and whether that
|
|
38
|
-
* chunk was a search match or just regular text.
|
|
39
|
-
*
|
|
40
|
-
* @class SearchSeparatedTranscriptEntry
|
|
41
|
-
*/
|
|
42
|
-
class SearchResult {
|
|
43
|
-
constructor(range, text, isSearchMatch) {
|
|
44
|
-
this.range = range;
|
|
45
|
-
this.text = text;
|
|
46
|
-
this.isSearchMatch = isSearchMatch;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
export { Range, TranscriptEntryRange, SearchResult };
|
|
1
|
+
/**
|
|
2
|
+
* A model to store the start and end indices for a given context.
|
|
3
|
+
* This is used for augmenting the source transcript entries (`TranscriptEntryRange`)
|
|
4
|
+
* as well as the search result entries (`SearchResult`) below.
|
|
5
|
+
*
|
|
6
|
+
* @class Range
|
|
7
|
+
*/
|
|
8
|
+
class Range {
|
|
9
|
+
constructor(startIndex, endIndex) {
|
|
10
|
+
this.startIndex = startIndex;
|
|
11
|
+
this.endIndex = endIndex;
|
|
12
|
+
}
|
|
13
|
+
get length() {
|
|
14
|
+
return Math.abs(this.endIndex - this.startIndex);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* This class augments the transcript entry with the range of the characters
|
|
19
|
+
* in the merged transcript. This is useful for more rapidly splitting up and
|
|
20
|
+
* restoring the transcript entries from the transforms that happen during the search.
|
|
21
|
+
*
|
|
22
|
+
* @class TranscriptIndexMap
|
|
23
|
+
*/
|
|
24
|
+
class TranscriptEntryRange {
|
|
25
|
+
constructor(entry, range) {
|
|
26
|
+
this.entry = entry;
|
|
27
|
+
this.range = range;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The first step in converting the search results back to a usable transcript
|
|
32
|
+
* is to break up the overall merged transcript into search results and non-search-results
|
|
33
|
+
* sections. This will allow us to more easily break it up into its original transcript
|
|
34
|
+
* chunks in a subsequent process.
|
|
35
|
+
*
|
|
36
|
+
* This class is a container to hold those chunks. They each have a start and end index
|
|
37
|
+
* from their spot in the merged transcript, the text from that chunk, and whether that
|
|
38
|
+
* chunk was a search match or just regular text.
|
|
39
|
+
*
|
|
40
|
+
* @class SearchSeparatedTranscriptEntry
|
|
41
|
+
*/
|
|
42
|
+
class SearchResult {
|
|
43
|
+
constructor(range, text, isSearchMatch) {
|
|
44
|
+
this.range = range;
|
|
45
|
+
this.text = text;
|
|
46
|
+
this.isSearchMatch = isSearchMatch;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export { Range, TranscriptEntryRange, SearchResult };
|
|
50
50
|
//# sourceMappingURL=search-models.js.map
|