Changeset 35296
- Timestamp:
- 2021-08-16T16:27:24+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/asr-controller.js
r35294 r35296 60 60 { 61 61 return { 62 files: undefined,62 files: [], 63 63 canTranscribe: false, 64 64 isTranscribing: false 65 } 66 }, 67 computed: 68 { 69 anyFiles() { 70 return this.files.length > 0; 71 }, 72 getFileNameList() 73 { 74 let fileNameList = ""; 75 76 for (let file of this.files) { 77 fileNameList += file.name + ", "; 78 } 79 80 return fileNameList.slice(0, fileNameList.length - 2); 65 81 } 66 82 }, … … 77 93 78 94 if (audioFileInput.files?.length != undefined && audioFileInput.files?.length > 0) { 79 this.canTranscribe = true;80 95 this.files = audioFileInput?.files 81 96 } 82 97 else { 83 this.canTranscribe = false; 84 this.files = undefined; 98 this.files = []; 85 99 } 86 100 }, … … 104 118 /** @type {Map<String, TranscriptionModel>} */ 105 119 transcriptions: new Map(), 106 /** @type {TranscriptionError[]} */ 107 failures: [], // TODO: Ability to remove failures 108 showCharDisplay: false 120 /** @type {Map<String, TranscriptionError>} */ 121 failures: new Map(), 122 showCharDisplay: false, 123 currentAudioTime: 0, 124 audioDuration: 0, 125 showWordList: false 109 126 } 110 127 }, … … 147 164 console.log("Starting at " + startTime + " seconds"); 148 165 loadAudioFile(fileName); 166 this.currentAudioTime = startTime; 149 167 TRANSCRIPTION_AUDIO_ELEMENT.currentTime = startTime; 150 168 TRANSCRIPTION_AUDIO_ELEMENT.play(); … … 153 171 this.transcriptions.delete(id); 154 172 // TODO: delete cached audio file 173 }, 174 removeFailure(id) { 175 this.failures.delete(id); 155 176 }, 156 177 getWords(transcriptionId) … … 203 224 204 225 return chars; 226 }, 227 toggleWordList() { 228 this.showWordList = !this.showWordList; 205 229 } 206 230 } … … 254 278 { 255 279 if (!t.success) { 256 TranscriptionsListVM.failures. push(new TranscriptionError(t.log, t.file_name));280 TranscriptionsListVM.failures.set(UUID.generate(), new TranscriptionError(t.log, t.file_name)); 257 281 } 258 282 else { … … 356 380 } 357 381 358 /** 359 * Recurses through the entire tree of a parent and adds the given data attribute. 360 * 361 * @param {Element} parent The parent node. 362 * @param {String} dataElementName The name of the data element. Must use hyphen notation, rather than camel case. 363 * @param {String} value The value of the data element. 364 */ 365 function recurseAddData(parent, dataElementName, value) 366 { 367 parent.setAttribute("data-" + dataElementName, value); 368 369 for (const child of parent.children) { 370 recurseAddData(child, dataElementName, value); 371 } 372 } 382 TRANSCRIPTION_AUDIO_ELEMENT.addEventListener('durationchange', function() { 383 TranscriptionsListVM.audioDuration = TRANSCRIPTION_AUDIO_ELEMENT.duration; 384 }); 385 386 TRANSCRIPTION_AUDIO_ELEMENT.addEventListener('timeupdate', function() { 387 console.log("time updated"); 388 TranscriptionsListVM.currentAudioTime = TRANSCRIPTION_AUDIO_ELEMENT.currentTime; 389 }); -
main/trunk/model-interfaces-dev/atea/style/asr.scss
r35294 r35296 7 7 --primary-bg-color-d1: #2785ad; 8 8 --primary-bg-color-d2: #227497; 9 10 --primary-fg-color: white; 11 9 12 --primary-box-shadow: 0px 2px 4px 0px #505050; 10 13 --primary-box-shadow-thin: 0px 1px 3px 0px #747474; 14 15 --paper-color: #FCFCFC; 16 --page-bg-color: #f7f4f0; 17 18 --monospace-font: 16px 'Roboto Mono', sans-serif; 19 20 --border-radius: 0.3em; 21 22 --transition-duration: 0.15s; 23 } 24 25 .theme-error { 26 --primary-bg-color: #f04848; 11 27 --primary-fg-color: white; 12 13 --error-bg-color: #ff4242;14 --error-fg-color: black;15 16 --paper-color: #FCFCFC;17 18 --monospace-font: 16px 'Roboto Mono', sans-serif;19 }20 21 .theme-error {22 --primary-bg-color: #ff4242;23 --primary-fg-color: black;24 28 } 25 29 26 30 .theme-flat { 27 31 --primary-bg-color: transparent; 28 --primary-fg-color: #2 22;32 --primary-fg-color: #2191c0; 29 33 --primary-box-shadow: none; 30 34 31 35 &:hover { 32 --primary-bg-color: #EEE; 33 } 36 background-color: #EEE; 37 } 38 } 39 40 @mixin transition-set { 41 -webkit-transition-duration: var(--transition-duration); 42 -moz-transition-duration: var(--transition-duration); 43 transition-duration: var(--transition-duration); 34 44 } 35 45 … … 40 50 .paper { 41 51 background-color: var(--paper-color); 42 padding: 1 em;43 border-radius: 0.3em;52 padding: 1.2em; 53 border-radius: var(--border-radius); 44 54 box-shadow: var(--primary-box-shadow); 45 55 } 46 56 57 .card { 58 @extend .paper; 59 margin: 1em; 60 61 box-shadow: var(--primary-box-shadow-thin); 62 } 63 64 .card-outlined { 65 @extend .card; 66 67 box-shadow: none; 68 border: 1px solid #BBB; 69 } 70 47 71 .divider { 48 margin: 1em 2em;49 72 height: 1px; 50 73 background-color: #0000001F; … … 66 89 margin: 0 1px 3px 1px; /* Keeps space around the box shadow */ 67 90 68 -webkit-transition-duration: 0.2s; 69 transition-duration: 0.2s; 91 @include transition-set; 70 92 71 93 &:hover { … … 84 106 box-shadow: var(--primary-box-shadow); 85 107 86 border-radius: 0.3em;108 border-radius: var(--border-radius); 87 109 padding: 0.5em; 88 110 cursor: pointer; … … 92 114 margin: 0 1px 3px 1px; /* Keeps space around the box shadow */ 93 115 94 -webkit-transition-duration: 0.15s; 95 transition-duration: 0.15s; 116 @include transition-set; 96 117 97 118 span { … … 109 130 } 110 131 132 /** Text display/input */ 133 134 .text-container { 135 display: flex; 136 align-items: center; 137 138 border-radius: var(--border-radius); 139 //border-bottom: 1px solid #AAA; 140 background: #DCDCDCDC; 141 padding: 0.5em; 142 } 143 144 .text-container-sl { 145 @extend .text-container; 146 147 overflow-x: auto; 148 overflow-y: hidden; 149 white-space: nowrap; 150 } 151 152 .text-input { 153 display: flex; 154 align-items: center; 155 156 border-radius: var(--border-radius) var(--border-radius) 0 0; 157 border-bottom: 1px solid #AAA; 158 background: #EEE; 159 padding: 0.5em; 160 161 @include transition-set; 162 163 &:hover { 164 background-color: #DDD; 165 } 166 167 &:active { 168 border-bottom: 1px solid var(--primary-bg-color); 169 170 .text-input-active { 171 width: 100%; 172 } 173 } 174 175 .text-input-active { 176 background-color: var(--primary-bg-color); 177 height: 1px; 178 margin: 0 auto; 179 width: 0; 180 181 @include transition-set; 182 } 183 } 184 185 .text-placeholder { 186 color: #666; 187 cursor: default; 188 font-style: italic; 189 } 190 111 191 /* === End component definitions === */ 112 192 … … 143 223 } 144 224 145 /* === End theme definitions === */ 225 /* Scrollbar replacement */ 226 227 ::-webkit-scrollbar { 228 width: 6px; 229 height: 6px; 230 } 231 232 ::-webkit-scrollbar-track { 233 background: #f1f1f1; 234 } 235 236 ::-webkit-scrollbar-thumb { 237 background: #CCC; 238 border-radius: 3px; 239 240 @include transition-set; 241 242 &:hover { 243 background: #666; 244 height: 8px; 245 width: 8px; 246 } 247 } 146 248 147 249 /* === Start theme application === */ … … 151 253 } 152 254 255 hr { 256 @extend .divider; 257 } 258 153 259 /* === End theme application === */ 154 260 155 261 body { 156 background-color: var(--pa per-color);262 background-color: var(--page-bg-color); 157 263 font-family: 'Roboto', sans-serif; 158 264 font-size: 16px; 159 265 } 160 266 267 #container { 268 background-color: var(--page-bg-color); 269 } 270 161 271 #gs_content { 162 272 padding: 0; 163 background-color: var(--pa per-color);273 background-color: var(--page-bg-color); 164 274 } 165 275 … … 171 281 #audio-transcription-container { 172 282 margin-top: 2em; 283 } 284 285 .audio-file-picker { 286 display: grid; 287 gap: 0.5em 0.5em; 288 grid-template-columns: auto 1fr auto; 289 align-items: stretch; 290 291 input { 292 display: none; 293 } 294 295 .text-container { 296 cursor: pointer; 297 } 173 298 } 174 299 … … 189 314 190 315 .transcription__container { 191 padding: 0.5em;192 193 316 -webkit-transition-duration: 0.15s; 194 317 transition-duration: 0.15s; 195 318 196 &:last-child { 197 margin-bottom: 0; 198 } 199 200 &:hover { 201 background-color: #EEE; 202 } 319 // &:hover { 320 // background-color: #EEE; 321 // } 203 322 } 204 323 205 324 .transcription__header { 325 display: grid; 326 gap: 0.5em 0.5em; 327 grid-template-columns: 1fr auto; 328 align-items: center; 329 330 margin-bottom: 0.5em; 331 } 332 333 .transcription__word-list { 334 display: grid; 335 gap: 0.5em 0.5em; 336 grid-template-columns: 1fr auto; 337 align-items: start; 338 339 font: var(--monospace-font); 340 padding-top: 1em; 341 } 342 343 .transcription__word-list__controls { 206 344 display: flex; 207 width: 100%; 208 justify-content: space-between; 209 align-items: center; 210 margin-bottom: 0.5em; 211 } 212 213 .transcription__file-name { 214 margin: 0 1em 0 1em; 215 } 216 217 .transcription__word-list { 218 font: var(--monospace-font); 345 flex-direction: column; 346 align-items: center; 219 347 } 220 348 221 349 .transcription__word { 222 350 display: inline-block; 223 margin- top: 1em;351 margin-bottom: 1em; 224 352 225 353 &:hover { … … 230 358 .transcription__error-container { 231 359 @extend .transcription__container; 232 233 background-color: rgba(255, 0, 0, 0.226);234 235 &:hover {236 background-color: rgba(255, 0, 0, 0.226); 237 }360 display: grid; 361 gap: 0.5em 0.5em; 362 grid-template-columns: 1fr auto; 363 align-items: center; 364 365 border-left: 3px solid red; 238 366 } 239 367 -
main/trunk/model-interfaces-dev/atea/transform/pages/asr.xsl
r35294 r35296 20 20 <!-- set page breadcrumbs --> 21 21 <xsl:template name="breadcrumbs"><gslib:siteLink/><gslib:rightArrow/> 22 <xsl:if test="$groupPath != ''">23 <xsl:for-each select="/page/pageResponse/pathList/group">24 <xsl:sort data-type="number" select="@position"/>25 <a>26 <xsl:attribute name="href"><gslib:groupHref path="{@path}"/></xsl:attribute>27 <xsl:attribute name="title"><gslib:groupName path="{@path}"/></xsl:attribute>28 <gslib:groupName path="{@path}"/>29 </a>30 <gslib:rightArrow/>31 </xsl:for-each>32 </xsl:if>22 <xsl:if test="$groupPath != ''"> 23 <xsl:for-each select="/page/pageResponse/pathList/group"> 24 <xsl:sort data-type="number" select="@position" /> 25 <a> 26 <xsl:attribute name="href"><gslib:groupHref path="{@path}"/></xsl:attribute> 27 <xsl:attribute name="title"><gslib:groupName path="{@path}"/></xsl:attribute> 28 <gslib:groupName path="{@path}" /> 29 </a> 30 <gslib:rightArrow/> 31 </xsl:for-each> 32 </xsl:if> 33 33 </xsl:template> 34 34 … … 37 37 <xsl:call-template name="audio-transcription"/> 38 38 </xsl:template> 39 40 <!-- <xsl:attribute-set name="vue">41 <xsl:attribute name="v-on" />42 <xsl:attribute name="v-bind" />43 </xsl:attribute-set> -->44 39 45 40 <!-- Template for processing audio file uploads --> … … 48 43 <link href="interfaces/{$interface_name}/style/asr.css" rel="stylesheet" type="text/css" /> 49 44 50 <section id="audio-transcription-container" class="paper">45 <section id="audio-transcription-container"> 51 46 <!-- Used to calculate the character size of our monospace font --> 52 47 <span class="monospace-font-size">ngÄ tama a rangi</span> 53 48 54 49 <!-- Contains the file input, transcribe button and transcription progress indicator --> 55 <div id="audioUploadContainer" >50 <div id="audioUploadContainer" class="audio-file-picker"> 56 51 <button type="button" v-on:click="openFilePicker" 57 52 v-bind:disabled="isTranscribing"> … … 60 55 </button> 61 56 57 <div class="text-container-sl" v-on:click="openFilePicker"> 58 <span class="text-placeholder" v-if="!anyFiles">Select file/s...</span> 59 <span v-if="anyFiles">{{ getFileNameList }}</span> 60 </div> 61 62 62 <input id="audioFileInput" type="file" v-on:input="onFilesChanged" 63 63 accept="audio/wav" multiple="multiple" v-bind:disabled="isTranscribing" /> 64 64 65 65 <button style="float: right;" type="submit" 66 v-bind:disabled="! canTranscribe" v-on:click="doTranscription">66 v-bind:disabled="!anyFiles" v-on:click="doTranscription"> 67 67 <span class="material-icons"></span> <!-- history_edu --> 68 68 <span>Transcribe</span> … … 75 75 <div id="transcriptionsDisplayContainer"> 76 76 77 <audio id="transcriptionAudio" v-on:timeupdate="">77 <audio id="transcriptionAudio"> 78 78 <source id="transcriptionAudioSource" /> 79 79 </audio> … … 81 81 <ul class="transcription__list"> 82 82 <!-- Displays any failed transcriptions --> 83 <li v-for="failure in failures" class="transcription__error-container"> 84 Failed to transcribe <i v-if="failure.file">{{ failure.file }}</i><br/> 85 <span v-if="failure.message">Reason: {{ failure.message }}</span> 83 <li v-for="[key, value] in failures"> 84 <div class="transcription__error-container card"> 85 <div> 86 Failed to transcribe <i v-if="value.file">{{ value.file }}</i><br/> 87 <span v-if="value.message">Reason: {{ value.message }}</span> 88 </div> 89 90 <button class="btn-fab theme-flat" v-on:click="removeFailure(key)"> 91 <span class="material-icons"></span> <!-- clear --> 92 </button> 93 </div> 86 94 </li> 87 95 88 <li v-for="transcription in getTranscriptions" class="transcription__container"> 89 <div class="transcription__header"> 90 <button class="btn-fab theme-flat" v-on:click="playAudioFile(transcription.fileName)" type="button"> 91 <span class="material-icons"></span> <!-- play_arrow --> 92 </button> 96 <!-- Displays each transcription --> 97 <li v-for="transcription in getTranscriptions"> 98 <div class="transcription__container card"> 99 <!-- Header containing info and actions for the transcription --> 100 <div class="transcription__header"> 101 <span>File: {{ transcription.fileName }}</span> 93 102 94 <p class="transcription__text">{{ transcription.transcription }}</p> 95 <p class="body2 transcription__file-name">File: {{ transcription.fileName }}</p> 103 <button class="theme-error" v-on:click="removeTranscription(transcription.id)" type="button"> 104 <span class="material-icons">delete</span> 105 <span>Remove</span> 106 </button> 107 </div> 96 108 97 <button class="btn-fab theme-error" v-on:click="removeTranscription(transcription.id)" type="button"> 98 <span class="material-icons"></span> <!-- delete --> 99 </button> 100 </div> 109 <div class="text-container"> 110 {{ transcription.transcription }} 111 </div> 101 112 102 <div class="transcription__word-list"> 103 <input type="checkbox" v-model="showCharDisplay" /> 104 <ul class="transcription__list"> 105 <li v-if="!showCharDisplay"> 106 <span v-for="word in getWords(transcription.id)" 107 class="transcription__word" 108 v-on:click="playAudioFile(transcription.fileName, word.startTime)"> 113 <div class="transcription__word-list collapse" v-if="showWordList"> 114 <div class="transcription__word-list__controls"> 115 <button class="btn-fab" v-on:click="playAudioFile(transcription.fileName)" type="button"> 116 <span class="material-icons">play_arrow</span> 117 </button> 118 119 <input type="checkbox" v-model="showCharDisplay" /> 120 </div> 121 122 <ul class="transcription__list"> 123 <li v-if="!showCharDisplay"> 124 <span v-for="word in getWords(transcription.id)" 125 class="transcription__word" 126 v-on:click="playAudioFile(transcription.fileName, word.startTime)"> 109 127 {{ word.word }} 110 128 </span> … … 112 130 <li v-if="showCharDisplay"> 113 131 <span v-for="char in getChars(transcription.id)" 114 class="transcription__word" 115 v-on:click="playAudioFile(transcription.fileName, char.startTime)"> 116 {{ char.char }} 117 </span> 118 </li> 119 <!-- <li v-for="line in getLines(transcription.id)" style="display: flex"> 120 <span v-for="word in line.words" style="border: 1px solid blue">{{ word }}</span> 121 <input type="range" min="0" max="100" /> 122 </li> --> 123 </ul> 132 class="transcription__word" 133 v-on:click="playAudioFile(transcription.fileName, char.startTime)"> 134 {{ char.char }} 135 </span> 136 </li> 137 </ul> 138 </div> 139 140 <hr /> 141 142 <button type="button" class="theme-flat" v-on:click="toggleWordList"> 143 <span class="material-icons" v-if="!showWordList">expand_more</span> 144 <span class="material-icons" v-if="showWordList">expand_less</span> 145 <span>Expand</span> 146 </button> 124 147 </div> 125 126 <hr class="divider" />127 148 </li> 128 149 </ul>
Note:
See TracChangeset
for help on using the changeset viewer.