Skip to content

Latest commit

ย 

History

80 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ•น๏ธ Mojo Snap Console

Build Version License

A high-performance Jellyfin plugin for retro game emulation. Runs standard RetroArch WebAssembly cores rendering directly onto a WebGL canvas with full USB/Bluetooth gamepad support. Now with Java ME game support via CheerpJ + FreeJ2ME.


๐Ÿ“‚ Project Structure

mojo-snap-plugin/
โ”œโ”€โ”€ build.ps1                  โ† Full build script (cores + J2ME + C# + packaging)
โ”œโ”€โ”€ installer.iss              โ† Inno Setup Windows installer script
โ”œโ”€โ”€ package.json               โ† ESLint / JS tooling
โ”‚
โ”œโ”€โ”€ src/                       โ† Jellyfin .NET Plugin (C#)
โ”‚   โ”œโ”€โ”€ Api/
โ”‚   โ”‚   โ”œโ”€โ”€ GameApiController.cs   โ† Retro ROM + save state API
โ”‚   โ”‚   โ””โ”€โ”€ J2meApiController.cs   โ† Java ME metadata, JAR streaming & save API
โ”‚   โ”œโ”€โ”€ Web/
โ”‚   โ”‚   โ”œโ”€โ”€ play.html          โ† Retro console player (WebGL canvas)
โ”‚   โ”‚   โ”œโ”€โ”€ play.js            โ† RetroArch/WASM boot loader
โ”‚   โ”‚   โ”œโ”€โ”€ j2me.html          โ† Java ME player page
โ”‚   โ”‚   โ””โ”€โ”€ j2me.js            โ† CheerpJ + FreeJ2ME boot loader
โ”‚   โ”œโ”€โ”€ Plugin.cs              โ† Plugin registration
โ”‚   โ”œโ”€โ”€ RomResolver.cs         โ† Jellyfin library item resolver
โ”‚   โ””โ”€โ”€ MojoSnapPlugin.csproj
โ”‚
โ”œโ”€โ”€ shared/                    โ† Common runtime assets served to the browser
โ”‚   โ”œโ”€โ”€ cores/                 โ† RetroArch WASM cores (populated by build.ps1)
โ”‚   โ”œโ”€โ”€ cheerpj/               โ† CheerpJ + FreeJ2ME runtime slot
โ”‚   โ”‚   โ”œโ”€โ”€ loader.js          โ† Optional self-hosted CheerpJ bridge
โ”‚   โ”‚   โ”œโ”€โ”€ freej2me.jar       โ† FreeJ2ME runtime (downloaded by build.ps1)
โ”‚   โ”‚   โ””โ”€โ”€ README.md          โ† Self-hosting instructions
โ”‚   โ”œโ”€โ”€ games/                 โ† Demo / shareware ROMs
โ”‚   โ”œโ”€โ”€ gameplay.js            โ† Core emulator & gamepad logic
โ”‚   โ””โ”€โ”€ logo96.png
โ”‚
โ”œโ”€โ”€ docs/                      โ† GitHub Pages landing page
โ”‚   โ””โ”€โ”€ index.html
โ””โ”€โ”€ tests/
    โ””โ”€โ”€ MojoSnapPlugin.Tests.csproj

๐Ÿ› ๏ธ Build & Install

Prerequisites

Tool Minimum version Notes
.NET SDK 6.0 dotnet --version
Node.js 18+ ESLint / JS tooling
7-Zip any Core extraction (build.ps1 auto-detects)
PowerShell 5.1+ Build runner

1 โ€” Build

Open PowerShell and run:

powershell -ExecutionPolicy Bypass -File build.ps1

The script runs 5 stages:

Stage What happens
1 โ€” Clean Wipes dist/ to ensure a fresh build
2 โ€” RetroArch cores Downloads the nightly Emscripten build from the libretro buildbot and extracts fceumm, snes9x2010, genesis_plus_gx, gambatte, mgba, and ecwolf WASM cores into shared/cores/
3 โ€” FreeJ2ME JAR Downloads the latest freej2me.jar from GitHub Releases into shared/cheerpj/ (skipped if already present)
4 โ€” C# plugin dotnet restore + dotnet build -c Release โ†’ dist/MojoSnapPlugin.dll
5 โ€” Package Copies src/Web/* and shared/ into dist/mojosnap/ and zips everything to MojoSnapPlugin-Release.zip

2 โ€” Install on Jellyfin

  1. Copy dist/MojoSnapPlugin.dll into your Jellyfin plugins/ folder.
  2. Copy dist/mojosnap/ into your Jellyfin web folder, typically:
    • Windows: C:\Program Files\Jellyfin\Server\jellyfin-web\mojosnap\
    • Linux: /usr/share/jellyfin/web/mojosnap/
  3. Restart your Jellyfin server.

Note: If you use the Windows installer (MojoSnap_Setup.exe) these steps are done automatically.


๐ŸŽฎ Supported Systems & Cores

Retro Console Emulation

System Core File Extensions
NES fceumm .nes
SNES snes9x2010 .sfc, .smc
Sega Genesis / Master System / Game Gear genesis_plus_gx .md, .sms, .gg, .bin
Game Boy / Game Boy Color gambatte .gb, .gbc
Game Boy Advance mgba .gba
Wolfenstein 3D ecwolf .pk3, .zip

Java ME Emulation (CheerpJ + FreeJ2ME)

Format Description
.jar Java ME application archive
.jad Java application descriptor (companion .jar auto-resolved)
.zip ZIP-wrapped Java ME archive

Java ME games run entirely in-browser using CheerpJ (browser JVM) and FreeJ2ME (MIDP runtime). No local Java installation required.


โ–ถ๏ธ Playing Games (UI Setup)

Because of Jellyfin 10.9+ security architecture, the C# plugin cannot automatically inject UI elements into the web client. To add a "Play" button to your ROM items, install the community JavaScript Injector plugin:

  1. In your Jellyfin Dashboard, go to Plugins โ†’ Catalog and install Jellyfin JavaScript Injector.
  2. Restart Jellyfin.
  3. Open JavaScript Injector settings and paste the snippet below.
  4. Hard-refresh your browser (Ctrl+F5).
let injectedForId = null;

// Java ME file extensions
const J2ME_EXTS = /\.(jar|jad)$/i;
// Retro console extensions
const RETRO_EXTS = /\.(nes|sfc|smc|md|gba|gb|gbc|sms|gg|bin|zip|pk3|img|cue|iso)$/i;

function checkAndInject() {
    const url = window.location.href;
    if (!url.includes('details?id=')) { injectedForId = null; return; }

    const idMatch = url.match(/id=([a-zA-Z0-9]+)/);
    if (!idMatch) return;
    const id = idMatch[1];
    if (injectedForId === id) return;

    const playBtn = document.querySelector(
        'button[title="Play"], button[aria-label="Play"], .btnPlay, button[data-action="play"]'
    );
    if (!playBtn) return;

    const container = playBtn.parentElement;
    if (container.querySelector('.btnMojoPlay')) return;
    injectedForId = id;

    const apiClient = window.ApiClient;
    if (!apiClient) return;

    apiClient.getItem(apiClient.getCurrentUserId(), id).then(item => {
        if (!item) return;
        const path = item.Path || '';

        let href = null;
        let label = '';
        let color = '';

        if (J2ME_EXTS.test(path)) {
            // Java ME game
            const title = encodeURIComponent(item.Name || '');
            href  = `/web/mojosnap/j2me.html?id=${id}&title=${title}`;
            label = 'โ˜• Play Java Game';
            color = '#c86dd7';
        } else if (RETRO_EXTS.test(path)) {
            // Retro console game
            href  = `/web/mojosnap/play.html?id=${id}`;
            label = '๐ŸŽฎ Play Retro Game';
            color = '#52B54B';
        }

        if (!href) return;

        const btn = document.createElement('button');
        btn.className = playBtn.className + ' btnMojoPlay';
        Object.assign(btn.style, {
            backgroundColor: color,
            color: '#fff',
            marginLeft: '10px',
            border: 'none',
            borderRadius: '5px',
            padding: '8px 16px',
            cursor: 'pointer',
            fontWeight: 'bold'
        });
        btn.innerHTML = label;
        btn.onclick = e => { e.preventDefault(); e.stopPropagation(); window.location.href = href; };
        container.appendChild(btn);

    }).catch(err => console.warn('[MojoSnap] Error fetching item:', err));
}

setTimeout(checkAndInject, 500);
setTimeout(checkAndInject, 1500);
new MutationObserver(checkAndInject).observe(document.body, { childList: true, subtree: true });

When you open a ROM or Java ME game in your Jellyfin library a coloured Play button will appear:

  • ๐ŸŸข Play Retro Game โ€” for console ROMs
  • ๐ŸŸฃ Play Java Game โ€” for .jar/.jad files

๐Ÿ”„ CI/CD โ€” GitHub Actions

The workflow (.github/workflows/build.yml) runs on every push and pull request to main:

push / pull_request
    โ”‚
    โ”œโ”€โ”€ lint (ubuntu-latest)       โ† ESLint on shared/gameplay.js, src/Web/*.js
    โ”‚
    โ””โ”€โ”€ build (windows-latest)     โ† depends on lint
            โ”œโ”€โ”€ dotnet restore
            โ”œโ”€โ”€ dotnet test
            โ”œโ”€โ”€ build.ps1 (cores + FreeJ2ME + C# + package)
            โ”œโ”€โ”€ Inno Setup โ†’ MojoSnap_Setup.exe
            โ””โ”€โ”€ Upload artifacts:
                    MojoSnapPlugin-dll
                    MojoSnapPlugin-release-zip
                    MojoSnapPlugin-installer

Artifacts are available for download from the Actions tab of the GitHub repository after each successful build.


๐Ÿšง Future Development

# Feature Status
1 Network Service Discovery (mDNS) โ€” Virtual gamepad via phone using mDNS + binary WebSocket protocol Planned
2 Settings & Controls Overlay โ€” EmulatorJS-style toolbar: play/pause, volume, save states, video/audio/hardware settings, controller remapping, cheats, context menu Planned
3 DOS Emulation โ€” dosbox_pure core for DOS games via .zip archives Planned
4 Java ME Emulation โ€” CheerpJ + FreeJ2ME for .jar/.jad games โœ… Implemented

License

Distributed under the GNU Lesser General Public License v2.1. See LICENSE for more information.

About

Play your local retro games directly in your browser with physical gamepad mapping.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages