Ignore:
Timestamp:
2021-11-08T15:51:51+13:00 (2 years ago)
Author:
cstephen
Message:

Add support for file macronisation

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/model-interfaces-dev/atea/macron-restoration/src/components/FileInput.vue

    r35716 r35723  
    11<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>
     2<file-upload />
    93
    10             <span>{{ translations.get("TranscriptionItem_FileName") }}: {{ transcription.fileName }}</span>
     4<div class="file-info-list">
     5    <div v-for="(fileInfo, index) in fileInfos" :key="fileInfo.id">
     6        <div class="info-container">
     7            <span class="material-icons mdi-override" v-if="fileInfo.fileType === '.txt'">description</span>
     8            <img class="icon-l" src="resources/word_icon.ico" v-if="fileInfo.fileType === '.docx'" />
    119
    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>
     10            {{ fileInfo.fileName }}
    1711
    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>
     12            <button @click="download(fileInfo)" type="button" class="btn-primary theme-flat">
     13                <span class="material-icons">download</span>
     14                <span>{{ translations.get("FileInput_Download") }}</span>
    4015            </button>
    4116        </div>
    4217
    43         <div class="editor-controls">
    44             <audio-time-bar v-model.number="currentPlaybackTime" :audio-length="audioLength" :isDisabled="playbackState.id != transcription.id" />
    45         </div>
     18        <hr class="divider-s" v-if="index !== fileInfos.length - 1" />
     19    </div>
     20</div>
    4621
    47         <hr />
     22<form ref="formDownload" method="post" target="_blank">
     23    <input ref="inputFilePath" type="hidden" name="filepath" />
     24    <input ref="inputFileName" type="hidden" name="filename" />
     25</form>
    4826
    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>
    5627</template>
    5728
    5829<style scoped lang="scss">
    59 .transcription__header {
     30.info-container {
    6031    display: grid;
    61     gap: 0.5em 0.5em;
    62     grid-template-columns: auto 1fr auto auto;
     32    grid-template-columns: auto 1fr auto;
    6333    align-items: center;
     34
     35    gap: 0.5em;
     36    padding: 0.5em;
     37    transition-duration: var(--transition-duration);
     38
     39    &:hover {
     40        background-color: #DDD;
     41    }
    6442}
    6543
    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;
     44.file-info-list {
     45    border: 1px solid #BBB;
     46    margin-top: 1em;
    8247}
    8348
    84 .download-popup-show {
    85     visibility: visible;
    86     opacity: 1;
     49.mdi-override {
     50    @extend .mdi-l;
     51
     52    color: var(--bg-color);
    8753}
    8854
    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);
     55.icon-l {
     56    height: 36px;
    9957}
    10058</style>
     
    10260<script>
    10361import { mapState } from "vuex";
    104 import { saveAs } from "file-saver"
    105 import { TranscriptionViewModel } from "../main";
    106 import AudioPlayback from "../js/AudioPlaybackModule"
    107 import Util from "../js/Util"
    108 import AudioTimeBar from "./AudioTimeBar.vue"
    109 import TranscriptionItemEditor from "./TranscriptionItemEditor.vue"
     62import MacronRestorationService from "../js/MacronRestorationModule"
     63import FileUpload from "./FileUpload.vue"
     64
     65const macronRestorer = new MacronRestorationService();
    11066
    11167export default {
    112     name: "TranscriptionItem",
     68    name: "FileInput",
    11369    components: {
    114         AudioTimeBar,
    115         TranscriptionItemEditor
    116     },
    117     props: {
    118         transcription: TranscriptionViewModel
    119     },
    120     data() {
    121         return {
    122             enableEditing: false,
    123             showDownloadOptions: false
    124         }
     70        FileUpload
    12571    },
    12672    computed: {
    127         currentPlaybackTime: {
    128             get() {
    129                 return this.$store.getters.transcriptionPlaybackTime(this.transcription.id);
    130             },
    131             set(value) {
    132                 this.$store.commit("playbackStateSetTime", { id: this.transcription.id, time: value });
    133             }
    134         },
    135         audioLength() {
    136             return this.$store.getters.transcriptionPlaybackLength(this.transcription.id);
    137         },
    138         isPlaying() {
    139             return this.playbackState.isPlaying && this.playbackState.id === this.transcription.id;
    140         },
    14173        ...mapState({
    14274            translations: state => state.translations,
    143             playbackState: state => state.playbackState
     75            fileInfos: state => state.macronisedFileInfos
    14476        })
    14577    },
    14678    methods: {
    147         async toggleAudio() {
    148             this.isPlaying ? AudioPlayback.pause() : await AudioPlayback.play(this.transcription.id, -1);
    149         },
    150         remove() {
    151             this.$store.commit("rawTranscriptionRemove", this.transcription.id);
    152         },
    153         downloadAsText() {
    154             const fileName = buildDownloadableFileName(this.transcription.fileName, "txt");
    155 
    156             const blob = new Blob([ this.$refs.editor.words.map(w => w.word).join(" ") ], { type: "text/plain;charset=utf-8" });
    157             saveAs(blob, fileName);
    158         },
    159         downloadAsJson() {
    160             const fileName = buildDownloadableFileName(this.transcription.fileName, "json");
    161             const toDownload = (({ fileName, transcription }) => ({ fileName, transcription }))(this.transcription);
    162             toDownload.words = this.$refs.editor.words.map(w => (({ word, startTime, endTime }) => ({ word, startTime, endTime }))(w));
    163 
    164             const blob = new Blob([ JSON.stringify(toDownload, null, 4) ], { type: "application/json;charset=utf-8" });
    165             saveAs(blob, fileName);
    166         },
    167         downloadAsWebvtt() {
    168             const fileName = buildDownloadableFileName(this.transcription.fileName, "vtt");
    169             const toDownload = buildWebvttFileContents(this.transcription, this.$refs.editor);
    170 
    171             const blob = new Blob([ toDownload ], { type: "text/vtt;charset=utf-8" });
    172             saveAs(blob, fileName);
     79        download(fileInfo) {
     80            this.$refs.inputFilePath.value = fileInfo.filePath;
     81            this.$refs.inputFileName.value = fileInfo.fileName;
     82            this.$refs.formDownload.submit();
    17383        }
     84    },
     85    mounted() {
     86        this.$refs.formDownload.setAttribute("action", macronRestorer.queryUrl + "Download");
    17487    }
    17588}
    176 
    177 /**
    178  * Builds a file name for a download.
    179  * @param {String} transcriptionFileName The name of the transcription that will be downloaded.
    180  * @param {String} extension The file extension of the download. Do not include a period.
    181  * @returns {String} The file name.
    182  */
    183 function buildDownloadableFileName(transcriptionFileName, extension) {
    184     const extensionIndex = transcriptionFileName.lastIndexOf(".");
    185     let fileName = transcriptionFileName.slice(0, extensionIndex);
    186     fileName += "_transcription." + extension;
    187 
    188     return fileName;
    189 }
    190 
    191 /**
    192  * Builds a WebVTT file of the given transcription
    193  * @param {TranscriptionViewModel} transcription The transcription.
    194  * @returns {String} The WebVTT content.
    195  */
    196 function buildWebvttFileContents(transcription, editor) {
    197     let contents = "WEBVTT Transcription of " + transcription.fileName + "\n\n";
    198 
    199     for (const word of editor.words) {
    200         const startTime = Util.formatSecondsTimeString(word.startTime, true);
    201         const endTime = Util.formatSecondsTimeString(word.endTime, true);
    202 
    203         contents += startTime + " --> " + endTime + "\n";
    204         contents += "- " + word.word + "\n\n";
    205     }
    206 
    207     return contents;
    208 }
    20989</script>
Note: See TracChangeset for help on using the changeset viewer.