28
loading...
This website collects cookies to deliver better user experience
So excited about this project @malgamves and I are working on! A live watch party app to host dedicated sync stream parties with your friends. We'll update this thread as we make progress #BuildingInPublic pic.twitter.com/5LnnVNzcSR
— Srushtika Neelakantam (@srushtika ) April 16, 2021Both the admin version and non admin version of the site can be retrieved directly from the CDN (based on URL routing).
Ably’s Pub/Sub platform requires you to authenticate before you can use the service. There are two options for this - either embed the API key directly into the front-end web app (which would be a bad idea because anyone can steal it), or use Token authentication by requesting an auth server to help the front-end clients to authenticate securely. We’ll use Strapi as our auth server (in addition to its beautiful CMS capabilities which we’ll touch upon soon).
After we’ve received an Ably Token Request back from Strapi, we can send it to Ably to securely authenticate with the service and initialize the SDK. This sets up a persistent realtime connection with Ably, allowing any new updates to be pushed directly to our app and vice versa. We’ll use this to synchronize the video stream, as well as to share comments and live online status of participants.
After the host has authenticated with Ably (and transparently with Strapi via dummy user credentials), they’ll be able to share an invite link with any participants they’d like to invite to their private watch party.
Next, the host will be able to request the video library from the Strapi CMS. This will show them a grid of various videos to choose from. After they’ve chosen a video, the unique reference code for that video will be immediately published to all the participant apps via Ably. These non-admin participants can then (behind the scenes) request the particular video resource directly from the Strapi CMS.
On this final screen, everyone will be able to add live comments and it is up to the host to play the video, pause it, seek it to a certain timestamp etc - all of which would be synchronized with the rest of the viewers.
http://localhost:1337/graphql
to access our GraphQL Playground and play around with different GraphQL operations.'use strict';
const Ably = require('ably/promises');
const ABLY_API_KEY = process.env.ABLY_API_KEY;
const realtime = Ably.Realtime({
key: ABLY_API_KEY,
echoMessages: false
});
module.exports = {
async auth(ctx) {
const clientId = 'id-' + Math.random().toString(36).substr(2, 16)
const tokenParams = { clientId };
try {
const ablyThing = await realtime.auth.createTokenRequest(tokenParams);
console.log(ablyThing)
return ablyThing
}
catch (err) {
return ctx.badRequest("Daas not good!!")
}
}
};
const ably = new Ably.Realtime(<auth endpoint or api key>);
const channel = ably.channels.get(‘jamstack-news’);
// Publish a message to the jamstack-news channel
channel.publish('greeting', 'hello');
// Subscribe to messages on jamstack-news channel
channel.subscribe('greeting', function(message) {
alert(message.data);
});
mainParty
: used mainly to share presence data (this is explained below in this article).video
: used to share updates related to the video player, including play, pause and seek events, along with the current timestamp.comments
: used to share live comments between participants of the specific watch party.watch-party-<random-room-code>
, video-<random-room-code>
and comments-<random-room-code>
.store/index.js
. This file works as a central store for most of the data in our static site. A typical VueX store contains four objects (possibly more depending on your specific app) - state, getters, mutations and actions.currentVideoStatus
state object:
currentVideoStatus: {
isVideoChosen: false,
didStartPlayingVideo: false,
chosenVideoRef: null,
currentTime: null,
isPlaying: false,
isPaused: false
},
instantiateAbly()
method:const ablyInstance = new Ably.Realtime({
authUrl: this.$config.API_URL + "/auth-ably"
});
attachToAblyChannels()
method:attachToAblyChannels(vueContext, isAdmin) {
//mainPartyChannel
const mainParty = this.state.ablyRealtimeInstance.channels.get(
this.state.channelNames.mainParty +
"-" +
this.state.watchPartyRoomCode
);
// similarly for the video and comments channels
subscribeToChannels()
method:state.channelInstances.comments.subscribe(msg => {
state.channelMessages.commentsChMsg = msg;
});
publishCurrentVideoStatus()
method:state.channelInstances.video.publish(
updateEvent,
this.state.currentVideoStatus
);
requestInitialVideoStatus()
method:force sync
button.requestInitialVideoStatus({ state }) {
state.channelInstances.video.publish(
"general-status-request",
"request"
);
},
publishMyCommentToAbly()
method:publishMyCommentToAbly({ state }, commentMsg) { state.channelInstances.comments.publish("comment", {
username: state.username,
content: commentMsg
});
},
getExistingAblyPresenceSet()
methodthis.state.channelInstances.mainParty.presence.get((err, members) => {....});
subscribeToAblyPresence()
method:this.state.channelInstances.mainParty.presence.subscribe("enter", msg => {....});
this.state.channelInstances.mainParty.presence.subscribe("leave", msg => {....));
handleNewMemberEntered()
and handleExistingMemberLeft()
methods:enterClientInAblyPresenceSet()
method: