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

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

Refactor the audio bar and transcription edit button to the TranscriptionItem component

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