Skip to content

Archives

LAME MP3 encoder

Installing LAME on Hostinger

Download the linux binary from Github (yes, it's outdated, but it's the only one I could find). Unzip it and upload the file to Hostinger (into the tools directory, which is in the $PATH), then run chmod +x lame to make it executable.

Convert MegaVoice audio files to the proper encoding

lame -m m -b 128 [input] [output] 
(The -m flag is for mono audio, and -b is bitrate).

On Windows, to do all MP3 files in a folder using CMD, use this (create the "new" subfolder first):

for %f in ("*.mp3") do "H:\Portable Apps\LAME_MP3\lame.exe" -m m -b 128 "%f" "new\%~nf.mp3"

Save bandwidth when finalizing Audio Courses in Admin

Serve the heavy MP3 files from your local device via caddy server:

cd "H:\Portable Apps\caddy.exe" 
caddy file-server --listen :8765

Files will be served from the /audio subdir beside the caddy exe. You'll need to mirror the folder structure of the prod server, e.g:

audio/
└── thai/
    └── ROM/
        └── 01 บทที่ 1 บทนำสู่จดหมายฝาก.mp3

The other piece is this Tampermonkey script:

Tampermonkey script
// ==UserScript==
// @name         Use local SGC audio early
// @namespace    local-sgc-audio
// @version      2026-07-22
// @author       You
// @match        https://courses.shepherdsglobal.org/admin/audio-job/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

  const CDN_PREFIX =
    "https://cdn.shepherdsglobal.org/production/uploads/audio/";

  const LOCAL_PREFIX =
    "http://127.0.0.1:8765/audio/";

  function localize(audio) {
    const src = audio.getAttribute("src");

    if (!src?.startsWith(CDN_PREFIX)) {
      return;
    }

    audio.setAttribute(
      "src",
      LOCAL_PREFIX + src.slice(CDN_PREFIX.length)
    );

    //audio.setAttribute("preload", "metadata");
  }

  new MutationObserver(mutations => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (!(node instanceof Element)) {
          continue;
        }

        if (node.matches("audio[src]")) {
          localize(node);
        }

        node.querySelectorAll?.("audio[src]").forEach(localize);
      }
    }
  }).observe(document.documentElement, {
    childList: true,
    subtree: true
  });
})();