source: main/trunk/model-interfaces-dev/atea/korero-maori-asr/src/App.vue@ 35432

Last change on this file since 35432 was 35432, checked in by cstephen, 3 years ago

Prevent duplicate transcriptions being created

File size: 5.6 KB
Line 
1<template>
2 <!-- Used to calculate the character size of our monospace font -->
3 <span class="monospace-font-sizer">ngā tama a rangi</span>
4
5 <div class="app-bar">
6 <div class="app-bar-content">
7 <span class="heading1">{{ translations.get("Title") }}</span>
8
9 <a class="btn-primary theme-accent" href="https://koreromaori.io" target="_blank">
10 <span class="material-icons mdi-m">open_in_new</span>
11 <u>koreromaori.io</u>
12 </a>
13
14 <toggle-button class="theme-accent" v-model="showInfo" :reverseState="true">
15 <span class="material-icons mdi-l">info</span>
16 </toggle-button>
17 </div>
18
19 <div v-if="showInfo" class="paper">
20 A tool to transcribe audio recordings of spoken Māori. Basic support for editing the transcriptions is provided,
21 and they can be downloaded in multiple formats.
22 <br />
23 The actual transcriptions are produced with gratitude by using <a href="https://koreromaori.io">koreromaori.io's</a> service.
24 </div>
25 </div>
26
27 <div class="paper content">
28 <AudioUpload />
29 </div>
30
31 <ul id="transcription-list" class="list-view content">
32 <transition-group name="transcription-list">
33 <li class="list-item transcription-list-item" v-for="[id, transcription] in transcriptions" :key="id">
34 <TranscriptionItem :transcription="transcription" />
35 </li>
36 </transition-group>
37 </ul>
38</template>
39
40<style lang="scss">
41#transcription-list {
42 margin-bottom: 1em;
43}
44
45.monospace-font-sizer {
46 font: var(--monospace-font);
47 padding: 0;
48 position: absolute;
49 top: -100px;
50}
51
52.app-bar {
53 display: flex;
54 flex-direction: column;
55 gap: 1em;
56
57 color: var(--primary-fg-color);
58 background-color: var(--primary-bg-color-l1);
59 box-shadow: 0px 0px 4px 3px #6d6d6d;
60
61 padding: 1em;
62 margin-bottom: 1em;
63}
64
65.app-bar-content {
66 display: flex;
67 align-items: center;
68 gap: 1em;
69
70 width: 100%;
71
72 & :first-child {
73 flex-grow: 1;
74 }
75}
76
77.content {
78 margin: 1em;
79}
80
81.transcription-list-item {
82 transition: all 0.8s ease;
83 margin-bottom: 1em;
84
85 &:last-child {
86 margin-bottom: 0;
87 }
88}
89
90.transcription-list-leave-to {
91 opacity: 0;
92 transform: translateX(30%);
93}
94
95.transcription-list-leave-active {
96 position: absolute;
97}
98</style>
99
100<script>
101import { mapState } from "vuex";
102import AudioUpload from "./components/AudioUpload.vue"
103import ToggleButton from "./components/ToggleButton.vue";
104import TranscriptionItem from "./components/TranscriptionItem.vue"
105
106export default {
107 name: "App",
108 components: {
109 AudioUpload,
110 TranscriptionItem,
111 ToggleButton
112 },
113 data() {
114 return {
115 /** @type {{id: String, url: String} | null} */
116 currentlyLoadedAudio: null,
117 player: new Audio(),
118 showInfo: false
119 }
120 },
121 computed: mapState({
122 translations: state => state.translations,
123 transcriptions: state => state.rawTranscriptions,
124 playbackState: state => state.playbackState,
125 shouldPlayAudio: state => state.playbackState.isPlaying
126 }),
127 watch: {
128 async playbackState(newValue) {
129 if (!newValue.isPlaying) {
130 return;
131 }
132
133 const transcription = this.transcriptions.get(newValue.id);
134 loadTranscriptionAudio(this.player, transcription, this.currentlyLoadedAudio);
135
136 let playbackTime = 0;
137 if (newValue.currentTime > 0) {
138 playbackTime = newValue.currentTime;
139 }
140
141 this.player.currentTime = playbackTime;
142 await this.player.play();
143
144 this.playbackState.length = this.player.duration;
145 }//,
146 // shouldPlayAudio(newValue) {
147 // if (newValue && this.$refs.audioPlayer.paused) {
148 // this.$refs.audioPlayer.play();
149 // }
150 // else if (!newValue && !this.$refs.audioPlayer.paused) {
151 // this.$refs.audioPlayer.pause();
152 // }
153 // }
154 },
155 mounted() {
156 pollAudioTime(this.player, this.$store);
157 }
158}
159
160// Adapted from https://davidwalsh.name/javascript-polling
161function pollAudioTime(audioElement, store) {
162 var lastTime = 0;
163 (function p() {
164 if (audioElement.currentTime !== lastTime) {
165 lastTime = audioElement.currentTime;
166 store.commit("setCurrentPlaybackTime", lastTime);
167 store.commit("setCurrentlyPlaying", true);
168 }
169 else {
170 store.commit("setCurrentlyPlaying", false);
171 }
172
173 setTimeout(p, 33); // Slightly more than 30hz
174 })();
175}
176
177/**
178 * Loads an audio file.
179 * @param {Audio} player The audio player.
180 * @param {TranscriptionViewModel} transcription The name of the requested audio file.
181 * @param {{id: String, url: String} | null} current The currently loaded audio.
182 * @returns {{id: String, url: String}} If a new audio file was loaded, a new audio tracking object, else the current one.
183 */
184function loadTranscriptionAudio(player, transcription, current) {
185 if (current == null || current.id !== transcription.id) {
186 // TODO: Need to profile on some larger tracks; this may not be worth it? Better to cache a URL object instead?
187 if (current !== null) {
188 URL.revokeObjectURL(current.url);
189 }
190
191 const urlObject = URL.createObjectURL(transcription.file);
192 player.src = urlObject;
193 player.load();
194
195 return { id: transcription.id, url: urlObject };
196 }
197
198 return current;
199}
200</script>
Note: See TracBrowser for help on using the repository browser.