The Problem
So I had a project recently in which there were two components: the "app", and the "big screen". Users take their photo with the app, apply some fun effects, and then send it to the big screen for the world to see! These "big screens" are always on.
The "app" was being developed by another developer externally. They would be passing data to me to display on the big screen via a custom API. The screen itself was a super simple microsite which rotated through several images until it was interrupted by some user generated content passed to me from the app.
The catch however was that there were multiple locations where these apps and screens could be installed. Wherever a photo was uploaded, it should appear on every screen. So if someone in Singapore took a photo, it would appear on the big screen in London and Singapore.
Basic user journey
- User takes photo using app.
- User adds some fun effects.
- User sends the photo to the big screen.
- Big screens all around the world display the photo.
- Photo then gets scheduled for deletion.
My Solution
Initially I used just an AJAX call to get the next photograph stored in the database, then set it to "used" so it didn't appear again. However this meant that if Screen A grabbed it before Screen B, Screen B would never see it. Not ideal if the user is waiting in front of Screen B to see themselves!
So I went down a server-side CRON route. Every 10 seconds, my CRON would run and set the status of any current image from 2, to 1; and set any unused image to 2. Below is the CRON script I used to run a command every 10 seconds.
for i in {1..5}; do curl --silent "https://***/api/prep" &>/dev/null; sleep 10; done
My table in my database looked like this:
ID | img | status |
---|---|---|
1 | e1321fsdffdsfds.jpg | 1 |
2 | mpfhmnh80fhj823.jpg | 2 |
3 | fdsfhjdskfu8h1u.jpg | 0 |
0 represents an image which hasn't been used yet, and is in essence queued.
1 represents an image which has been used / shown on the big screen.
2 represents the current image to be shown on the screen.
My AJAX call would get the next image where status = 2
and display it.
This works mostly; however there is a tiny chance of the image not appearing on a screen. My CRON runs every 10 seconds. My Javascript AJAX call runs every 10.15 seconds. There's the tiny possibility that an image a user takes does not appear on the screen they're looking at.
How would you have solved this problem?
Very interested to hear how other developers may have tackled this problem! The solution I've presented above is a PHP based solution, with some AJAX/JS thrown in.
I should add this project launched successfully. I'm just looking to open a discussion to discover what other technologies/methods could be used to complete this job.
Top comments (11)
Sorry to necro; but I couldn't resist throwing my 2 cents at it!
I would add a table to track the "big screen" 's current image (a row for each "big screen", with a foreign key to the images table you have above):
Then there's just a few operations to implement:
CurrentImageID
is less than the largest ID in the Images tableCurrentImageID
points to, and incrementCurrentImageID
to the next oneCurrentImageID
CurrentImageID
to the most recent image.Great solution ! Thanks for taking the time to share it :D Don't worry about necroing the thread, it's great to get several different perspectives, as I know a similar job will come around again when we can travel more freely!
How would you trigger the "Big Screen" image check? CRON? OR just "listen" for an image upload?
If I'm limited to just HTTP (so no websockets, server push schemes, or custom protocols) then I would just have the "Big Screen" regularly poll (aka cron) for a new image (maybe 10 seconds?).
If we can use websockets or similar, then that opens the door for fun things of the server notifying screens that new images are available (but then the screen would still make the "request" to get that new image so that it always goes through the same consistent process)
Definitely a good case for server-push via WebSockets. I'd make the big screens just dumb clients that connect to the server via WebSocket, listen for images over that socket, and display whatever is recieved. Then whenever the server receives a new user image, it should push it once to all connected big screen clients. Probably no need to even keep the image state around anymore in this architecture.
But that's not to say that your solution was invalid; some platforms still don't have great support for WebSockets (though Socket.io usually takes care of that nicely), and ultimately if your solution worked, then it worked! It's definitely a clever solution!
Thank's for your feedback Ken! This is exactly what I hoped to achieve from making this thread. I'd completely forgotten WebSockets were a thing. They're not something I've had to use yet, but you're right this project seems like the perfect time to research how to use them. May even look into refactoring my code; or at least using them the next time a similar project comes along.
Thank you! At the end of the day, the client doesn't care what's running on the backend, so long as it works :)
I would use WebSockets. Then you don't have to worry about timing or marking photos as used. The PHP microsite would get the photo from the app just as it is now, then the PHP microsite can send the photo out via WebSockets to every screen at the same time.
WebSockets! Of course! That's basically what they're designed for right? I'd completely forgotten they were a thing. This is exactly what I wanted to start a discussion like this. I'm the only developer at my company, so I don't have someone I can spitball ideas with; and I guess in this instance I forgot Google existed - haha.
Thanks Neil! Definitely going to research more into WebSockets now and create this project again using them. May refactor the code to include them, but at the very least I'll have a new version the next time a similar project comes along.
I'd structure things with a server-push architecture instead of a client-pull architecture. Such an approach has a couple of specific advantages:
As far as how to do it, there are literally dozens of options. I'd probably go with a PWA and use the Push API from the app's service worker. From there, it just amounts to a bit of extra server-side logic to actually push things out.
Thanks for your suggestions Austin!
Luckily we can control which browser the "big screen" will use. As it won't be something which is launched to the wide world to access, we can ensure we use newer browsers. So thankfully we're open to new technologies!
Your approach for real-time content on multiple screens is interesting! Instead of a CRON job, consider WebSockets for faster image updates. They ensure immediate display when users upload content. Exploring serverless computing or event-driven setups can boost efficiency. Congrats on the successful project launch and being open to different solutions!