Floppy Image Converter
WordPress plugin for async floppy image conversion via Greaseweazle (REST API + status polling + ZIP extraction).
by Alexander Peppe · github.com/alexs-piano-service/floppy-image-conversion-tool · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/alexs-piano-service/floppy-image-conversion-tool/archive/refs/heads/main.zipFloppy Image Converter (WordPress Plugin)
Convert floppy disk image files asynchronously using Greaseweazle from WordPress. This plugin exposes a REST API for conversion jobs, polling status, and downloading outputs.
Features
- Asynchronous conversion jobs using background shell execution
- REST API endpoints for
convertandstatus - Built-in optional ClamAV scan on upload
- Support for many legacy floppy image formats
- Optional ZIP extraction workflow (
7z+zip) - Automatic repair for 720 KB Yamaha/FAT12 images with a blank or omitted first sector before ZIP extraction
- Detected Yamaha E-SEQ to MIDI ZIP option using APS MIDI Prep Tool conversion modules
- Ensoniq EPS/EPS16/ASR EFE import/export via intermediate IMG images
- Ensoniq EPS/EPS16 EDE disk-image import/export via intermediate IMG images
- Responsive, accessible converter form with upload validation, progress, and downloads
- Self-contained submit/polling behavior with recoverable connection errors
- Daily cleanup of old conversion artifacts
Requirements
- WordPress (current supported version)
- PHP with
exec()available - Greaseweazle CLI (
gw) installed on the server (required) 7zandzipCLI tools available for ZIP output- PHP CLI available to the web process for Yamaha/FAT12 copy-protection repair and Ensoniq EFE/EDE conversion; set the
fic_php_cli_pathfilter if needed - Python 3 for optional Yamaha E-SEQ to MIDI ZIP conversion; the needed APS MIDI Prep Tool conversion modules are bundled, and
fic_aps_midi_prep_tool_pathcan point to a newer checkout if needed - Standard shell tools (
find,rm,ps) clamdscan(optional, used when available)
Installation
- Copy this plugin folder into
wp-content/plugins/floppy-image-conversion-tool. - Activate Floppy Image Converter in WordPress admin.
- Ensure server paths for
gwand optionalclamdscanare valid. - Place a shortcode in a page, or call the REST API directly.
Shortcodes
[floppy-converter-form][floppy-image-converter][progress-bar]
[floppy-converter-form] supports:
show_advanced="1"(default)show_advanced="0"
Example:
[floppy-converter-form show_advanced="0"]
Frontend note:
- The plugin handles file selection, basic/advanced options, uploads, polling, errors, and downloads. No theme JavaScript is required.
- The form uses the APS website's white cards, red actions, and Chakra display font when available; body text inherits the theme font. Styles are scoped to the converter.
- When ZIP extraction finds E-SEQ songs and prepares a MIDI ZIP, both original and MIDI downloads appear in the result panel.
- Multiple converter shortcodes can operate independently. The first form keeps its original IDs; later forms receive unique suffixes. Standalone progress bars have unique
fic-progress-*IDs. - Existing inline scripts on the converter page may be removed. The plugin handles submit and advanced-toggle events in the capture phase so those older handlers cannot submit duplicate jobs or override the active fields.
- REST URLs come from WordPress, including subdirectory installations and plain permalinks. Failed status checks can resume the same job without another upload.
REST API
Namespace: floppy/v1
POST /wp-json/floppy/v1/convert
Starts a new conversion job.
Required fields:
file(multipart file upload)out_fmtdiskdef
Example:
curl -X POST "https://example.com/wp-json/floppy/v1/convert" \
-F "file=@/path/to/disk.img" \
-F "out_fmt=zip" \
-F "diskdef=ibm.720"
Typical success response:
{
"job_id": "f7d0f8f0-13cc-4a2b-b750-2f600c1f7f57",
"download_url": "https://example.com/wp-json/floppy/v1/download?job_id=f7d0f8f0-13cc-4a2b-b750-2f600c1f7f57&out_fmt=zip&filename=disk.zip",
"download_filename": "disk.zip"
}
GET /wp-json/floppy/v1/status?job_id=...
Polls job state.
Possible status values:
processingcompleteerror
Typical processing response:
{
"status": "processing",
"message": "STEP 3/6",
"percent": 58,
"step": 3,
"step_total": 6,
"phase": "Extracting files from intermediate image"
}
Typical complete response:
{
"status": "complete",
"job_id": "<job>",
"download_url": "https://example.com/wp-json/floppy/v1/download?job_id=<job>&out_fmt=<fmt>&filename=<clean-name>.<fmt>",
"download_filename": "<clean-name>.<fmt>",
"eseq_midi_available": false
}
When ZIP extraction finds Yamaha E-SEQ files and the optional conversion succeeds, the complete response also includes:
{
"eseq_midi_available": true,
"eseq_midi_download_url": "https://example.com/wp-json/floppy/v1/download?job_id=<job>&out_fmt=zip&filename=<clean-name>-midi.zip&variant=eseq_midi",
"eseq_midi_download_filename": "<clean-name>-midi.zip",
"eseq_midi_count": 7
}
GET /wp-json/floppy/v1/download?job_id=...&out_fmt=...&filename=...
Streams the finished conversion with a sanitized, user-facing filename via
Content-Disposition while keeping UUID-based storage paths on disk.
Add variant=eseq_midi to download the prepared MIDI ZIP when a complete status response reports eseq_midi_available: true.
Typical error response:
{
"status": "error",
"message": "No files extracted from IMG (nothing to zip).",
"log_tail": ["..."]
}
Supported formats
Input formats:
a2r, adf, ads, adm, adl, bin, ctr, d1m, d2m, d4m, d64, d71, d81, d88, dcp, dim, dmk, do, dsd, dsk, ede, edsk, efe, fd, fdi, hdm, hfe, ima, img, imd, ipf, mgt, msa, nfd, nsi, po, raw, sf7, scp, ssd, st, td0, xdf
Output formats:
- All input formats above
zip
Ensoniq EFE notes:
- Uploading an
.efefirst creates a temporary Ensoniq IMG using the selectedensoniq.800orensoniq.1600disk definition, then passes that IMG to Greaseweazle for the requested output. - When
out_fmt=efe, the selected Ensoniq image must contain exactly one exportable file. Useout_fmt=zipfor disks with multiple files. - When
out_fmt=zipanddiskdefisensoniq.800orensoniq.1600, the ZIP contains extracted.efefiles from the Ensoniq EPS/EPS16/ASR filesystem. - Other ZIP jobs continue to use the generic archive extraction path via
7z.
Ensoniq EDE notes:
- EDE is a compact Giebler EPS/EPS16 disk-image container, not a single-file EFE container.
- EDE conversion requires
diskdef=ensoniq.800; the ASR 1600K sibling format is EDA. - Older classic EDE headers such as
EPS Diskare supported in addition to newerEPS-16 Diskheaders. - Uploading
.edefirst expands it to a temporary raw Ensoniq IMG, then passes that IMG to Greaseweazle for other output formats. - When
out_fmt=ede, the intermediate IMG is compacted back to EDE after conversion.
Disk definitions:
- Includes a large built-in list (IBM, Amiga, Acorn, ZX, etc.) in
includes/config.phpviafic_allowed_diskdefs().
Configuration
Override executable paths via WordPress filters:
add_filter( 'fic_greaseweazle_cli_path', function () {
return '/opt/greaseweazle/.venv/bin/gw';
} );
add_filter( 'fic_clamdscan_path', function () {
return '/usr/bin/clamdscan';
} );
add_filter( 'fic_php_cli_path', function () {
return '/usr/bin/php';
} );
add_filter( 'fic_python_cli_path', function () {
return '/usr/bin/python3';
} );
add_filter( 'fic_aps_midi_prep_tool_path', function () {
return '/home/peppe/Projects/aps-midi-prep-tool';
} );
Storage, cleanup, and job lifetime
- Converted files/logs are stored under:
wp-content/uploads/floppy-convert/ - A cleanup cron runs daily and removes artifacts older than one week
- Job metadata is stored as a WordPress transient for one hour
Security notes
- REST endpoints currently use public permission callbacks (
__return_true). - If you need restricted access, replace with an auth/capability check.
- Shell arguments are escaped and error messages are sanitized before API output.
Project structure
floppy-image-converter.phpplugin bootstrap and hooksincludes/config.phpformats, diskdefs, defaultsincludes/helpers.phppath/process helpers and filtersincludes/conversion.phpupload handling and conversion pipelineincludes/status.phplog parsing and status responsesincludes/rest.phpREST route registrationincludes/frontend.phpshortcodes and template renderingincludes/cleanup.phpscheduled artifact cleanuptemplates/converter-form.phpshortcode HTML templateassets/floppy-converter.cssshortcode CSSassets/floppy-converter.jsconverter form controller and Yamaha E-SEQ download handlingassets/repair-copy-protected-yamaha-720k.phpcopy-protected Yamaha/FAT12 repair helperassets/convert-yamaha-eseq-to-midi.pyZIP-content Yamaha E-SEQ to MIDI helper using bundled APS MIDI Prep Tool importsassets/aps_midi_prep_tool_app/bundled APS MIDI Prep Tool E-SEQ conversion modulesassets/create-ensoniq-img-from-efe.phpEnsoniq EFE to IMG helperassets/convert-ensoniq-ede.phpEnsoniq EDE/IMG conversion helperassets/extract-ensoniq-efe.phpEnsoniq EPS/EPS16/ASR EFE extraction helper
Development
PHP syntax check:
php -l floppy-image-converter.php
for f in includes/*.php templates/*.php; do php -l "$f"; done
node --check assets/floppy-converter.js
Browser regression checks (Python with playwright and its Chromium browser installed):
python tests/test-frontend.py
The browser test renders the PHP shortcode with isolated WordPress stubs and mocks all conversion requests. It checks validation, repeated conversions, advanced selections, multiple instances, polling recovery, MIDI download names, keyboard focus, and mobile overflow. Set FIC_LEGACY_SCRIPT to a saved inline page script to also verify compatibility with an older site integration. These checks do not exercise the Greaseweazle backend.