MultiFunPlayer v1.33.5 - Multi-axis funscript player with SLR & FapTap support

Oh, yea that one was my fault, it was fixed in v1.33.0. Basically MFP tried to grab a handle to the potplayer window before it was shown. So it was reading data from invalid window.
Btw you don’t need start a video before connecting, just make sure the window is open.

Okay, thanks for your help! :wink:

MultiFunPlayer v1.33.5:

Download: timed patreon only exclusive
Patreon build: https://www.patreon.com/posts/122741611

1.33.5

  • Fix possible crash when using null to target all axes
  • Fix duplicate axis clear/reload actions
  • Fix Script::SkipGap action ignoring target axis
  • Fix Axis::Script::Reload and Axis::LinkAxis::Set actions not blocking until script reload is finished
  • Fire PostScriptSearchMessage before applying the scripts to allow plugins to modify the result

1.33.4

  • Support script invert and media playback speed when using PolledUpdate
  • Add ability to disable assigned shortcut actions without having to remove them
  • Add ability to search for local scripts from plugins
  • Add ability to load scripts from shortcuts actions
  • Improve randomness of random motion generation
  • Improve performance of heatmap generation
  • Change default Serial and UDP output settings to improve smoothness by adding some “additional elapsed time”
  • Fallback to default device when selected device has no axes
  • Remove CreateView from plugins and only load view from .xaml files
  • Fix Buttplug output PolledUpdate ignoring axis settings Enabled state
  • Fix broken interpolation preview
  • Fix exception when trying to schedule shortcut actions after disposing
  • Fix crash when disposing a plugin with registered actions
  • Fix crash when plugin throws exception during initializing or disposing
  • Fix crash when selected device is not found
  • Fix Axis::InvertValue actions migration
  • Fix cloned items in UI when using “move up”/“move down” buttons
  • Fix ability to set device axis name longer than 2 characters

1.33.3

  • Fix TCode commands sent without new line when using additional elapsed time
  • Disable Control-flow Enforcement Technology (CET) Shadow Stack, it was enabled by default in .net 9 but causes crashes on some systems

1.33.2

Additional patreon only changelog:

  • Fix FapTap repository not loading scripts for some videos

Changelog:

  • Add ability to stretch TCode move duration, can help with micro stutters caused by jitter
  • Add RawInput/XInput logging toggles
  • Add draggable separator between assigned actions and action search
  • Add ability to invoke shortcuts from plugins
  • Allow script file drag-drop on axis tabs
  • Allow drag-dropping multiple script files
  • Fix script out of sync when using PotPlayer
  • Fix PotPlayer media path containing garbage data in some cases

1.33.1

  • Improve Web window UI
  • Add support for F11 and video fullscreen in Web window
  • Close Web window if initialization fails
  • Show WebView2 runtime download dialog if it’s not installed
  • Allow changing playback speed of Web window player from MFP
  • Fix instant device movement due to invalid PolledUpdate events
  • Fix Web source start page not allowing urls with custom ports
  • Fix crash when calculating heatmap with negative keyframe positions
  • Fix crash when receiving messages in UDP output after disposing
  • Add FixedUpdate and PolledUpdate helpers for plugins
  • Add dynamic KeyframeCollection which allows live streaming of keyframes

1.33.0

Additional patreon only changelog:

  • Add FapTap script repository - works with Web media source
  • Fix scripts stored on VR headset not loading via SLR interactive api
  • Fix SLR login code not opening browser

