【问题标题】:Google Script: Play Sound when a specific cell change the ValueGoogle Script:当特定单元格更改值时播放声音
【发布时间】:2016-11-30 06:21:19
【问题描述】:

情况:

Example Spreadsheet

工作表:支持
列:H有如下函数"=IF(D:D>0;IF($B$1>=$G:G;"Call";"In Time");"")"这会根据结果更改值。

问题:

我需要:

  1. 当 H 列中的单元格在“支持”表上更改为“呼叫”时播放声音。
  2. 此函数需要每 5 分钟运行一次。
  3. 是否需要将声音上传到云端硬盘,或者我可以使用来自 URL 的声音吗?

我会感谢任何人可以提供帮助...我看到很多代码但我不太了解。

【问题讨论】:

    标签: javascript audio google-apps-script google-sheets


    【解决方案1】:

    这是一个相当棘手的问题,但可以通过一个定期轮询 H 列的更改的侧边栏来完成。

    代码.gs

    // creates a custom menu when the spreadsheet is opened
    function onOpen() {
      var ui = SpreadsheetApp.getUi()
        .createMenu('Call App')
        .addItem('Open Call Notifier', 'openCallNotifier')
        .addToUi();
    
      // you could also open the call notifier sidebar when the spreadsheet opens
      // if you find that more convenient
      // openCallNotifier();
    }
    
    // opens the sidebar app
    function openCallNotifier() {
      // get the html from the file called "Page.html"
      var html = HtmlService.createHtmlOutputFromFile('Page') 
        .setTitle("Call Notifier");
    
      // open the sidebar
      SpreadsheetApp.getUi()
        .showSidebar(html);
    }
    
    // returns a list of values in column H
    function getColumnH() {
      var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Support");
    
      // get the values in column H and turn the rows into a single values
      return sheet.getRange(1, 8, sheet.getLastRow(), 1).getValues().map(function (row) { return row[0]; });
    }
    

    Page.html

    <!DOCTYPE html>
    <html>
      <head>
        <base target="_top">
      </head>
      <body>
        <p id="message">Checking for calls...</p>
    
        <audio id="call">
          <source src="||a URL is best here||" type="audio/mp3">
          Your browser does not support the audio element.
        </audio>
    
        <script>
        var lastTime = []; // store the last result to track changes
    
        function checkCalls() {
    
          // This calls the "getColumnH" function on the server
          // Then it waits for the results
          // When it gets the results back from the server,
          // it calls the callback function passed into withSuccessHandler
          google.script.run.withSuccessHandler(function (columnH) {
            for (var i = 0; i < columnH.length; i++) {
    
              // if there's a difference and it's a call, notify the user
              if (lastTime[i] !== columnH[i] && columnH[i] === "Call") {
                notify();
              }
            }
    
            // store results for next time
            lastTime = columnH;
    
            console.log(lastTime);
    
            // poll again in x miliseconds
            var x = 1000; // 1 second
            window.setTimeout(checkCalls, x);
          }).getColumnH();
        }
    
        function notify() {
          document.getElementById("call").play();
        }
    
        window.onload = function () {
          checkCalls();
        }
    
        </script>
      </body>
    </html>
    

    一些帮助来源:

    【讨论】:

    • Great is Working... 但我有一个问题,声音结束时侧边栏不清楚。声音结束时可以自动关闭吗?
    • 不,侧边栏需要保持打开状态,以便使用window.setTimeout(checkCalls, x) 继续检查 H 列。也许您可以对侧边栏进行更多样式设置并添加更多功能,以便用户有更多理由保持打开状态。
    • 乔希,我找到了这个函数 google.script.host.close() 但我可以找到使用它的方法...运行声音需要 2 秒...我试图找到5 秒后执行 google.script.host.close() 的方法...这个函数关闭侧边栏
    • 使用在音频播放完毕时触发的事件处理程序:W3 Schools onended
    • 我找到了代码...有没有什么方法可以让音频听起来,即使它不在我打开电子表格的浏览器翻盖中?因为只有当我在打开该电子表格的襟翼时才会发出声音。
    【解决方案2】:

    当我实现给出的主要答案时,递归调用 checkCalls() 最终导致错误(这大部分是正确且非常有用的,所以谢谢!)。

    // 注意:但最初的实现会在一段时间内正常工作——比如 90 分钟——然后崩溃。通常需要 1 秒的调用将需要 300 秒,并且执行将停止。看起来它通过继续递归调用自身而炸毁了堆栈。当移动到一个 check() 调用并正确退出函数时,它就可以工作了。

    运行 JavaScript 的 Chrome 控制台登录,是这样说的: ERR_QUIC_PROTOCOL_ERROR.QUIC_TOO_MANY_RTOS 200

    经过大量调查,我找到了一种更好的方法......不需要递归(因此不会破坏堆栈)。

    删除此行: // window.setTimeout(checkCalls, 500);

    并在脚本末尾使用类似这样的内容:

      // This function returns a Promise that resolves after "ms" Milliseconds
    
            // The current best practice is to create a Promise...
      function timer(ms) {
       return new Promise(res => setTimeout(res, ms));
      }
    
      
      async function loopthis () { // We need to wrap the loop into an async function for the await call (to the Promise) to work.  [From web: "An async function is a function declared with the async keyword. Async functions are instances of the AsyncFunction constructor, and the await keyword is permitted within them. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains."]
        for (var i = 0; i >= 0; i++) {
          console.log('Number of times function has been run: ' + i);
          checkCalls();
          await timer(3000);
        }
      }
    
    
      window.onload = function () {
        loopthis();
      }
    
    </script>
    

    【讨论】:

      猜你喜欢
      • 2020-01-19
      • 1970-01-01
      • 1970-01-01
      • 2020-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-14
      相关资源
      最近更新 更多