-
Notifications
You must be signed in to change notification settings - Fork 214
Guide to adding support for a new media player to Syncplay
While 99% of the time the best option is to use mpv, if you want to develop and maintain support for a different media player then this guide is for you.
This guide is not super detailed because it expects a high level of pre-existing development knowledge and for people to refer to the existing implementations to see how it is supposed to behave.
In the base class are the following variables that need to be set:
speedSupported = True # Does Syncplay have the ability to specify the playback speed of this media player?
customOpenDialog = False # In most cases this should be False, but can be set to True if you are using a custom open file dialogue (although that hasn't been fully tested)
chatOSDSupported = False # True if media player supports OSD messages for that that are different from the regular OSD messages (e.g. mpv)
alertOSDSupported = True # True if media player supports alert messages for that that are different from the regular OSD messages (e.g. mpv)
osdMessageSeparator = "; " # Separator for when Syncplay has to send multiple messages at once (might want to be something like "\n" if the media player supports it)
See https://github.com/Syncplay/syncplay/blob/master/syncplay/players/basePlayer.py for details on precise input variables, etc. See existing media player implementations if additional clarity is required.
- askForStatus: This method is supposed to execute updatePlayerStatus(paused as a Boolean, position in floating point seconds) on client using the arguments: boolean paused and float position in seconds. See https://github.com/Syncplay/syncplay/blob/master/syncplay/players/mpv.py for an example implementation.
-
displayMessage: Displays an OSD message for a specified duration. OSDType can be the constants OSD_NOTIFICATION, OSD_ALERT or OSD_CHAT; mood can be the constants MESSAGE_NEUTRAL, MESSAGE_BADNEWS or MESSAGE_GOODNEWS. Syncplay assumes that the player has an implementation for this, so it needs to be included even if it is just a
pass
stub. - Drop: Clean-up connection with player before Syncplay will close down. Generally this involves sending an exit/quit message to shut the player down.
-
Run: Start up the player. Note:
run()
needs to callclient.initPlayer
with the new player instance and not just return that instance. - setPaused: Sets playing/paused states. Uses Boolean.
- setFeatures: Used to tell the mpv implementation to send various initiation commands. You can normally just pass this, as per VLC: https://github.com/Syncplay/syncplay/blob/master/syncplay/players/vlc.py
- setPosition: Sets position of media player (might be an invalid position). Uses floating point seconds.
- setSpeed: Set playback rate (usually 1, but can be different if slowing down due to time difference, etc).
- openFile: Specifies a file to open in the media player. Might be a URL. The Boolean resetPosition is used to specify whether Syncplay wants the player to set position to 0 when the file is loaded or to retain the current (previous) playback position. This might require the position to be set to self._client.getGlobalPosition() in the event that resetPosition is False but the position is automatically reset by the player. Note: openFile is not supposed to change the paused state from what it was before the openFile, but in some cases Syncplay will try to pause at the same time as the playlist advances if not everyone is ready. This is accomplished through a setPaused command being sent around the same time as the openFile command.
- getDefaultPlayerPathsList: Returns a list of relevant media player constants file and returns making use of getExpandedPath. See mpv and VLC for example implementations.
- isValidPlayerPath: Return true if the media player path is recognised as that for your media player (e.g. it is VLC if it contains vlc in the path). Don't always return true, or Syncplay will think all paths relate to this media player!
- openCustomOpenDialog: You don't usually need this because opening a custom open dialog is not normal.
- getPlayerPathErrors: You can probably just return none here. This is meant for returning an error if your media player has to be given a media path to start but no path has been specified (i.e. mplayer2).
-
When a media file is loaded (or more specifically when the path, filename or duration changes), this should trigger an update send to self._client.updateFile which should be run through a thread. See mpv/VLC implementations.
-
When the media player is closed that it also ends Syncplay using elf.drop(). If relevant you can ask Syncplay to first display an error message by calling self._client.ui.showErrorMessage from a thread. See mpv for an example of how to do this.
The most common use case is to add support for a variant of a currently supported media player.
To do this you can:
- Add paths relating to your variant media player to syncplay/constants.py in a similar manner to MPV_PATHS. This is where Syncplay will look for the media player, and you will be making use of this later in your implementation of getDefaultPlayerPathsList. Place the new code just below the code for your base class.
- In syncplay/constants.py create a constant for your icon using your own variant of the _ICONPATH constant - this will want to be a .png file that you add to the repository (or you can use the path for the base player if that is appropriate).
- Create a new .py file in the syncplay/syncplay/players/ folder for that player.
- Use https://github.com/Syncplay/syncplay/blob/master/syncplay/players/mpvnet.py as a guide base for how to make this a sub-class of an existing player. Basically, you are making a new class using the base class from the main player as your starting point. You then need to specify the areas where your class differs from the base class. You are likely to want to re-implement run, getDefaultPlayerPathsList, isValidPlayerPath, getExpandedPath and getExpandedPath. These are explained above.
- In syncplay/players/init.py import the media player and add the player class to getAvailablePlayers. You can search for references to your base media player's class for examples of how to do this. Import/add your media player after the base media player.
- If you want Syncplay to be officially supporting this variant then you will need to add references to it in all the syncplay/messages_*.py references to media players in the player-path-error, player-path-config-error, and executable-path-tooltip messages.
From Syncplay's side, you need to:
- Add paths relating to your new media player to syncplay/constants.py in a similar manner to MPV_PATHS. This is where Syncplay will look for the media player, and you will be making use of this later in your implementation of getDefaultPlayerPathsList. Look for where the existing paths are and put your new one after the existing ones.
- In syncplay/constants.py create a constant for your icon using your own new player of the _ICONPATH constant - this will want to be a .png file that you add to the repository.
- Create a new .py file in the syncplay/syncplay/players/ folder for your new player.
- Use https://github.com/Syncplay/syncplay/blob/master/syncplay/players/basePlayer.py as a starting point guide base for what you will need to implement
from syncplay.players.basePlayer import BasePlayer
, and then use that as the basis for your new class. You are likely to need to import os.path as well. See above for more details on methods. - At the start of your class, also specify the player variables (see above).
- In syncplay/players/init.py import the media player and add the player class to getAvailablePlayers and import your media player..
- If you want Syncplay to be officially supporting this player then you will need to add references to it in all the syncplay/messages_*.py references to media players in the player-path-error, player-path-config-error, and executable-path-tooltip messages.