Skip to content

Add PotPlayer support - #812

Closed
DrSaeedHub wants to merge 1 commit into
Syncplay:masterfrom
DrSaeedHub:add-potplayer-support
Closed

DrSaeedHub wants to merge 1 commit into
Syncplay:masterfrom
DrSaeedHub:add-potplayer-support

Conversation

@DrSaeedHub

Copy link
Copy Markdown

Adds PotPlayer (Windows) as a supported player.

PotPlayer is controlled through the remote-control window messages of the PotPlayer SDK (SendMessage(hwnd, WM_USER, POT_..., value), plus WM_COPYDATA for strings). The new player only uses ctypes, so it adds no dependency, and it follows the existing BasePlayer contract, so client.py and the GUI are unchanged: the player is picked up through PlayerFactory, including default install paths and an icon.

What is supported

  • Play/pause, seeking, file name/path/duration, end of file
  • Slowdown on desync (PotPlayer's speed can be set)
  • OSD messages (UTF-8, non-Latin text works)
  • Shared playlists / switching files
  • Closing PotPlayer stops the client, as with MPC

PotPlayer quirks the implementation handles

  • Coarse time. PotPlayer refreshes the time it reports only every ~500 ms, but every refresh is exact (measured spread ~3 ms). A 5 ms poll thread anchors on those refreshes and interpolates in between, so askForStatus() returns a position accurate to a few ms. A refresh that races one of our seeks still carries the old timeline, so it is ignored instead of being reported as a user seek.
  • No "open file" message. The SDK has none, and WM_DROPFILES is ignored. The only way to hand a file to a running instance is PotPlayerMini64.exe <file> /current, which skips instances started with /new. So:
    • If no other PotPlayer is running, Syncplay starts its instance without /new and opens files in the same window with /current.
    • Otherwise it starts with /new and opens a file by starting a fresh /new window and closing the old one. That way a PotPlayer the user already had open is never taken over.
  • Loading. PotPlayer ignores commands (and may resume a remembered position) until a new file has really started playing. The room state is applied after that, and pause is re-applied for a short while if PotPlayer starts playing on its own.
  • End of file. PotPlayer reports an undocumented status 0 and then resets its time to 0. The player reports the end of the file (and calls eofReportedByPlayer) instead of a jump to 0.
  • Keyframe seeking. With PotPlayer's default Playback > Jump (to) > Use keyframe to jump to a time, seeks land on the previous keyframe, often seconds away. The player detects this, shows a one-time warning telling the user how to turn it off, and from then on skips corrective seeks shorter than SEEK_THRESHOLD. Those would otherwise land seconds back and be broadcast to the room as a new seek on every pause.
  • Like MPC, run() blocks until PotPlayer's window is up, so the client never syncs against a missing player.

Testing

Tested on Windows 11 with PotPlayer 64-bit (PotPlayerMini64.exe). I used a local syncplayServer.py and two console clients, each driving its own PotPlayer, and played the user by sending input directly to the PotPlayer windows:

  • Both clients start paused and load the first shared-playlist file.
  • Play, pause and seek propagate both ways within ~60–150 ms (seeks ~0.9 s). Positions while playing agree within ~0.1 s.
  • Slowdown to 0.95× kicks in on desync and reverts.
  • Switching playlist items opens the file in both players, which stay paused. With a single client, the switch reuses the same window in ~0.3 s.
  • A second pause with keyframe seeking on causes no extra jump.
  • Both players stop at the end of the file. Closing PotPlayer exits its client cleanly.
  • Client CPU while playing: ~1.6% of one core.

Not tested: 32-bit PotPlayer and PotPlayer64.exe (the non-Mini build). They use the same SDK and window classes, and their paths are included.

Notes

  • New strings are English-only, like the recent reconnect/watched-history strings.
  • OSD duration can't be set through the SDK, so PotPlayer's own OSD duration applies.
  • Unrelated to this PR, noticed while testing: SyncplayClient.__init__ initialises _globalPaused = 0.0. If the server's first state arrives before the player is ready, the client sends "paused": 0.0 and unpauses the room. run() blocking avoids this for PotPlayer in practice, but the initial value probably wants to be True.

Control PotPlayer (Windows) through the remote-control window messages of the
PotPlayer SDK, using only ctypes.

- Play state, position, duration, file path, speed and OSD via SendMessage /
  WM_COPYDATA.
- PotPlayer only refreshes its reported time every ~500 ms (each refresh is
  exact), so the position is interpolated between refreshes from a 5 ms poll.
- Slowdown on desync is supported (speed is settable).
- Files are opened in the same window with `/current` when Syncplay's
  PotPlayer is the only one running; otherwise a fresh `/new` window replaces
  it, so a PotPlayer the user already had open is never taken over.
- Waits for a newly loaded file to actually start before applying the room
  state, and re-applies pause while PotPlayer is still settling.
- End of file is reported to the client instead of PotPlayer's reset to 0.
- Detects PotPlayer seeking to keyframes, warns once, and then skips
  sub-second corrective seeks that would land seconds away.
@Et0h

Et0h commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Hi, we've had people suggest support for additional media players over the years, but this is the first time we've received a complete implementation.

Your PR has prompted a discussion among the core Syncplay developers about whether we want to expand the range of media players we support, particularly given our experience maintaining the existing integrations.

Syncplay works really well with mpv, which is what we use ourselves and are best placed to support. We've found that getting other players from an initial implementation to a smooth, reliable experience requires considerable testing, bug fixes and ongoing maintenance. Other players can also require workarounds and compromises that make synchronisation less reliable or result in a poorer implementation of certain Syncplay features.

We already support several mpv-based players, including mpv itself, mpv.net (my favourite), IINA and memento, offering users a choice of interfaces across different platforms. We believe these provide sufficient variety for Syncplay's intended use cases. If anything, we'd rather reduce the number of player integrations we have to maintain than expand it further.

As a result of this discussion, we've updated our FAQ to clarify that, for the foreseeable future, we'll only consider new players that can build on our existing mpv integration.

I appreciate this will be disappointing given the work you've put into the implementation, but I hope the reasoning behind our decision makes sense. Thanks for contributing, and I'm going to close this PR in line with our updated policy.

If you have other big ideas for Syncplay, feel free to discuss them in our [Ideas Discussions](https://github.com/Syncplay/syncplay/discussions/categories/ideas) before putting time into implementing them. If I haven't responded within a month, feel free to send me a reminder via my public email address.

@Et0h Et0h closed this Oct 9, 2026
@DrSaeedHub

Copy link
Copy Markdown
Author

Thanks for the detailed explanation, and no problem at all — I completely understand the decision, especially given the effort it takes to keep each player integration working well. Since I need PotPlayer support myself, I'll keep building and using it from my own fork. Thanks again for Syncplay!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants