INL / corpus-frontend

BlackLab Frontend, a feature-rich corpus search interface for BlackLab.
16 stars 7 forks source link

Adding a button for external file download from cloud #451

Closed stcoats closed 1 year ago

stcoats commented 1 year ago

Hi guys,

How would I go about adding functionality that can retrieve a file hosted on an external cloud server and allow the user to download it?

For example, if a user listens to an audio clip

image

I would like to add a button at the bottom right "Download Audio Clip". The URL of the clip on the external cloud server shouldn't be visible, and the downloaded audio file should also have a different name than it does on the cloud server. I'd also like to add an additional button to download a different metadata file for that clip, "Download Audio Information", with similar functionality.

Thank you for any tips you can give me in this direction!

KCMertens commented 1 year ago

If you want to hide the urls, you'll have the initiate the download using your own code, as otherwise you'd have to set the href attribute on a link somewhere, and it would show up when the user hovers it.

Try something like the following. This code defines a new Vue component download-and-info-buttons, with a single prop named context. You can think of props like named function arguments. You could do all of this without using a component, but you'd have to manually go attach event listeners, store the context somewhere, do cleanup afterward, etc. It would be a whole thing, so just let Vue do the heavy lifting here.

This way we can just do @click and it will call the function of our choice. And we will magically have the hit's context available.

The only thing is that the location in the DOM is hardcoded to be right before the hit text, so getting it to show up in the bottom right will require some inline styles or css trickery. I'll leave that to you.

Vue.component('download-and-info-buttons', {
    props: ['context'],
    template: `
        <div class="download-and-info-buttons">
            <button class="btn btn-sm btn-default" @click="download">Download Audio Clip</button>
            <button class="btn btn-sm btn-default" @click="info">Download Audio Information</button>
        </div>
    `,
    methods: {
               // try logging this.context to console if you're unsure what's in there. 
        download() {
            alert('download button clicked, we have the following info: ' + JSON.stringify(this.context, undefined, 2));
            const link = document.createElement("a");
            // Add file content in the object URL
            link.href = 'link_goes_here';
            // Add file name
            link.download = "audio_file_name_goes_here.mp3";
            link.click();
            link.remove();
        },
        info() {
            alert('info button clicked, we have the following info: ' + JSON.stringify(this.context, undefined, 2));
        }
    }
})

vuexModules.ui.getState().results.hits.addons.push(context => {
    return {
        name: 'download-addon', // must be unique
        component: 'download-and-info-buttons', // render the component we defined above
        props: { context: context }, // pass the hit context into the component's 'context' prop
    }
}
stcoats commented 1 year ago

Thanks! I added the code as the last entries in .../static/custom.search.js, but it seems to have cancelled the effects of the other scripts in that file for the search page hit rendering -- the number of words to the right and left, which were defined using vuexModules.ui.actions.results.shared.concordanceSize(20) in the custom file, has reverted to the default of 50, and Punct, which I had removed (and is also not indexed in the small testing corpus), is back. Likewise, the custom audio player is no longer available.

Before:

image

After: image

Do I need to put this script somewhere else?

Thanks for your help -- my JavaScript knowledge is minimal, and my vue/vuex knowledge non-existent!

KCMertens commented 1 year ago

Sounds like the script ran into an error somehow, is there an error logged in the browser console?

stcoats commented 1 year ago

No, no errors that I can see? this.context is not defined, though.

image

stcoats commented 1 year ago

Update, I found it, it was a missing ) at the very end of the script! 😄 Working correctly now.

Thank you again for the excellent help. I will probably come back with more questions soon!