Changeset 35301
- Timestamp:
- 2021-08-17T11:08:33+12:00 (3 years ago)
- Location:
- main/trunk/model-interfaces-dev/atea
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/model-interfaces-dev/atea/js/asr/TranscribeModule.js
r35297 r35301 50 50 * @param {Number | undefined} statusCode The HTTP status code of the error. 51 51 * @param {String | undefined} message The status message. 52 * @param {String | null} file The file on which the transcription failed.53 */ 54 constructor(message = undefined, file = null, statusCode = -1)52 * @param {String | null} fileName The file on which the transcription failed. 53 */ 54 constructor(message = undefined, fileName = null, statusCode = -1) 55 55 { 56 56 super(message); 57 //this.message = message; 58 59 this.file = file; 57 58 /** @type {String | null} The name of the file that the transcription error occured on. */ 59 this.fileName = fileName; 60 61 /** @type {Number | undefined} The status code returned by the API when the erro was generated. */ 60 62 this.statusCode = statusCode; 61 63 } … … 85 87 * A maximum of 5 MiB before chunking. 86 88 * 87 * @param {FileList } files The files to upload89 * @param {FileList | File[]} files The files to upload 88 90 * @returns {AsyncGenerator<TranscriptionModel[]>} The transcribed audio files. 89 91 * @throws {TranscriptionError} When the transcription request fails to complete. -
main/trunk/model-interfaces-dev/atea/js/asr/asr-controller.js
r35297 r35301 14 14 const TRANSCRIPTION_AUDIO_SOURCE_ELEMENT = document.getElementById("transcriptionAudioSource"); 15 15 16 let cachedAudioFileList = new Map(); 16 class TranscriptionViewModel { 17 /** 18 * Initialises a new instance of the {@link TranscriptionViewModel} class. 19 * 20 * @param {TranscriptionModel} transcription 21 * @param {File} file The file from which the transcription was generated. 22 */ 23 constructor(transcription, file) { 24 /** @type {String} The UUID of this transcription. */ 25 this.id = UUID.generate(); 26 27 /** @type {String} The transcription. */ 28 this.transcription = transcription.transcription; 29 30 /** @type {String} The name of the file from which the transcription was generated. */ 31 this.fileName = transcription.file_name; 32 33 /** @type {TranscriptionMetadata[]} The transcription metadata. */ 34 this.metadata = transcription.metadata; 35 36 /** @type {File} The file from which the transcription was generated. */ 37 this.file = file; 38 } 39 } 40 17 41 var transcribeService = new TranscribeService(); 18 42 … … 93 117 94 118 if (audioFileInput.files?.length != undefined && audioFileInput.files?.length > 0) { 95 this.files = audioFileInput?.files 119 120 for (const f of audioFileInput.files) { 121 this.files.push(f); 122 } 96 123 } 97 124 else { … … 103 130 // Else call getTranscriptions(); 104 131 // Then, we don't have to worry about preventing the user from transcribing multiple items. 105 await getTranscriptions( );132 await getTranscriptions(this.files); 106 133 } 107 134 } … … 110 137 const AudioUploadVM = AudioUploadComponent.mount("#audioUploadContainer"); 111 138 112 /**113 * @typedef TranscriptionViewModel114 * @property {String} id115 * @property {String} transcription116 * @property {String} fileName117 * @property {TranscriptionMetadata} metadata118 * @property {File} file119 */120 121 139 // @ts-ignore 122 140 const TranscriptionsListComponent = Vue.createApp( … … 125 143 { 126 144 return { 127 /** @type {Map<String, Transcription Model>} */145 /** @type {Map<String, TranscriptionViewModel>} */ 128 146 transcriptions: new Map(), 129 147 /** @type {Map<String, TranscriptionError>} */ 130 148 failures: new Map(), 131 149 showCharDisplay: false, 132 showWordList: false 133 } 134 }, 135 computed: 136 { 137 getTranscriptions() 138 { 139 let converted = []; 140 141 for (const [key, value] of this.transcriptions) 142 { 143 converted.push( 144 { 145 id: key, 146 transcription: value.transcription, 147 fileName: value.file_name, 148 metadata: value.metadata 149 }); 150 } 151 152 return converted; 150 showWordList: false, 151 /** @type {{id: String, url: String} | null} Gets the ID of the transcription for which the audio is currently loaded */ 152 currentlyLoadedAudio: null 153 153 } 154 154 }, 155 155 methods: 156 156 { 157 playAudioFile( fileName, startTime = 0) // TODO: Convert to ID157 playAudioFile(transcriptionId, startTime = 0) // TODO: Convert to ID 158 158 { 159 159 if (startTime < 0) … … 169 169 } 170 170 171 console.log("Starting at " + startTime + " seconds"); 172 loadAudioFile(fileName); 173 this.currentAudioTime = startTime; 171 this.currentlyLoadedAudio = loadTranscriptionAudio(transcriptionId, this.currentlyLoadedAudio); 172 174 173 TRANSCRIPTION_AUDIO_ELEMENT.currentTime = startTime; 175 174 TRANSCRIPTION_AUDIO_ELEMENT.play(); … … 267 266 /** 268 267 * Gets the transcription of each submitted audio file. 268 * 269 * @param {File[]} files The files to transcribe. 269 270 */ 270 async function getTranscriptions( )271 async function getTranscriptions(files) 271 272 { 272 273 AudioUploadVM.isTranscribing = true; 273 274 /** @type {FileList} */275 const files = AudioUploadVM.files;276 274 277 await delay(200); // TODO: Remove - UI testing purposes only 278 279 // Cache the file list so that we can playback audio in the future 280 for (const file of files) { 281 cachedAudioFileList.set(file.name, file) 282 } 275 // await delay(200); // TODO: Remove - UI testing purposes only 283 276 284 277 // Transcribe each audio file in batches. … … 293 286 } 294 287 else { 295 TranscriptionsListVM.transcriptions.set(UUID.generate(), t); 288 let f = files.find(f => f.name == t.file_name); 289 if (f == undefined) 290 throw new Error("File name mismatch"); 291 292 let model = new TranscriptionViewModel(t, f); 293 294 TranscriptionsListVM.transcriptions.set(model.id, model); 296 295 } 297 296 } … … 302 301 console.error("Failed to transcribe files"); 303 302 console.error(e); 304 TranscriptionsListVM.failures. push(e);303 TranscriptionsListVM.failures.set(UUID.generate(), e); 305 304 } 306 305 … … 371 370 /** 372 371 * Loads an audio file. 373 * @param {String} requestedAudioFile The name of the requested audio file. 372 * @param {String} transcriptionId The name of the requested audio file. 373 * @param {{id: String, url: String} | null} current The currently loaded audio. 374 * @returns {{id: String, url: String}} If a new audio file was loaded, a new audio tracking object, else the current one. 374 375 */ 375 function loadAudioFile(requestedAudioFile) 376 { 377 const currentAudioFile = TRANSCRIPTION_AUDIO_SOURCE_ELEMENT.dataset.fileName; 378 379 // Load the appropiate audio if necessary. 380 if (currentAudioFile != requestedAudioFile) 381 { 382 // If an audio file is already loaded we can revoke it. 383 if (currentAudioFile) { 384 URL.revokeObjectURL(currentAudioFile); 385 } 386 387 const urlObject = URL.createObjectURL(cachedAudioFileList.get(requestedAudioFile)); 376 function loadTranscriptionAudio(transcriptionId, current) 377 { 378 if (current == null || current.id != transcriptionId) 379 { 380 // TODO: Need to profile on some larger tracks; this may not be worth it? Better to cache a URL object instead? 381 if (current != null) { 382 URL.revokeObjectURL(current.url); 383 } 384 385 const urlObject = URL.createObjectURL(TranscriptionsListVM.transcriptions.get(transcriptionId).file); 388 386 TRANSCRIPTION_AUDIO_SOURCE_ELEMENT.src = urlObject; 389 TRANSCRIPTION_AUDIO_SOURCE_ELEMENT.dataset.fileName = requestedAudioFile;390 387 TRANSCRIPTION_AUDIO_ELEMENT.load(); 391 } 388 389 return { id: transcriptionId, url: urlObject }; 390 } 391 392 return current; 392 393 } 393 394 -
main/trunk/model-interfaces-dev/atea/transform/pages/asr.xsl
r35297 r35301 84 84 <div class="transcription__error-container card"> 85 85 <div> 86 Failed to transcribe <i v-if="value.file">{{ value.file }}</i><br/>86 Failed to transcribe <i v-if="value.file">{{ value.fileName }}</i><br/> 87 87 <span v-if="value.message">Reason: {{ value.message }}</span> 88 88 </div> … … 95 95 96 96 <!-- Displays each transcription --> 97 <li v-for=" transcription in getTranscriptions">97 <li v-for="[id, transcription] in transcriptions"> 98 98 <div class="transcription__container card"> 99 99 <!-- Header containing info and actions for the transcription --> … … 101 101 <span>File: {{ transcription.fileName }}</span> 102 102 103 <button class="theme-error" v-on:click="removeTranscription( transcription.id)" type="button">103 <button class="theme-error" v-on:click="removeTranscription(id)" type="button"> 104 104 <span class="material-icons">delete</span> 105 105 <span>Remove</span> … … 113 113 <div class="transcription__word-list" v-if="showWordList"> 114 114 <div class="transcription__word-list__controls"> 115 <button class="btn-fab" v-on:click="playAudioFile( transcription.fileName)" type="button">115 <button class="btn-fab" v-on:click="playAudioFile(id)" type="button"> 116 116 <span class="material-icons">play_arrow</span> 117 117 </button> … … 123 123 <li v-if="!showCharDisplay"> 124 124 <span v-for="word in getWords(transcription.id)" class="transcription__word" 125 v-on:click="playAudioFile( transcription.fileName, word.startTime)">125 v-on:click="playAudioFile(id, word.startTime)"> 126 126 {{ word.word }} 127 127 </span> … … 129 129 <li v-if="showCharDisplay"> 130 130 <span v-for="char in getChars(transcription.id)" class="transcription__word" 131 v-on:click="playAudioFile( transcription.fileName, char.startTime)">131 v-on:click="playAudioFile(id, char.startTime)"> 132 132 {{ char.char }} 133 133 </span>
Note:
See TracChangeset
for help on using the changeset viewer.