source: main/trunk/model-interfaces-dev/atea/korero-maori-asr/src/components/TranscriptionItem.vue@ 35731

Last change on this file since 35731 was 35731, checked in by cstephen, 3 months ago

Port updated material styles

File size: 7.7 KB
Line 
1<template>
2 <div class="card">
3 <!-- Header containing info and actions for the transcription -->
4 <div class="transcription__header">
5 <button class="btn-fab" v-on:click="toggleAudio" type="button" :title="translations.get('TranscriptionItem_PlayButtonTooltip')">
6 <span class="material-icons mdi-l play-button" v-if="!isPlaying">play_arrow</span>
7 <span class="material-icons mdi-l play-button" v-if="isPlaying">pause</span>
8 </button>
9
10 <span>{{ translations.get("TranscriptionItem_FileName") }}: {{ transcription.fileName }}</span>
11
12 <div style="position: relative;">
13 <button class="btn-primary" @mouseover="showDownloadOptions = true" @mouseout="showDownloadOptions = false" type="button">
14 <span class="material-icons">download</span>
15 <span>{{ translations.get("TranscriptionItem_Download") }}</span>
16 </button>
17
18 <div class="download-popup card" :class="{ 'download-popup-show': showDownloadOptions }"
19 @mouseover="showDownloadOptions = true" @mouseout="showDownloadOptions = false">
20 <button @click="downloadAsText" type="button" class="btn-primary theme-flat left-align">
21 <span class="material-icons">text_snippet</span>
22 <span>{{ translations.get("TranscriptionItem_DownloadAsText") }}</span>
23 </button>
24
25 <button @click="downloadAsJson" type="button" class="btn-primary theme-flat left-align">
26 <span class="material-icons">integration_instructions</span>
27 <span>{{ translations.get("TranscriptionItem_DownloadAsJson") }}</span>
28 </button>
29
30 <button @click="downloadAsWebvtt" type="button" class="btn-primary theme-flat left-align">
31 <span class="material-icons">subtitles</span>
32 <span>{{ translations.get("TranscriptionItem_DownloadAsWebvtt") }}</span>
33 </button>
34 </div>
35 </div>
36
37 <button class="btn-primary theme-error" @click="remove" type="button">
38 <span class="material-icons">delete</span>
39 <span>{{ translations.get("TranscriptionItem_Remove") }}</span>
40 </button>
41 </div>
42
43 <div class="editor-controls">
44 <audio-time-bar v-model.number="currentPlaybackTime" :audio-length="audioLength" :isDisabled="playbackState.id != transcription.id" />
45 </div>
46
47 <hr />
48
49 <div class="editor-controls">
50 <TranscriptionItemEditor ref="editor" :transcription="transcription" style="margin-bottom: 1em" :enableEditing="enableEditing" />
51 <toggle-button v-model="enableEditing" :title="translations.get('TranscriptionItemEditor_ToggleEditTooltip')">
52 <span class="material-icons">edit</span>
53 </toggle-button>
54 </div>
55 </div>
56</template>
57
58<style scoped lang="scss">
59.transcription__header {
60 display: grid;
61 gap: 0.5em 0.5em;
62 grid-template-columns: auto 1fr auto auto;
63 align-items: center;
64}
65
66.download-popup {
67 display: flex;
68 flex-direction: column;
69 align-items: stretch;
70
71 position: absolute;
72 top: 97%;
73 z-index: 2;
74
75 padding: 2px;
76 margin: 0;
77 width: 14em;
78
79 transition-duration: var(--transition-duration);
80 visibility: hidden;
81 opacity: 0;
82}
83
84.download-popup-show {
85 visibility: visible;
86 opacity: 1;
87}
88
89.editor-controls {
90 display: grid;
91 align-items: flex-start;
92 grid-template-columns: 1fr auto;
93 margin: 0.5em 0 0.3em 0;
94 gap: 1em;
95}
96
97.rotate-180 {
98 transform: rotate(180deg);
99}
100
101.left-align {
102 justify-content: flex-start;
103}
104</style>
105
106<script>
107import { mapState } from "vuex";
108import { saveAs } from "file-saver"
109import { TranscriptionViewModel } from "../main";
110import AudioPlayback from "../js/AudioPlaybackModule"
111import Util from "../js/Util"
112import AudioTimeBar from "./AudioTimeBar.vue"
113import TranscriptionItemEditor from "./TranscriptionItemEditor.vue"
114
115export default {
116 name: "TranscriptionItem",
117 components: {
118 AudioTimeBar,
119 TranscriptionItemEditor
120 },
121 props: {
122 transcription: TranscriptionViewModel
123 },
124 data() {
125 return {
126 enableEditing: false,
127 showDownloadOptions: false
128 }
129 },
130 computed: {
131 currentPlaybackTime: {
132 get() {
133 return this.$store.getters.transcriptionPlaybackTime(this.transcription.id);
134 },
135 set(value) {
136 this.$store.commit("playbackStateSetTime", { id: this.transcription.id, time: value });
137 }
138 },
139 audioLength() {
140 return this.$store.getters.transcriptionPlaybackLength(this.transcription.id);
141 },
142 isPlaying() {
143 return this.playbackState.isPlaying && this.playbackState.id === this.transcription.id;
144 },
145 ...mapState({
146 translations: state => state.translations,
147 playbackState: state => state.playbackState
148 })
149 },
150 methods: {
151 async toggleAudio() {
152 this.isPlaying ? AudioPlayback.pause() : await AudioPlayback.play(this.transcription.id, -1);
153 },
154 remove() {
155 this.$store.commit("rawTranscriptionRemove", this.transcription.id);
156 },
157 downloadAsText() {
158 const fileName = buildDownloadableFileName(this.transcription.fileName, "txt");
159
160 const blob = new Blob([ this.$refs.editor.words.map(w => w.word).join(" ") ], { type: "text/plain;charset=utf-8" });
161 saveAs(blob, fileName);
162 },
163 downloadAsJson() {
164 const fileName = buildDownloadableFileName(this.transcription.fileName, "json");
165 const toDownload = (({ fileName, transcription }) => ({ fileName, transcription }))(this.transcription);
166 toDownload.words = this.$refs.editor.words.map(w => (({ word, startTime, endTime }) => ({ word, startTime, endTime }))(w));
167
168 const blob = new Blob([ JSON.stringify(toDownload, null, 4) ], { type: "application/json;charset=utf-8" });
169 saveAs(blob, fileName);
170 },
171 downloadAsWebvtt() {
172 const fileName = buildDownloadableFileName(this.transcription.fileName, "vtt");
173 const toDownload = buildWebvttFileContents(this.transcription, this.$refs.editor);
174
175 const blob = new Blob([ toDownload ], { type: "text/vtt;charset=utf-8" });
176 saveAs(blob, fileName);
177 }
178 }
179}
180
181/**
182 * Builds a file name for a download.
183 * @param {String} transcriptionFileName The name of the transcription that will be downloaded.
184 * @param {String} extension The file extension of the download. Do not include a period.
185 * @returns {String} The file name.
186 */
187function buildDownloadableFileName(transcriptionFileName, extension) {
188 const extensionIndex = transcriptionFileName.lastIndexOf(".");
189 let fileName = transcriptionFileName.slice(0, extensionIndex);
190 fileName += "_transcription." + extension;
191
192 return fileName;
193}
194
195/**
196 * Builds a WebVTT file of the given transcription
197 * @param {TranscriptionViewModel} transcription The transcription.
198 * @returns {String} The WebVTT content.
199 */
200function buildWebvttFileContents(transcription, editor) {
201 let contents = "WEBVTT Transcription of " + transcription.fileName + "\n\n";
202
203 for (const word of editor.words) {
204 const startTime = Util.formatSecondsTimeString(word.startTime, true);
205 const endTime = Util.formatSecondsTimeString(word.endTime, true);
206
207 contents += startTime + " --> " + endTime + "\n";
208 contents += "- " + word.word + "\n\n";
209 }
210
211 return contents;
212}
213</script>
Note: See TracBrowser for help on using the repository browser.