TrunkRecorder Userscripts

Tampermonkey Userscripts for Trunk Recorder

Enhancing Trunk Recorder with Tampermonkey Userscripts

I prompted ChatGPT’s new model o1-preview into making a working Userscript to create a playback speed button

and another to automatically expand all new calls in the case that you use transcription.

Installation

You will first need to install the Tampermonkey extension for your browser of choice. Firefox and Chrome are supported,

and probably Opera and others as well.

This also works on Firefox for Mobile.

Configuration

You will need to edit the scripts to point to your own instance of Trunk Recorder. Mine is at 192.168.124.207;

The part that says: // @match *://192.168.124.207 is where you insert the address to the webserver

Scripts

Script #1 (updated, and working with firefox too): Implementing Speed Changing for Recordings

Playback Speed Button

Click to reveal the script code

// ==UserScript==
// @name         Trunk Recorder Dynamic Playback Speed Control with Scrollable Menu
// @namespace    http://tampermonkey.net/
// @version      1.9
// @description  Adds a dynamic speed control button with a scrollable menu from 0.25x to 10x increments.
// @author
// @match        *://192.168.124.207/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Default playback speed set to 1x
    let savedSpeed = parseFloat(localStorage.getItem('playbackSpeed') || '1.0');

    // Function to set playback speed on an audio element
    function setAudioPlaybackSpeed(audio, speed) {
        if (audio) {
            audio.playbackRate = speed;
            audio.addEventListener('loadedmetadata', () => {
                audio.playbackRate = speed;
            });Auto Expand CallText
        }
    }

    // Apply the playback speed to all audio elements
    function applyPlaybackSpeedToAll(speed) {
        const audioElements = document.querySelectorAll('audio');
        audioElements.forEach(audio => {
            setAudioPlaybackSpeed(audio, speed);
        });
    }

    // Function to dynamically generate speed options based on your logic
    function generateSpeedOptions() {
        const speeds = [];
        for (let i = 0.25; i <= 10; i += 0.1) {
            const rounded = (Math.round(i * 100) / 100).toFixed(2);  // Keep two decimal places
            const displayValue = (rounded % 1 === 0.25 || rounded % 1 === 0.75) ? parseFloat(rounded) : parseFloat(i.toFixed(1));
            speeds.push(displayValue);
        }
        return [...new Set(speeds)]; // Remove duplicates and return unique values
    }

    // Initialize the speed control button
    function initSpeedControl() {
        applyPlaybackSpeedToAll(savedSpeed);

        const targetElement = document.querySelector('#searchcontent .headeritem#playbackcontrols');

        if (!targetElement) {
            console.error('Target element for speed control not found.');
            return;
        }

        const speedControlContainer = document.createElement('div');
        speedControlContainer.id = 'speedcontrolcontainer';
        speedControlContainer.className = 'headeritem';

        const speedButtonWrapper = document.createElement('div');
        speedButtonWrapper.className = 'headeritem searchcheckbox ui-controlgroup ui-controlgroup-horizontal ui-helper-clearfix';
        speedButtonWrapper.setAttribute('role', 'toolbar');

        const speedButton = document.createElement('button');
        speedButton.id = 'speedcontrol';
        speedButton.className = 'ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only';
        speedButton.type = 'button';

        const buttonContent = document.createElement('span');
        buttonContent.className = 'ui-button-text';
        buttonContent.textContent = `${savedSpeed}x`;

        speedButton.appendChild(buttonContent);
        speedButtonWrapper.appendChild(speedButton);

        speedButton.addEventListener('click', (event) => {
            event.stopPropagation();
            showSpeedOptions(speedButton);
        });

        speedControlContainer.appendChild(speedButtonWrapper);
        targetElement.parentNode.insertBefore(speedControlContainer, targetElement.nextSibling);
    }

    // Display speed options
    function showSpeedOptions(button) {
        const existingMenu = document.querySelector('#speed-options-menu');
        if (existingMenu) {
            existingMenu.remove();
            return;
        }

        const menu = document.createElement('div');
        menu.id = 'speed-options-menu';
        menu.style.position = 'absolute';
        menu.style.backgroundColor = '#333';
        menu.style.border = '1px solid #555';
        menu.style.padding = '5px';
        menu.style.zIndex = '10000';
        menu.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)';
        menu.style.fontFamily = 'Arial, sans-serif';
        menu.style.color = '#fff';
        menu.style.maxHeight = '200px'; // Set a fixed height
        menu.style.overflowY = 'scroll'; // Enable vertical scrolling

        const rect = button.getBoundingClientRect();
        menu.style.left = `${rect.left + window.scrollX}px`;
        menu.style.top = `${rect.bottom + window.scrollY}px`;

        const speeds = generateSpeedOptions();
        speeds.forEach(speed => {
            const option = document.createElement('div');
            option.textContent = `${speed}x`;
            option.style.padding = '5px';
            option.style.cursor = 'pointer';

            if (speed === savedSpeed) {
                option.style.fontWeight = 'bold';
                option.style.backgroundColor = '#555';
            }

            option.addEventListener('mouseenter', () => {
                option.style.backgroundColor = '#666';
            });
            option.addEventListener('mouseleave', () => {
                option.style.backgroundColor = speed === savedSpeed ? '#555' : '';
            });

            option.addEventListener('click', (e) => {
                e.stopPropagation();
                savedSpeed = speed;
                localStorage.setItem('playbackSpeed', speed);
                applyPlaybackSpeedToAll(speed);
                button.querySelector('.ui-button-text').textContent = `${speed}x`;
                menu.remove();
            });

            menu.appendChild(option);
        });

        document.body.appendChild(menu);

        function handleClickOutside(event) {
            if (!menu.contains(event.target) && event.target !== button) {
                menu.remove();
                document.removeEventListener('click', handleClickOutside);
            }
        }

        document.addEventListener('click', handleClickOutside);
    }

    // Observe new audio elements in the DOM
    function observeNewAudioElements() {
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.tagName === 'AUDIO') {
                        setAudioPlaybackSpeed(node, savedSpeed);
                    } else if (node.querySelectorAll) {
                        node.querySelectorAll('audio').forEach(audio => {
                            setAudioPlaybackSpeed(audio, savedSpeed);
                        });
                    }
                });
            });
        });

        observer.observe(document.body, { childList: true, subtree: true });
    }

    function init() {
        initSpeedControl();
        applyPlaybackSpeedToAll(savedSpeed);
        observeNewAudioElements();
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        init();
    } else {
        document.addEventListener('DOMContentLoaded', init);
    }

})();

Script #2 (updated v2, faster, much simpler code and works in firefox too): Script for automatically expanding each tab as soon as a transcript is detected:

transcripts

Click to reveal the script code

// ==UserScript== // @name Trunk Recorder Auto Expand CallText Rows (Polling + Event Simulation) // @namespace http://tampermonkey.net/ // @version 1.6 // @description Automatically clicks and expands rows using polling for compatibility with Firefox and Chrome. // @author You // @match ://192.168.124.207/ // @grant none // ==/UserScript==

(function() { ‘use strict’;

// Polls and simulates clicks on CallText elements
function expandCallTextRows() {
    document.querySelectorAll('td.dt-control.CallText:not(.expanded)').forEach((element) => {
        if (!element.closest('tr').nextElementSibling?.querySelector('td[colspan="14"]')) {
            element.click(); // Simulate click
            element.classList.add('expanded'); // Prevent re-clicking
        }
    });
}

// Poll for new rows every 2 seconds
setInterval(expandCallTextRows, 2000);

})();

Have fun!