syncP - Bunch of scripts syncing media playback

ยท

10 min read

Brief

SyncP (sync player) is basically a couple of python scripts which uses an mpv backend to play local media files in sync between hosts and clients. The need of host and client is because it is a serverless app and one needs to be a host and port forward in order to have clients connecting to the host and syncing the playback of the media files. The mpv backend is the core of the project, I was lucky enough to find a lot of help from jaseg/python-mpv. In the age of applications which can stream all the media content in real time or share screen between people this project seems to be doing nothing innovative and indeed it does nothing more than just sync the playback and listen to events like toggles (play/pause). Being in a third world nation I do not have access to high bandwidth, low latency, unlimited data therefore, I had to think of something which was not streaming. The problem with screen sharing and streaming are to do with the data consumption and therefore syncP solves that problem entirely. This app achieves its purpose by encoded text messages based communication between hosts and clients. Initially this project was limited to one client for every host but now it can have any number of clients for every host. The project is available in pypi and can be simply installed using pip install syncP after which a syncp --help should be enough to get started. Before installing one has to set up mpv for the player to run and the guide to which is available in the repo lawRathod/syncP.

Typical Use Case

Since it can play any kind of media extension (lovely mpv), you can enjoy watching movies or tv shows or even listening to music with your friends.

Behind the scenes

Three major components of the project are the following scripts:

Host script

The host script needs to start first by a host and the script will have the following things done in the following order:

  • Creates the object of the class player (inside the player.py) and the constructor from that class lets one choose the media file in the very beginning which is to be played. The function returns a player object which is later used to control the player.
  • Open a port on the system and bind with it to start listening for the connections. The app spawns a thread with a method and the socket passed as a parameter . The port is not forwarded yet and has to be done using ngrok or manually using the router.
  • The player object returned in the very beginning is used in a method spawned in another thread. The playback is paused in the very beginning and one thread from these two is still waiting for the first connection.
  • The moment the socket receives a connection it is appended to a linked list. This list is shared with the player.py and all the threads. Every connection is then appended to the list.
  • If any one connection is dropped the connection is removed from the list.
  • The playback is paused everytime when a connection is added or removed.

These things are done consistently and allows the host to receive the connections anytime during the process is running. The data sent between the host and clients is encoded text messages like "toggle" on playback toggle, "sync: time" this is a way to sync the time between all the clients and host just in case. I figured to only allow hosts to sync time between connections. For now when a toggle message is received from a client the host receives it and sends to all other connections and acts as a broadcaster.

The host starts the socket with keep_alive flag to help the connections sustain the life of the media playback and not drop randomly after some time. Found this the hard way but the fix was easy and quick to implement.

Client Script

The client side of the project does very little when compared to the host because I felt it should be kept as light as possible and the host handle most of the complexities. The following jobs happen on the client side of the connections:

  • It prompts to provide a host url and port number on which the host is accepting connections. The last connection is stored in the file in the package repository.
  • The user needs to select the media from the list of files in the current directory at this point.
  • After the media is selected the client tries to connect to the host by spawning a new thread and a separate thread for the player.
  • If everything goes right the client receives the time from the host of its player for seeking in the client.

That's all that happens, once the connection is set the app sends a keep alive message to keep the connection alive and now the host and client are in sync.

Player Script

This is the common module between client and host which enables the playback and sync operations in the lifetime of this application. This primarily uses the work from jaseg/python-mpv. The player class is to keep event listeners in the player and media file selection tidy and in a module. The constructor is to bring all the necessary objects to the class necessary for the future use case. Some event listeners on toggle and keys are part of this script.

New stuff I learned

This was my first shot at working with a couple of things. Mainly the web sockets and threads that took a little time to get used to. A systematic design which could then come to life and help get things done in the desired way. This was a cute little project for me to hack together in little time to help me watch stuff with my friends. It's always a little challenging to bake an interface and make design choices when you aren't the only one who is gonna use the app. This app is cli based and I didn't have to design something as complex as a platform which saved me a lot of time. Some things I took for granted are the abstractions python has to offer and I wish to understand all of that in more depth.

Things that could've been better

Here's a list of things where I think the app could shine and could have had a better experience, I am not sure if I am going to implement these ever because I know the application once made is never over and when I will finish something new things will pop up.

  • Lack of UI. A gui could have solved this problem with some nifty features like drag and drop of files instead of running the app in the directory where the file is present.
  • Instead of having a host which does a lot of work, a single point of truth in cloud db maybe where everyone is listening to for toggle changes and the seek time for the playback. This would eliminate the need of port forwarding on the host side of things.
  • A chat window next to the player.

I can think of many more but it feels like a rabbit hole where you begin to imagine a completely different thing which it isn't right now.



Thank you for making this far. Have a great day! ๐Ÿ˜„

ย