Shared engine for the NoMercy native player libraries. Kotlin Multiplatform sibling of @nomercy-entertainment/nomercy-player-core.
  • Kotlin 97.1%
  • Shell 1.5%
  • Swift 0.7%
  • Python 0.4%
  • Dockerfile 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Stoney_Eagle 8c348ec72b feat(equalizer): the equaliser announces what it just did
EqualizerEvents has been declared since the port began -- BandChanged,
PresetChanged, Ready, Changed, each with a payload and a doc comment explaining
when to use which -- with ZERO references anywhere in the library. No key, no
emit, no listener. A payload type nothing ever sends is a contract nobody can
hold.

The consequence is not abstract: a panel can only show the changes it made
itself, so a preset applied from a remote, a restored session, or a second view
leaves every slider displaying the old curve. That is what the web's
EqualizerPanel is built on -- band:changed and preset:changed -- and porting it
found the gap.

Plugin.emit is now silent before registration rather than fatal. A plugin's
setters are callable on a plugin nobody has registered -- this library's own
equaliser tests construct one and drag a band -- and an announcement with no
host to hear it is not an error. Adding the emits turned four green tests red
until this went in, which is the proof the case is real. Reads, options and
storage still throw: asking a detached plugin for its host's state is a question
with no honest answer, and that is a different thing from telling nobody.

Proven by removing the emit: three of the new assertions fail, and the third
covers the unregistered case that would otherwise regress in silence.
2026-08-10 02:35:52 +02:00
.github/workflows ci: the workflow declares env once, so the file parses at all 2026-08-09 20:21:32 +02:00
api feat(equalizer): the equaliser announces what it just did 2026-08-10 02:35:52 +02:00
config/detekt refactor(natives): the desktop engine talks to libVLC itself 2026-08-01 20:15:44 +02:00
conformance-kit fix(events): a listener that launches work must not take the bus down with it 2026-08-02 22:40:17 +02:00
contract feat(conformance): the type surface is measured and ratcheted, not asserted 2026-08-08 20:46:45 +02:00
detekt-player feat(cues): CueParser and CueTracker carry their format's payload, not a flattened string 2026-08-01 16:32:33 +02:00
gradle refactor(natives): the desktop engine talks to libVLC itself 2026-08-01 20:15:44 +02:00
natives fix(natives): the windows libass payload is one that loads on a clean machine 2026-08-09 21:51:34 +02:00
scenarios chore(contract): track the web trio at 2.0.4, proven by the scenario replay 2026-08-06 00:31:06 +02:00
scripts feat(events): the whole base event map, generated from the contract 2026-07-25 23:51:15 +02:00
Sources/NoMercyPlayerDevTools feat(devtools): the firehose, elevated to a tool 2026-07-28 18:26:21 +02:00
src feat(equalizer): the equaliser announces what it just did 2026-08-10 02:35:52 +02:00
swift-conformance fix(apple): a sealed interface's cases flatten, they do not nest 2026-08-09 03:34:21 +02:00
templates/plugin docs(testing): the stand-in needs the test scheduler when a plugin launches 2026-07-28 20:32:57 +02:00
testing fix(mpv): a stream that cannot be opened says so instead of loading forever 2026-08-09 06:00:03 +02:00
Tests/NoMercyPlayerDevToolsTests feat(devtools): the firehose, elevated to a tool 2026-07-28 18:26:21 +02:00
tools fix(payload): the macOS payload is named the same by both files that name it 2026-08-09 22:37:21 +02:00
ui-compose fix(devtools): a muted event still counts, so a quiet log is not a silent player 2026-08-04 05:25:40 +02:00
.gitattributes fix(conformance): the fixtures are checked, and the check can actually fail 2026-07-28 13:58:00 +02:00
.gitignore feat(core): scaffold the KMP library with every gate proven 2026-07-25 19:33:43 +02:00
build.gradle.kts fix(payload): gh is found by path, like brew and gtar before it 2026-08-09 23:00:20 +02:00
CHANGELOG.md refactor(natives): the desktop engine talks to libVLC itself 2026-08-01 20:15:44 +02:00
check.sh feat(templates): a plugin to copy, and the rule that was guarding nothing 2026-07-28 18:42:38 +02:00
contract.lock feat(conformance): the type surface is measured and ratcheted, not asserted 2026-08-08 20:46:45 +02:00
gradle.properties chore(version): the trio is v0, not a release candidate for the web's v2 2026-08-06 02:15:03 +02:00
gradlew fix(build): mark gradlew executable in the index 2026-07-25 19:35:14 +02:00
gradlew.bat feat(core): scaffold the KMP library with every gate proven 2026-07-25 19:33:43 +02:00
LICENSE feat(core): scaffold the KMP library with every gate proven 2026-07-25 19:33:43 +02:00
Package.swift feat(devtools): the firehose, elevated to a tool 2026-07-28 18:26:21 +02:00
README.md feat(core): scaffold the KMP library with every gate proven 2026-07-25 19:33:43 +02:00
scenarios.lock chore(contract): track the web trio at 2.0.4, proven by the scenario replay 2026-08-06 00:31:06 +02:00
settings.gradle.kts feat(templates): a plugin to copy, and the rule that was guarding nothing 2026-07-28 18:42:38 +02:00
THIRD_PARTY_NOTICES.md refactor(natives): the desktop engine talks to libVLC itself 2026-08-01 20:15:44 +02:00

