Skip to content

Expand sound share URL functionality for human readable links via HTML code + forum links #2123

@qubodup

Description

@qubodup

The SHARE button on sound pages is pretty good:

Image

Example page: https://freesound.org/s/674568/

But to make easy linking to sounds with title/license/name attribution as needed in freesound descriptions and forum posts, this can be improved.

This allowed me to create vastly more aesthetically pleasing sound descriptions and forum posts in the past months, avoiding ugly long URLs that are displeasing to read to humans.

For example in https://freesound.org/people/qubodup/sounds/854382/

Or should I say: for example in Big Slow Heavy Drone Hovering Idle Loop (cc0) by qubodup?

Image

A userscript creates such comfy links for me:

// ==UserScript==
// @name         Freesound Credits & Filenames
// @namespace    http://tampermonkey.net/
// @version      2026-05-17
// @description  comfy ui
// @author       You
// @match https://freesound.org/people/*/sounds/*/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=freesound.org
// @grant        none
// ==/UserScript==

// GenAI instructions:
// - Keep these and other comments intact unless they need to be updated
// - Never use unicode icons for decorating comments or logs
// - Do not update the title with version relevant notes, do update the version number
// - Never write comments that are only relevant to a change. Always write generally useful comments that are timeless
// - I'm not interested in occupational therapy, whenever possible give me just one block to replace or write the whole script so I don't have to do chores for the GenAI

