source:
main/trunk/model-interfaces-dev/atea/korero-maori-asr/src/js/TranscribeModule.js@
35456
Last change on this file since 35456 was 35454, checked in by , 3 years ago | |
---|---|
File size: 9.5 KB |
Line | |
---|---|
1 | /** |
2 | * @file Defines components that are used to interact with the transcription proxy servlet. |
3 | * @author Carl Stephens |
4 | * @module |
5 | */ |
6 | |
7 | /* eslint-disable camelcase */ |
8 | |
9 | export class TranscriptionMetadata { |
10 | /** |
11 | * A metadata object held by a {@link TranscriptionModel}. |
12 | * |
13 | * @param {String} char The character. |
14 | * @param {Number} confidence The confidence that the character is correct. Smaller values are more confident. |
15 | * @param {Number} start_time The timestamp in the audio at which this character is spoken. |
16 | */ |
17 | constructor(char, confidence, start_time) { |
18 | this.char = char; |
19 | this.confidence = confidence; |
20 | this.start_time = start_time; |
21 | } |
22 | } |
23 | |
24 | export class TranscriptionModel { |
25 | /** |
26 | * The transcription object returned from our transcription endpoint. |
27 | * |
28 | * @param {String} file_name The name of the file that was transcribed. |
29 | * @param {String} log A note of how the transcription was processed. |
30 | * @param {TranscriptionMetadata[]} metadata The character metadata. |
31 | * @param {boolean} success A value indicating if the transcription was successful or not. |
32 | * @param {String} transcription The transcription. |
33 | */ |
34 | constructor(file_name, log, metadata, success, transcription) { |
35 | this.file_name = file_name; |
36 | this.log = log; |
37 | this.metadata = metadata; |
38 | this.success = success; |
39 | this.transcription = transcription; |
40 | } |
41 | } |
42 | |
43 | /* eslint-enable camelcase */ |
44 | |
45 | export class TranscriptionError extends Error { |
46 | /** |
47 | * Initialises a new instance of the {@link TranscriptionError} object. |
48 | * |
49 | * @param {Number | undefined} statusCode The HTTP status code of the error. |
50 | * @param {String | undefined} message The status message. |
51 | * @param {String | null} fileName The file on which the transcription failed. |
52 | */ |
53 | constructor(message = undefined, fileName = null, statusCode = -1) { |
54 | super(message); |
55 | |
56 | /** @type {String | null} The name of the file that the transcription error occured on. */ |
57 | this.fileName = fileName; |
58 | |
59 | /** @type {Number | undefined} The status code returned by the API when the erro was generated. */ |
60 | this.statusCode = statusCode; |
61 | } |
62 | } |
63 | |
64 | /** |
65 | * A service that uploads audio files in a multipart request to the Korero Maori API Interface servlet, and interprets the result. |
66 | */ |
67 | export default class TranscribeService { |
68 | constructor() { |
69 | /* eslint-disable quotes */ |
70 | this.TEST_JSON = '{"file_name":"akl_mi_pk_0002.wav","log":"Took 0.6s to transcribe 8.7s audio file /tmp/transcribe-aw7ujpu8-clean.wav","metadata":[{"char":"k","prob":7.0E-6,"start_time":0.24},{"char":"o","prob":8.3E-5,"start_time":0.26},{"char":"t","prob":0.995578,"start_time":0.42},{"char":"a","prob":0.997089,"start_time":0.44},{"char":"h","prob":3.0E-6,"start_time":0.54},{"char":"i","prob":8.0E-6,"start_time":0.56},{"char":" ","prob":2.2E-5,"start_time":0.6},{"char":"a","prob":0.982987,"start_time":0.64},{"char":"n","prob":5.0E-6,"start_time":0.8},{"char":"Å","prob":1.82E-4,"start_time":0.84},{"char":" ","prob":0.996944,"start_time":1.08},{"char":"t","prob":1.23E-4,"start_time":1.14},{"char":"e","prob":6.3E-5,"start_time":1.16},{"char":" ","prob":0.587595,"start_time":1.28},{"char":"t","prob":4.91E-4,"start_time":1.34},{"char":"u","prob":0.010654,"start_time":1.36},{"char":"p","prob":5.9E-5,"start_time":1.52},{"char":"u","prob":1.26E-4,"start_time":1.54},{"char":"n","prob":0.0,"start_time":1.7},{"char":"a","prob":0.99997,"start_time":1.72},{"char":" ","prob":0.993724,"start_time":2.14},{"char":"o","prob":4.3E-5,"start_time":2.22},{"char":" ","prob":0.99696,"start_time":2.32},{"char":"t","prob":1.4E-5,"start_time":2.36},{"char":"e","prob":3.8E-5,"start_time":2.38},{"char":" ","prob":0.996606,"start_time":2.48},{"char":"t","prob":0.0,"start_time":2.52},{"char":"a","prob":0.994873,"start_time":2.54},{"char":"Å","prob":2.9E-5,"start_time":2.64},{"char":"a","prob":0.994639,"start_time":2.66},{"char":"t","prob":3.0E-6,"start_time":2.8},{"char":"a","prob":0.99995,"start_time":2.82},{"char":" ","prob":0.0,"start_time":2.9},{"char":"m","prob":0.999606,"start_time":2.98},{"char":"Ä","prob":0.010628,"start_time":3.0},{"char":"o","prob":0.996415,"start_time":3.2},{"char":"r","prob":0.0,"start_time":3.28},{"char":"i","prob":0.999698,"start_time":3.32},{"char":" ","prob":0.997927,"start_time":4.5},{"char":"k","prob":0.0,"start_time":4.58},{"char":"o","prob":3.2E-5,"start_time":4.6},{"char":" ","prob":0.956949,"start_time":4.7},{"char":"r","prob":2.5E-5,"start_time":4.74},{"char":"a","prob":0.997649,"start_time":4.76},{"char":"Å","prob":0.0,"start_time":4.9},{"char":"i","prob":1.63E-4,"start_time":4.92},{"char":"n","prob":1.3E-5,"start_time":5.28},{"char":"u","prob":4.0E-6,"start_time":5.3},{"char":"i","prob":0.99997,"start_time":5.42},{"char":" ","prob":0.994305,"start_time":5.78},{"char":"e","prob":3.1E-5,"start_time":5.84},{"char":" ","prob":0.966097,"start_time":6.04},{"char":"t","prob":2.0E-6,"start_time":6.1},{"char":"Å«","prob":5.4E-5,"start_time":6.12},{"char":" ","prob":0.837368,"start_time":6.34},{"char":"n","prob":0.0,"start_time":6.38},{"char":"e","prob":0.004238,"start_time":6.4},{"char":"i","prob":0.99966,"start_time":6.5},{"char":" ","prob":0.99728,"start_time":7.28},{"char":"o","prob":6.28E-4,"start_time":7.36},{"char":" ","prob":0.961049,"start_time":7.48},{"char":"p","prob":5.0E-6,"start_time":7.52},{"char":"a","prob":0.984707,"start_time":7.54},{"char":"p","prob":9.0E-6,"start_time":7.66},{"char":"a","prob":0.998754,"start_time":7.68},{"char":"t","prob":0.002356,"start_time":7.88},{"char":"Å«","prob":1.24E-4,"start_time":7.9},{"char":"Ä","prob":0.883958,"start_time":8.0},{"char":"n","prob":0.985871,"start_time":8.24},{"char":"u","prob":0.907369,"start_time":8.26},{"char":"k","prob":0.999045,"start_time":8.38},{"char":"u","prob":0.995632,"start_time":8.4}],"success":true,"transcription":"kotahi anÅ te tupuna o te tangata mÄori ko ranginui e tÅ« nei o papatÅ«Änuku"}'; |
71 | /* eslint-enable quotes */ |
72 | |
73 | /** @type {String} The URL to which query POST requests should be made. */ |
74 | this.queryUrl = "//localhost:8383/gs3-koreromaori/transcribe"; |
75 | |
76 | /** @type {Number} The maximum number of files which can be transcribed in one request to the API. */ |
77 | this.MAX_BATCH_COUNT = 3; |
78 | |
79 | /** @type {Number} The soft upper limit on how many bytes can be submitted in one request. */ |
80 | this.BATCH_BYTE_LIMIT = 5242880; // 5 MiB |
81 | } |
82 | |
83 | /** |
84 | * Performs chunked queries to transcribe the given audio files, returning the data in iterations. |
85 | * Data is chunked according to which ever occurs first: |
86 | * A maximum of three files per request, or; |
87 | * A maximum of 5 MiB before chunking. |
88 | * |
89 | * @param {FileList | File[]} files The files to upload |
90 | * @returns {AsyncGenerator<TranscriptionModel[]>} The transcribed audio files. |
91 | * @throws {TranscriptionError} When the transcription request fails to complete. |
92 | */ |
93 | async* batchTranscribeFiles(files) { |
94 | let filesToSubmit = []; |
95 | let fileCounter = 0; |
96 | let byteCounter = 0; |
97 | |
98 | for (const file of files) { |
99 | if (fileCounter === this.MAX_BATCH_COUNT || byteCounter > this.BATCH_BYTE_LIMIT) { // 5 MiB |
100 | yield await this.transcribeFiles(filesToSubmit); |
101 | filesToSubmit = []; |
102 | byteCounter = 0; |
103 | fileCounter = 0; |
104 | } |
105 | |
106 | filesToSubmit[fileCounter++] = file; |
107 | byteCounter += file.size; |
108 | } |
109 | |
110 | if (filesToSubmit.length > 0) { |
111 | yield await this.transcribeFiles(filesToSubmit); |
112 | } |
113 | } |
114 | |
115 | /** |
116 | * Performs a query to transcribe the given audio files. |
117 | * |
118 | * @param {FileList | File[]} files The files to upload. |
119 | * @returns {Promise<TranscriptionModel[]>} The transcribed audio file. |
120 | * @throws {TranscriptionError} When the transcription request fails to complete. |
121 | */ |
122 | async transcribeFiles(files) { |
123 | const objects = []; |
124 | |
125 | for (const file of files) { |
126 | const object = JSON.parse(this.TEST_JSON); |
127 | object.file_name = file.name; |
128 | objects.push(object); |
129 | } |
130 | |
131 | return objects; |
132 | } |
133 | |
134 | /** |
135 | * Performs a query to transcribe the given audio files. |
136 | * |
137 | * @param {FileList | File[]} files The files to upload. |
138 | * @returns {Promise<TranscriptionModel[]>} The transcribed audio file. |
139 | * @throws {TranscriptionError} When the transcription request fails to complete. |
140 | */ |
141 | async transcribeFilesActual(files) { |
142 | const that = this; |
143 | const formData = new FormData(); |
144 | |
145 | let audioFileKeys = ""; |
146 | for (let i = 0; i < files.length; i++) { |
147 | const f = files[i]; |
148 | const key = "audioFile" + i; |
149 | |
150 | formData.append(key, f, f.name); |
151 | audioFileKeys += key + "|"; |
152 | } |
153 | formData.append("audioFileKeys", audioFileKeys); |
154 | |
155 | try { |
156 | const response = await fetch( |
157 | that.queryUrl, |
158 | { |
159 | method: "POST", |
160 | body: formData |
161 | } |
162 | ); |
163 | |
164 | if (!response.ok) { |
165 | console.error(`Transcription API failed with status ${response.status} and message ${response.statusText}`); |
166 | throw new TranscriptionError(response.statusText, undefined, response.status); |
167 | } |
168 | |
169 | return await response.json(); |
170 | } |
171 | catch (e) { |
172 | console.error(`Transcription failed with reason ${e}`); |
173 | throw new TranscriptionError(undefined, "Unknown"); |
174 | } |
175 | } |
176 | } |
Note:
See TracBrowser
for help on using the repository browser.