nomercy-player-core-kmp

The shared engine behind the NoMercy native players. It holds the parts that have nothing to do with a screen or a decoder: the event system, playback state, the plugin runtime, and the ports that platform engines plug into.

This is the Kotlin Multiplatform sibling of @nomercy-entertainment/nomercy-player-core. The web package is the specification. Where the two disagree, the web package is right and this one has a bug.

Status

Scaffold. The build, the publishing pipeline, and the gates are in place and proven; the engine itself is being written on top of them. Nothing is published to Maven Central yet.

Targets

Target Consumed by
androidTarget the Android phone and TV app
jvm the desktop client and the test harness
iosArm64, iosSimulatorArm64, iosX64 the iOS app, through NoMercyPlayerCore.xcframework
tvosArm64, tvosSimulatorArm64 the tvOS app, through the same xcframework

Building

You need JDK 21 and an Android SDK. Everything except linking Apple frameworks builds on any host, including Windows and Linux, because Kotlin/Native compiles klibs without Apple hardware.

./gradlew build

That runs the JVM and Android tests, detekt, and both API checks. Running the Apple unit tests and linking the xcframework needs macOS:

./gradlew iosSimulatorArm64Test tvosSimulatorArm64Test
./gradlew assembleNoMercyPlayerCoreXCFramework

Gates

Four things fail the build, and none of them have a baseline file to hide in.

Public API. Every public symbol is recorded in api/. Adding, removing or changing one without updating those files fails apiCheck. Both the JVM surface and the Kotlin/Native klib surface are covered, so an Apple-only change is caught on a Linux runner. Run ./gradlew apiDump when a change to the surface is intended, and read the diff before committing it.

Complexity. config/detekt/detekt.yml sets thresholds below detekt's own defaults. This is a library that people read to learn the plugin API, so every function in it is documentation whether it was meant to be or not.

Versions. KIT_VERSION and CONTRACT_VERSION are generated from gradle.properties at build time and never typed into Kotlin. A hand-maintained version constant and the test asserting it drift together silently, which is exactly how the web contract generator once claimed to describe a release that had already been superseded.

Tag and version agreement. Publishing checks that the git tag matches VERSION_NAME before it uploads anything.

Publishing

Through CI only, triggered by a v* tag, from the macOS runner. A multiplatform publication has to carry every declared target, and the Apple frameworks can only be linked on Apple hardware. Publishing from anywhere else would ship an artifact that quietly has no Apple variants in it.

Licence

Apache 2.0. See LICENSE.