To facilitate the annotation of audio files in a Google spreadsheet, I\'d like to implement an audio player in the sidebar which automatically plays the audio file mentioned as
I made some small changes to the example code you provided so that the sidebar does not update periodically following the time interval.
Basically, I've used PropertiesService to store the row that is selected. The idea is that the script checks whether the currently selected row and the previously selected row (the one selected last time getRecord
was called, that is, during last interval) are the same. If they are the same, there hasn't been a row selection change, which means the audio in the sidebar doesn't need updating.
So it only updates if the selected row changes, which is, I think, the main issue you are having.
To achieve this, your code would have to be modified in the following way (look at inline comments for details on the changes):
getRecord()
function getRecord() {
var scriptProperties = PropertiesService.getScriptProperties();
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var headers = data[0];
var rowNum = sheet.getActiveCell().getRow(); // Get currently selected row
var oldRowNum = scriptProperties.getProperty("selectedRow"); // Get previously selected row
if(rowNum == oldRowNum) { // Check if the was a row selection change
// Function returns the string "unchanged"
return "unchanged";
}
scriptProperties.setProperty("selectedRow", rowNum); // Update row index
if (rowNum > data.length) return [];
var record = [];
for (var col=0;col<headers.length;col++) {
var cellval = data[rowNum-1][col];
if (typeof cellval == "object") {
cellval = Utilities.formatDate(cellval, Session.getScriptTimeZone() , "M/d/yyyy");
}
record.push({ heading: headers[col],cellval:cellval });
}
return record;
}
Depending on whether there was a selection change, getRecord
returns:
record
array, if the selected row is different."unchanged"
, if the selected row is the same. Probably this is not the most elegant way to handle this, but you get the idea.Then, showRecord(record)
gets this returned value. If this value is the string "unchanged"
, it won't update the sidebar:
showRecord(record)
function showRecord(record) {
// Checks whether returned value is `"unchanged"` (this means the row selected is the same one as before)
if (record != "unchanged" && record.length) {
for (var i = 2; i <= 2; i++) {
// build field name on the fly, formatted field-1234
var str = '' + i;
var fieldId = 'field-' + ('0000' + str).substring(str.length)
// If this field # doesn't already exist on the page, create it
if (!$('#'+fieldId).length) {
var newField = $($.parseHTML('<div id="'+fieldId+'"></div>'));
$('#sidebar-record-block').append(newField);
}
// Replace content of the field div with new record
$('#'+fieldId).replaceWith('<div id="'+fieldId+'" class="div-table-row"></div>');
$('#'+fieldId).append($('<div class="div-table-th">' + record[i].heading + '</div>'))
.append('<audio id="player" controls autoplay> <source src=' + record[i].cellval + ' type=audio/wav > Your browser does not support the audio element. </audio>');
}
}
// TODO: hide any existing fields that are beyond the current record length
//Setup the next poll
poll();
}
I also added the autoplay
attribute in this line:
.append('<audio id="player" controls> <source src=' + record[i].cellval + ' type=audio/wav > Your browser does not support the audio element. </audio>')
So that the audio plays automatically when you select a new row, without having to click the play
button.
Finally, I changed the poll
interval to 500, so that you don't have to wait so much for the new audio to play. Anyway you can edit this to whatever suits you best:
interval = interval || 500;
I didn't modify the rest of the script, even though it can probably be improved owing to the fact that it was mainly written for a different issue.
I hope this is of any help.
Playing My Music
I added a play this button to each of my playlist selections. Perhaps this will help you to accomplish what you wish.
code.gs:
function onOpen() {
SpreadsheetApp.getUi().createMenu('My Music')
.addItem('Launch Music', 'launchMusicDialog')
.addToUi();
}
function convMediaToDataUri(filename){
var filename=filename || "You Make Loving Fun.mp3";//this was my debug song
var folder=DriveApp.getFolderById("Music Folder Id");
var files=folder.getFilesByName(filename);
var n=0;
while(files.hasNext()) {
var file=files.next();
n++;
}
if(n==1) {
var blob=file.getBlob();
var b64DataUri='data:' + blob.getContentType() + ';base64,' + Utilities.base64Encode(blob.getBytes());
Logger.log(b64DataUri)
var fObj={filename:file.getName(),uri:b64DataUri}
return fObj;
}
throw("Multiple Files with same name.");
return null;
}
function launchMusicDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('music1');
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Music');
}
function doGet() {
return HtmlService.createHtmlOutputFromFile('music1').addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
function getPlaylist() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('MusicList');
var rg=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn());
var vA=rg.getValues();
var pl=[];
var idx=0;
var html='<style>th,td{border:1px solid black;}</style><table><tr><th>Index</th><th>Item</th><th>FileName</th><th> </th></tr>';
for(var i=0;i<vA.length;i++) {
if(vA[i][4]) {
pl.push(vA[i][1]);
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td><td><input type="button" value="Play This" onClick="playThis(%s)" /></td></tr>',idx,vA[i][0],vA[i][1],idx++);
}
}
html+='</table>';
return {playlist:pl,html:html};
}
music1.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
label{margin:2px 10px;}
</style>
</head>
<script>
var selectionList=["BarbaraAnn.mp3","Don't Let Me Come Home a Stranger.mp3"];
var gVolume=0.2;
var index=0;
$(function(){
document.getElementById('msg').innerHTML="Loading Playlist";
google.script.run
.withSuccessHandler(function(Obj){
selectionList=Obj.playlist;
console.log(Obj.playlist);
document.getElementById('list').innerHTML=Obj.html;
google.script.run
.withSuccessHandler(function(fObj){
$('#audio1').attr('src',fObj.uri);
var audio=document.getElementById("audio1");
audio.volume=gVolume;
audio.onended=function() {
document.getElementById('status').innerHTML='Ended...';
playnext();
}
var msg=document.getElementById('msg');
msg.innerHTML="Click play to begin playlist. Additional selections will begin automatically";
audio.onplay=function() {
document.getElementById('msg').innerHTML='Playing: ' + selectionList[index-1];
document.getElementById('status').innerHTML='Playing...';
document.getElementById('skipbtn').disabled=false;
}
audio.onvolumechange=function(){
gVolume=audio.volume;
}
})
.convMediaToDataUri(selectionList[index++]);
})
.getPlaylist();
});
function playnext() {
if(index<selectionList.length) {
document.getElementById('status').innerHTML='Loading...';
document.getElementById('msg').innerHTML='Next Selection: ' + selectionList[index];
google.script.run
.withSuccessHandler(function(fObj){
$('#audio1').attr('src',fObj.uri);
var audio=document.getElementById('audio1');
audio.volume=gVolume;
audio.play();
})
.convMediaToDataUri(selectionList[index++]);
}else{
document.getElementById('status').innerHTML='Playlist Complete';
document.getElementById('msg').innerHTML='';
document.getElementById('cntrls').innerHTML='<input type="button" value="Replay Playlist" onClick="replayPlaylist()" />';
}
}
function replayPlaylist() {
index=0;
document.getElementById('cntrls').innerHTML='';
playnext();
}
function skip() {
var audio=document.getElementById('audio1');
document.getElementById('skipbtn').disabled=true;
audio.pause();
playnext();
}
function playThis(idx) {
index=idx;
var audio=document.getElementById('audio1');
//audio.pause();
playnext();
}
</script>
<body>
<div id="msg"></div>
<audio controls id="audio1" src=""></audio><br />
<div id="status"></div>
<div><input type="button" id="skipbtn" value="Skip" onClick="skip()" disabled /></div>
<div id="cntrls"></div>
<div id="list"></div>
</body>
</html>
Admittedly, the transition is a little rough but I didn't put that much effort into the modification so perhaps you can smooth it out a little. Just run launchMusicDiaog()
to get it going. There's also a doGet() in there for the webapp.