Skip to content

TogetherTube is a YouTube client that brings YouTube and playback sync together!

License

Notifications You must be signed in to change notification settings

sjagoori/TogetherTube

Repository files navigation

📺 TogetherTube

TogetherTube is a YouTube client that brings YouTube and playback sync together!

💻Live demo 💡 The Concept 📝Features 📺 YouTube Player API 📖 YouTube Data API 🔄 Data lifecycle diagram 🤖 Installation 🤝 Sources 📝 License

💻Live demo

Link to the demo

💡The concept

TogetherTube is a YouTube client that brings YouTube and playback sync together using Socket.io. Users can enter a YouTube URL in the search bar to open a player. On that page, they'll be greeted with the desired video along a live chat and related videos.

Concept 1 The first concept was similar to the final concept. Unlike the final version, it used an oauth login screen that would allow personalized rooms. The main difference was to control your other devices/tabs. After a review, it became clear that it should focus on the multi-user aspect rather than single-user&multi-device.
The (final) concept While processing the feedback on my first concept, I decided to follow the multi-user comment. In this iteration I focussed on syncing playback and adding more multi-user features (the chat) and applied them in the concept. To improve and stimulate usage and UX, I stripped away the login and personalization such as playlists and custom rooms. Here's how the data will flow within the application.

📝Features

  • Search any YouTube video
  • Play any YouTube video
  • Watch any YouTube video together
  • Chat together on any YouTube video
  • Get related videos on any YouTube video
  • Save TogetherTube as PWA on your device

MoSCoW

Must haves

Core functionality

  • Create room by YouTube link
  • Count of members in a room

Player

  • Play video in the room

Chat

  • Chat with other viewers
  • Cache messages and restrict chat history to one hour

Related videos

  • Display related videos
  • Related video creates and joins new room
  • Cache related videos on an external DB

Should haves

Core functionality

  • Mobile friendly layout
  • A search bar to paste links and open new rooms

Player

  • Sync position in the video with the room
  • Sync the playback state with the room

Chat

  • Know when others join the room
  • Have a name when chatting if not anonymous

Could haves

Core functionality

  • Custom playbar
  • Video title as tab title
  • Display current active rooms
    • Technical restrictions; find a way to monitor room sizes without using window.onbeforeunload and applying changes
  • Creating playlists starting from current video

Won't have this time

  • User roles for playback controls
  • Manually searching videos
  • Redirect to next video in related videos section

📺 YouTube Player API

Source: link to iframe API

The YouTube player API provides an iFrame wherein the video player is found. The API supports two different approaches; automatically where the iFrame player embeds a video link, then renders it in the iFrame:

<iframe id="ytplayer" type="text/html" width="640" height="360"
  src="YouTubeLinkGoesHere?autoplay=1&origin=http://example.com/"
  frameborder="0"></iframe>

In this case, the player takes the given url source along with its parameters ?autoplay=1&origin=http://example.com/" to generate a video player within the iframe. The parameters are optional and can be expended with additional parameters found in the documentation: link to additional parameters.

The manual approach, however, is different. This approach needs an additional piece of javascript to render the iframe. The main advantages are:

  • Programmatically change sources
  • Handle player events manually
  • Retrieve additional meta data

For this project, we're using the manual way to handle data events:

  • state: 1 - video is playing
  • state: 2 - video is paused
  • state: 3 - video is seeking

Knowing this, we can use javascript to interact with the Player object, which contains methods such as pauseVideo(), playVideo(), seekTo(), and other interactions normally found in the UI. See complete list

📖 YouTube Data API

Source: link to YouTube Data API

This project uses the YouTube Data API to retrieve and render related videos to the video that is currently playing. To achieve that, the Search endpoint is used: link to the Search endpoint. This endpoint takes a video id and returns videos related to it. The data returned contains the following from which only the snippets are used:

{
    "kind": "youtube#searchResult",
    "etag": etag,
    "id": {
        "kind": string,
        "videoId": string,
        "channelId": string,
        "playlistId": string
    },
    "snippet": {
        "publishedAt": datetime,
        "channelId": string,
        "title": string,
        "description": string,
        "thumbnails": {
        (key): {
            "url": string,
            "width": unsigned integer,
            "height": unsigned integer
        }
        },
        "channelTitle": string,
        "liveBroadcastContent": string
    }
}

🔄Data lifecycle diagram

Real-time events

  1. State
    The state event handles the seeking events sent from the client and broadcasts it to other clients. The state contains the roomid, timestamp, and playstate
{
  id: String,
  playing: Boolean,
  timestamp: Number,
  room: String
}
  1. Playback
    The playback event handles the playback state of a give room. The array contains a room name and its playback state that is emitted to other clients connected to that room. The playback state is a boolean that plays the video on true and pauses it on false.
{ 
  room: String, 
  state: Boolean 
}
  1. Message
    The message event handles the sent messages from clients. The client emits a message event which is fetched by the server, the server caches the message using the cacheMessage function, and then emits the received message object to the entire room which is then rendered to in the DOM.
{
  "roomName": [
    {
      "name": String,
      "message": String,
      "timestamp": Number,
      "room": String
    }
  ]
}

Caching data

This projects optimizes data storage and tries to serve them as efficiently as possible. The two features that handle data, the related videos and messages, are stored and managed differently.

Related cache
It is created when the user opens a room that has not been cached previously. The cache has the following structure:

{
  "roomId": [
    {
        "kind": "youtube#searchResult",
        "etag": etag,
        "id": {
            "kind": string,
            "videoId": string,
            "channelId": string,
            "playlistId": string
        },
        "snippet": {
            "publishedAt": datetime,
            "channelId": string,
            "title": string,
            "description": string,
            "thumbnails": {
            (key): {
                "url": string,
                "width": unsigned integer,
                "height": unsigned integer
            }
            },
            "channelTitle": string,
            "liveBroadcastContent": string
        }
    }
  ]
}

While the related videos is a nice-to-have feature, it is not essential to the core functionality. Therefore, it has been decided to load the related cache in async. Furthermore, in consideration to the API quota limit, the cache is hosted on a remote, cloud-based Redis database. This does add some latency; the application fetches data over the internet, transforms the data, then sends it to the client over the internet.

Messages cache
The messages are cached in a local JSON file called messagesCache with the following structure:

{
  "roomName": [
    {
      "name": String,
      "message": String,
      "timestamp": Number,
      "room": String
    }
  ]
}

The chat functionality is instant messaging therefore it is essential to fetch, save, and send data as fast as possible. That considered, it has been decided that storing messages in a local file is the preferred method.

🤖 Installation

Dependencies

Get a YouTube API key
Requirements:

  • Google account
  • Redis database

Get your Youtube API key:

  1. Open the cloud console link
  2. Create a project
  3. Open the project
  4. Go to the API Console
  5. Enable YouTube Data API v3
  6. Copy your API key to the .env file in project root

Set up Redis database:

  • Local:
  • Cloud (preferred method)
    1. Create an account at RedisLabs
    2. Copy credentials to the .env in project root

Run the project:

  1. Install dependencies npm install
  2. Run project npm start

🤝 Sources

📝 License

GPLv3