Changelog:

  • Add Web media source - opens a custom Edge browser which sends playback information to MFP from a video element on the page
  • Add option to split axes in heatmap preview (#198)
  • Add ability to configure main thread update rate (#176) - can be used to lower CPU usage
  • Add support for reading and writing PotPlayer playback speed - requires 2501xx beta or later
  • Make looping optional in script motion provider
  • Aggregate plugin file watcher events and queue compile only once
  • Optimize axis state locking
  • Fix unable to receive data from auto-started PotPlayer
  • Fix OFS source PathAndQuery not set after changing ip or port (#203)
  • Fix first plugin #r reference not found if source file contains BOM

If you like what I’m doing, please consider supporting me on Patreon
https://www.patreon.com/yoooi

1 Like

Hello! I have a question: I would like to make the playback behavior of scripts in MFP work like that of ScriptPlayer. Sorry if this is unclear. When I play a script in MFP, if it’s a continuous script running from 0 to 99, it moves like 0, 10, 20… so I’m not very used to the smoothing function. I’d like to turn off this smoothing function—could you tell me how to do that?

Also, is there anything I need to configure in MFP to make the script playback behave like it does in ScriptPlayer?

MFP v.1.33.0

Do you mean smoothing like when you play/pause the video the device smoothly ramps up the motion? Or do you mean smoothing as in how MFP sends multiple small steps instead of one long step from 0 to 99?

To make MFP behave like ScriptPlayer the most you would have to change from FixedUpdate to PooledUpdate via the clock button in the bottom right. You can only do that while your serial/udp output is disconnected.

But note that PooledUpdate is meant for devices that can’t handle the amount of updates and it basically one supports script playback, most of MFP features will be ignored. So if you have OSR/SSR1 I would advice against it unless you have some specific issue with FixedUpdate.

1 Like

Both types of smoothing! One that makes the device start moving smoothly when the video is played/paused, and one that sends multiple small steps instead of a long step from 0 to 99!

I’m sorry to ask again, but is it okay to leave these settings as they are without changing anything?

Then switch to PooledUpdate.

If you switch to PooledUpdate only invert script and speed limit work from those settings, others are ignored.

Running 1.31.3:
The MFP UI states it connects to Plex HTPC, then immediately disconnects. Behavior (and log) repeats with Auto-connect on.

This feels slightly different than most of the plex errors I’ve seen in the thread; MFP is definitely able to at least say it’s connected to the Plex HTPC client. I haven’t been able to track down a ‘listen on this port’ option in plex, but totally possible I’ve missed it.

Tried a few different URLs in a browser just to see what would happen:
http://127.0.0.1:32400
sends me to a Plex login screen
http://127.0.0.1:32400/?X-Plex-Token=myactualplextoken
gives me an xml, seems normal
http://127.0.0.1:32400/player/timeline/poll?wait=1&commandID=1
throws a 400 error
http://127.0.0.1:32400/?X-Plex-Token=myactualplextoken/player/timeline/poll?wait=1&commandID=1
throws a 401 error eventually, after sending the browser to http://127.0.0.1:32400/?X-Plex-Token=[myactualplextoken]/player/timeline/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/web/index.html

Not sure if any of that’s relevant, but that url is neato.

MFP log (note: handy is disconnected during this log dump, but same issue occurred when handy was connected)
(In the event that it matters, hitting the same issue with the self-contained 1.31.3 as well)

2025-06-07 12:51:53.0720|TRACE|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Publishing message "MediaPathChangedMessage { Path = , ReloadScripts = True, Context =  }"
2025-06-07 12:51:53.0720|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Received MediaPathChangedMessage [Path: ""]
2025-06-07 12:51:53.0720|TRACE|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Publishing message "MediaPlayingChangedMessage { IsPlaying = False }"
2025-06-07 12:51:54.1895|TRACE|MultiFunPlayer.OutputTarget.ViewModels.TheHandyOutputTarget|The Handy/0 api get [URI: https://www.handyfeeling.com/api/handy/v2/connected]
2025-06-07 12:51:54.3745|TRACE|MultiFunPlayer.OutputTarget.ViewModels.TheHandyOutputTarget|The Handy/0 api response [Content: {"connected":false}]
2025-06-07 12:51:59.1215|TRACE|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Sending "http://127.0.0.1:32400/player/timeline/poll?wait=1&commandID=1" to "Plex"
2025-06-07 12:51:59.6271|ERROR|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Plex failed with exception|System.TimeoutException: The operation was canceled.
 ---> System.Threading.Tasks.TaskCanceledException: The operation was canceled.
 ---> System.IO.IOException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
 ---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request.
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   --- End of inner exception stack trace ---
   at MultiFunPlayer.Common.ExceptionExtensions.Throw(Exception e)
   at MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource.ReadAsync(HttpClient httpClient, CancellationToken token)
   at MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource.RunAsync(ConnectionType connectionType, CancellationToken token)
   at MultiFunPlayer.Common.TaskExtensions.ThrowIfFaulted(Task task)

One last question:
When the script is stopped, is there a setting that makes the hole return to the base position during pause?

In ScriptPlayer, this would be the “Enable Auto Homing” option.

@superheadon
For plex you need a server. MFP connects to the server not the clients.
Also X-Plex-Token is a parameter so it needs to be at the end of the url, so either ?X-Plex-Token=... or &X-Plex-Token=...

Yes there is auto-home, its enabled by default so you must have disabled it.

Plex Server’s running without issues; I can connect on http://127.0.0.1:32400 , the plex app, and the Plex HTPC app.

Plex HTPC is the only version that I can get to show up for Plex in MFP:

Will the Plex server logs from around the same time as the error in the MFP logs help anything?

Yea trace logs from start to exception from plex source could help.
What plex server version do you use?

1 Like

Thanks a lot for your reply!! :laughing:

1 Like

Plex Media Server for Windows, ver 1.41.7.9823

Not sure how I forgot to mention before that the handy is active for half a second per disconnect loop. Not applicable for these logs, but still.

MFP logs:

2025-06-08 01:18:54.9256|DEBUG|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Refreshing clients
2025-06-08 01:18:54.9256|TRACE|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Received "<MediaContainer size="1"><Server name="plexservername" host="192.168.86.229" address="192.168.86.229" port="32400" machineIdentifier="ni3iy90a58mj9t32oj7u0ekg" version="1.70.1.303-5bbf114f" protocol="plex" product="Plex HTPC for Windows" deviceClass="pc" protocolVersion="2" protocolCapabilities="timeline,playback,navigation,playqueues,provider-playback" /></MediaContainer>" from "Plex"
2025-06-08 01:18:54.9256|DEBUG|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Selected PlexClient { Name = plexservername, Host = 192.168.86.229, Address = 192.168.86.229, Port = 32400, MachineIdentifier = ni3iy90a58mj9t32oj7u0ekg, Version = 1.70.1.303-5bbf114f, Protocol = plex, Product = Plex HTPC for Windows, DeviceClass = pc, ProtocolVersion = 2, ProtocolCapabilities = timeline,playback,navigation,playqueues,provider-playback }
2025-06-08 01:18:54.9585|INFO|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Attaching view MultiFunPlayer.MediaSource.Views.PlexMediaSource
2025-06-08 01:18:55.0029|INFO|MultiFunPlayer.OutputTarget.ViewModels.TheHandyOutputTarget|Attaching view MultiFunPlayer.OutputTarget.Views.TheHandyOutputTarget
2025-06-08 01:18:55.1547|DEBUG|MultiFunPlayer|Bootstrapper OnLaunch
2025-06-08 01:18:57.9492|TRACE|MultiFunPlayer.OutputTarget.ViewModels.TheHandyOutputTarget|The Handy/0 api get [URI: https://www.handyfeeling.com/api/handy/v2/connected]
2025-06-08 01:18:58.2047|TRACE|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Sending "http://127.0.0.1:32400/player/timeline/poll?wait=1&commandID=1" to "Plex"
2025-06-08 01:18:58.5273|TRACE|MultiFunPlayer.OutputTarget.ViewModels.TheHandyOutputTarget|The Handy/0 api response [Content: {"connected":false}]
2025-06-08 01:18:58.7062|ERROR|MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource|Plex failed with exception|System.TimeoutException: The operation was canceled.
 ---> System.Threading.Tasks.TaskCanceledException: The operation was canceled.
 ---> System.IO.IOException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
 ---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request.
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   --- End of inner exception stack trace ---
   at MultiFunPlayer.Common.ExceptionExtensions.Throw(Exception e)
   at MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource.ReadAsync(HttpClient httpClient, CancellationToken token)
   at MultiFunPlayer.Common.ExceptionExtensions.Throw(Exception e)
   at MultiFunPlayer.Common.TaskExtensions.ThrowIfFaulted(Task task)
   at MultiFunPlayer.MediaSource.ViewModels.PlexMediaSource.RunAsync(ConnectionType connectionType, CancellationToken token)

Plex Media Server log:

Jun 08, 2025 01:18:58.205 [20360] DEBUG - Request: [127.0.0.1:59042 (Loopback)] GET /player/timeline/poll?wait=1&commandID=1 (17 live) #74 Signed-in Token (plexusername) / Host => 127.0.0.1:32400 / X-Plex-Client-Identifier => b6ed4adb-dc1f-444f-bcf8-e9c37014c169 / X-Plex-Device => Windows / X-Plex-Device-Name => plexdevicename / X-Plex-Features => external-media / X-Plex-Platform => Windows / X-Plex-Platform-Version => 10 / X-Plex-Product => MultiFunPlayer / X-Plex-Provides => controller / X-Plex-Sync-Version => 2 / X-Plex-Target-Client-Identifier => ni3iy90a58mj9t32oj7u0ekg / X-Plex-Token => xxxxxxxxxxxxxxxxxxxx / X-Plex-Version => 1.31.3
Jun 08, 2025 01:18:58.205 [20360] DEBUG - [Req#74/CompanionProxy] [CompanionProxy] Controller b6ed4adb-dc1f-444f-bcf8-e9c37014c169 attached to player ni3iy90a58mj9t32oj7u0ekg
Jun 08, 2025 01:18:58.205 [20360] VERBOSE - [Req#74/CompanionProxy] It took 0.0 sec to serialize a list with 1 elements.
Jun 08, 2025 01:18:58.205 [23348] DEBUG - Completed: [127.0.0.1:58855] 200 GET /player/proxy/poll?deviceClass=pc&protocolVersion=2&protocolCapabilities=timeline%2Cplayback%2Cnavigation%2Cplayqueues%2Cprovider-playback&timeout=1 (17 live) #59 TLS GZIP 9224ms 513 bytes (pipelined: 3)
Jun 08, 2025 01:18:58.208 [20360] DEBUG - Request: [127.0.0.1:58855 (Loopback)] GET /player/proxy/poll?deviceClass=pc&protocolVersion=2&protocolCapabilities=timeline%2Cplayback%2Cnavigation%2Cplayqueues%2Cprovider-playback&timeout=1 (17 live) #75 TLS GZIP Signed-in Token (plexusername) / Accept => application/xml / Accept-Encoding => gzip, deflate, br / Accept-Language => en / Connection => keep-alive / Host => 127-0-0-1.0e1d55b04dfc4fb2b76ff6d6d01de40e.plex.direct:32400 / Sec-Fetch-Dest => empty / Sec-Fetch-Mode => cors / Sec-Fetch-Site => cross-site / User-Agent => Plex HTPC; 1.70.1.303-5bbf114f; Windows 10 Version 2009 / X-Plex-Client-Identifier => ni3iy90a58mj9t32oj7u0ekg / X-Plex-Device =>  / X-Plex-Device-Name => plexdevicename / X-Plex-Device-Screen-Resolution => 1707x960 / X-Plex-Device-Vendor => Plex / X-Plex-Drm => widevine:video / X-Plex-Features => external-media,indirect-media / X-Plex-Language => en / X-Plex-Model => Gecko / X-Plex-Platform => windows / X-Plex-Platform-Version => 10.0.26100 / X-Plex-Product => Plex HTPC for Windows / X-Plex-Session-Id => e55bc62d-de89-408f-8c21-119150c25c3d / X-Plex-Token => xxxxxxxxxxxxxxxxxxxx / X-Plex-Version => 1.70.1.303-5bbf114f
Jun 08, 2025 01:18:58.208 [4912] DEBUG - Request: [127.0.0.1:58858 (Loopback)] POST /player/proxy/response?commandID=1 (17 live) #5 TLS GZIP Signed-in Token (plexusername) / Accept => application/json / Accept-Encoding => gzip, deflate, br / Accept-Language => en / Connection => keep-alive / Content-Length => 35 / Content-Type => application/xml / Host => 127-0-0-1.0e1d55b04dfc4fb2b76ff6d6d01de40e.plex.direct:32400 / Sec-Fetch-Dest => empty / Sec-Fetch-Mode => cors / Sec-Fetch-Site => cross-site / User-Agent => Plex HTPC; 1.70.1.303-5bbf114f; Windows 10 Version 2009 / X-Plex-Client-Identifier => ni3iy90a58mj9t32oj7u0ekg / X-Plex-Device =>  / X-Plex-Device-Name => plexdevicename / X-Plex-Device-Screen-Resolution => 1707x960 / X-Plex-Device-Vendor => Plex / X-Plex-Drm => widevine:video / X-Plex-Features => external-media,indirect-media / X-Plex-Language => en / X-Plex-Model => Gecko / X-Plex-Platform => windows / X-Plex-Platform-Version => 10.0.26100 / X-Plex-Product => Plex HTPC for Windows / X-Plex-Session-Id => e55bc62d-de89-408f-8c21-119150c25c3d / X-Plex-Token => xxxxxxxxxxxxxxxxxxxx / X-Plex-Version => 1.70.1.303-5bbf114f
Jun 08, 2025 01:18:58.208 [23348] DEBUG - Completed: [127.0.0.1:58858] 200 POST /player/proxy/response?commandID=1 (17 live) #5 TLS GZIP 0ms 195 bytes (pipelined: 1)
Jun 08, 2025 01:18:58.996 [23348] VERBOSE - WebSocket: processed 1 frame(s)

Is there a way to fill the start gaps of a video? when the funscript has no action from the start of the video the software skips directly to the start of the funscript

Hi, I updated the plugin according to your recommendations, I hope I didn’t make any mistakes. I added some functions too. However, I would like to put the same slider system that you put in the output range to the random threshold for the merged ones but I can’t do it :confused:

1 Like

I updated just in case and for me Plex with MFP works fine with iOS plexamp and Plex HTPC. For some reason it seems like they removed the ability to control the iOS app and Plex for Windows (Supported Plex Companion Apps | Plex Support).
So it seems like an issue on your end.

Yes, disable skip to script start and setup motion provider on L0:


You can of course use other motion provider if you want.

You need to put common properties like UpdateInterval, RandomThresholdMin, RandomThresholdMax, Selected etc. to a AxisSettings class, then expose Dictionary<DeviceAxis, AxisSettings> from your plugin class and bind it in view with for example ItemsControl.
Here is view part in MFP: MultiFunPlayer/Source/MultiFunPlayer/OutputTarget/Views/SerialOutputTarget.xaml at f485e7a4eb996869c056bf56f365575c934a08ae · Yoooi0/MultiFunPlayer · GitHub
In plugin you can simply do AxisSettings = DeviceAxis.All.ToDictionary(a => a, _ => new AxisSettings())); to initialize the dictionary.
This way your plugin will handle all axes and you won’t have to use DeviceAxis.Parse, and you wont have so many properties.

For the SetAxisValue you are still using 0.1 duration, I think you should use the interval instead.
You could try adding invokeDirectly: true as the last parameter to the InvokeAction for "Axis::Value::Set" to gain some performance.
Also I don’t know if having tasks per axis is necessary, seems a little bit over complicated.

Hi Yoooi,
I recently discovered MFP and gotta say it’s truly a great piece of software! I wanted to ask though, does MFP also support MQTT? If not, would it be possible to add this? I would love to be able to hook it up to my Shelly S smart plugs, to control mains powered toys.

1 Like

Its possible via a custom plugin. I can’t add direct support for everything to MFP, so everything thats outside of “standard” setup has to be done via plugins.