(function() {
    'use strict';

    // fail
    function fail(reason) { console.log('[Freesound Credits & Filenames] stopped:', reason); return; }

    // not on edit pages
    if (window.location.pathname.endsWith('/edit/')) return;

    const player = document.getElementsByClassName('bw-player')[0];
    if (!player) { fail('player element not found'); return; }

    // get id
    var id = player.getAttribute('data-sound-id');

    // get license
    const licenseEl = document.querySelector('body > div.bw-page > div > div > div > div.col-md-4.col-extra-left-padding-large-md > div.bw-sound__sidebar > div.v-spacing-top-5.middle > a');
    if (!licenseEl) { fail('license element not found'); return; }

    var license = licenseEl.textContent;
    const licenseURL = licenseEl.getAttribute('href');

    if ( license == 'Creative Commons 0') { license = 'cc0'; }
    if ( license == 'Attribution NonCommercial 4.0') { license = 'nc'; }
    if ( license == 'Attribution 4.0') { license = 'by'; }
    if ( license == 'Sampling+') { license = 's+'; }

    // get filename
    var title = player.getAttribute('data-title');

    // get username
    const userEl = document.querySelector('body > div.bw-page > div > div > div > div.col-md-8 > div.bw-sound-page__information.v-spacing-top-5.word-wrap-break-word > div.middle.bw-sound-page__user.v-spacing-top-5 > div.h-spacing-left-1.ellipsis > a');

    if (!userEl) { fail('user element not found'); return; }

    var user = userEl.textContent;

    // filename
    var baseName = license + '__' + user + '__' + title + '__' + id;
    baseName = baseName.replace(/[/\\?%*:|"<>]/g, '_').replace(/ /g, '-');

    // credit info
    const ogUrl = document.querySelector('meta[property="og:url"]')?.content;
    const ogUsr = document.querySelector('meta[property="og:audio:artist"]')?.content;
    const ogSnd = document.querySelector('meta[property="og:audio:title"]')?.content;
    const ogUsrUrl = ogUrl?.match(/https:\/\/freesound\.org\/people\/[^/]+\//)?.[0];

    const encoded = str =>
        str.replace(/&/g, "&amp;")
           .replace(/"/g, "&quot;")
           .replace(/</g, "&lt;")
           .replace(/>/g, "&gt;");

    var htmls = [
        `<a target="_blank" href="${ogUrl}">${ogSnd}</a> (<a target="_blank" href="${licenseURL}">${license}</a>) by <a target="_blank" href="${ogUsrUrl}">${ogUsr}</a>`,
        `<a target="_blank" href="${ogUrl}">${ogSnd}</a> (<a target="_blank" href="${licenseURL}">${license}</a>)`,
        `<a target="_blank" href="${ogUrl}">${ogSnd}</a> by <a target="_blank" href="${ogUsrUrl}">${ogUsr}</a>`,
        `<a target="_blank" href="${ogUrl}">${ogSnd}</a>`
    ];

    var table = '<table class="fs-credit-main-table" style="width: 100%; border-collapse: collapse;">';

    // filename row
    table += '<tr>';
    table += `<td style="padding: 4px;">
        <input value="${baseName}" style="width: 100%; border: 1px solid #a2a2b4; padding: 8px; font-family: monospace;" onclick="this.select()" readonly>
    </td>`;
    table += '</tr>';

    // credit rows
    for (let i = 0; i < htmls.length; i++) {

        table += '<tr>';

        table += `<td style="padding: 4px; vertical-align: middle;">
            <div class="freesound-credit" contenteditable="true"
                style="cursor: text; padding: 8px; border: 1px solid #a2a2b4; background: #fff; color: #000; font-family: sans-serif; font-size: 14px;">
                ${htmls[i]}
            </div>
        </td>`;

        table += `<td style="padding: 4px;">
            <input value="${encoded(htmls[i])}" style="width: 100%; border: 1px solid #a2a2b4; padding: 8px;" onclick="this.select()">
        </td>`;

        table += '</tr>';
    }

    table += '</table>';

    document.querySelector(
        'body > div.bw-page > div > div > div > div.col-md-8 > div:nth-child(1)'
    ).insertAdjacentHTML('beforeend', table);

    const STORAGE_KEY = 'fs_credit_table_hidden';

    const mainTable = document.querySelector('.fs-credit-main-table');

    if (!mainTable) {
        fail('main table not found');
        return;
    }

    const downloadButton = document.querySelector(
        'body > div.bw-page > div > div > div > div.col-md-4.col-extra-left-padding-large-md > div.bw-sound__sidebar > div.v-spacing-top-5.v-padding-bottom-5 > a'
    );

    if (!downloadButton) {
        fail('download button not found');
        return;
    }

    mainTable.parentElement.style.position = 'relative';
    downloadButton.parentElement.style.position = 'relative';

    const hideButton = document.createElement('div');

    hideButton.textContent = 'hide';

    hideButton.style.position = 'absolute';
    hideButton.style.right = '0';
    hideButton.style.bottom = '-10px';
    hideButton.style.fontSize = '9px';
    hideButton.style.color = '#777';
    hideButton.style.cursor = 'pointer';
    hideButton.style.zIndex = '9999';

    const revealArea = document.createElement('div');

    revealArea.style.position = 'absolute';
    revealArea.style.left = '0';
    revealArea.style.top = downloadButton.offsetTop + 'px';
    revealArea.style.width = '20%';
    revealArea.style.height = downloadButton.offsetHeight + 'px';
    revealArea.style.zIndex = '1000';
    revealArea.style.cursor = 'pointer';

    revealArea.addEventListener('mouseenter', function() {

        revealArea.style.background = '#000';
        revealArea.style.color = '#fff';
        revealArea.style.display = 'flex';
        revealArea.style.alignItems = 'center';
        revealArea.style.justifyContent = 'center';
        revealArea.style.textAlign = 'center';
        revealArea.style.fontSize = '11px';
        revealArea.style.padding = '2px';
        revealArea.textContent = 'show';

    });

    revealArea.addEventListener('mouseleave', function() {

        revealArea.style.background = 'transparent';
        revealArea.textContent = '';

    });

    revealArea.addEventListener('click', function(e) {

        e.preventDefault();
        e.stopPropagation();

        localStorage.setItem(STORAGE_KEY, '0');

        mainTable.style.display = '';
        hideButton.style.display = '';

        revealArea.style.display = 'none';

    });

    hideButton.addEventListener('click', function(e) {

        e.preventDefault();
        e.stopPropagation();

        localStorage.setItem(STORAGE_KEY, '1');

        mainTable.style.display = 'none';
        hideButton.style.display = 'none';

        revealArea.style.display = '';

    });

    mainTable.parentElement.appendChild(hideButton);
    downloadButton.parentElement.appendChild(revealArea);

    const hidden = localStorage.getItem(STORAGE_KEY) === '1';

    if (hidden) {

        mainTable.style.display = 'none';
        hideButton.style.display = 'none';

        revealArea.style.display = '';

    } else {

        revealArea.style.display = 'none';

    }

    document.querySelectorAll('.freesound-credit').forEach(el => {

        el.addEventListener('click', function() {

            var range = document.createRange();

            range.selectNodeContents(this);

            var sel = window.getSelection();

            sel.removeAllRanges();
            sel.addRange(range);

        });

    });

    document.querySelectorAll('.freesound-credit').forEach(el => {

        el.addEventListener('copy', function(e) {

            e.preventDefault();

            const selection = window.getSelection();

            const container = document.createElement('div');

            container.appendChild(selection.getRangeAt(0).cloneContents());

            const cleanHTML = container.innerHTML;

            e.clipboardData.setData('text/plain', selection.toString());
            e.clipboardData.setData('text/html', cleanHTML);

        });

    });

})();

The UI:

Image

This could be put in the share sidebar area. The left side should be right aligned so users can easily see which version includes license/name.

I would also recommend local storage to be used to remember site wide whether the share area should be permanently unfolded. I for one have it open most of the time.

Forum

I recommend the same for forum threads. Human readable thread links like the following instead of ugly URLs:

Image

This is achieved with this userscript:

// ==UserScript==
// @name         Freesound Forum Link Snippet
// @namespace    http://tampermonkey.net/
// @version      2026-04-26
// @description  Adds self-selecting HTML snippet + preview for forum threads
// @match        https://freesound.org/forum/*/*/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=freesound.org
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const titleEl = document.querySelector('h3');
    if (!titleEl) return;

    const title = titleEl.textContent.trim();
    const url = document.querySelector('meta[property="og:url"]')?.content || window.location.href;

    // extract + normalize date
    const metaText = document.querySelector('.text-grey.v-spacing-7')?.textContent || '';
    let date = '';
    const match = metaText.match(/Started\s+([A-Za-z]+\s+\d{1,2}\w{2},\s+\d{4})/);
    if (match) {
        const d = new Date(match[1]);
        if (!isNaN(d)) {
            date = d.toLocaleDateString('en-US', {
                year: 'numeric',
                month: 'short',
                day: 'numeric'
            });
        } else {
            date = match[1];
        }
    }

    const encoded = str =>
        str.replace(/&/g, "&amp;")
           .replace(/"/g, "&quot;")
           .replace(/</g, "&lt;")
           .replace(/>/g, "&gt;");

    // DATE OUTSIDE LINK
    const html = date
        ? `<a target="_blank" href="${url}">${title}</a> (${date})`
        : `<a target="_blank" href="${url}">${title}</a>`;

    const plain = date
        ? `${title} (${date}) - ${url}`
        : `${title} - ${url}`;

    const box = document.createElement('div');
    box.style.margin = '10px 0';

    box.innerHTML = `
        <table style="width:100%; border-collapse:collapse;">
            <tr>
                <td style="padding:4px;">
                    <div class="fs-preview"
                         style="cursor:text; padding:8px; border:1px solid #a2a2b4; background:#fff; color:#000;">
                        ${html}
                    </div>
                </td>
                <td style="padding:4px;">
                    <input value="${encoded(html)}"
                           style="width:100%; border:1px solid #a2a2b4; padding:8px; font-family:monospace;"
                           readonly onclick="this.select()">
                </td>
            </tr>
            <tr>
                <td colspan="2" style="padding:4px;">
                    <input value="${plain}"
                           style="width:100%; border:1px solid #a2a2b4; padding:8px;"
                           readonly onclick="this.select()">
                </td>
            </tr>
        </table>
    `;

    // INSERT AT TOP (before header block)
    const container = titleEl.closest('.col-12.no-paddings.v-spacing-5');
    if (container) {
        container.insertBefore(box, container.firstChild);
    }

    // select-on-click + clean copy
    box.querySelectorAll('.fs-preview').forEach(el => {
        el.addEventListener('click', () => {
            const range = document.createRange();
            range.selectNodeContents(el);
            const sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        });

        el.addEventListener('copy', (e) => {
            e.preventDefault();
            const selection = window.getSelection();
            const container = document.createElement('div');
            container.appendChild(selection.getRangeAt(0).cloneContents());

            e.clipboardData.setData('text/plain', selection.toString());
            e.clipboardData.setData('text/html', container.innerHTML);
        });
    });

})();

which provides an easy to copy snippet:

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions