Skip to content

Pocket-Watch/watch-locally

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Watch Locally

Webpage preview

Preface

This is a watch party website. Is it like the others? No.
There are many alternatives, yet the majority of them suffers from the same recurring issues, such as:

  • poor performance (laggy sliders, stuttery animations)
  • poor design choices or silly limitations
  • plenty of bugs and issues which, if reported, are usually ignored, swept aside and never resolved
  • no backwards support (nowadays web developers can barely support the latest browser release)
  • chromium only (because other browser engines don't exist)
  • slow backends written in JS or other scripting languages
  • little to no support for mobile devices (no progress bar, no subtitles, extension-based)
  • glaring synchronization issues across multiple clients
  • lack of server-side support for bypassing CORS

The goals of this project

  • open-source
  • cross-browser compatibility
  • cross-device compatibility (quality experience for mobile users, hence the name - pocket)
  • compatibility with older browsers (4 years back)
  • minimal dependencies
  • no JS frameworks
  • avoiding needlessly complicated or bloated code (let's keep it sane)
  • fighting around browser-specific quirks (lack of standardized slider customization, cues stacking)

Prerequisites

  • Go version 1.22 (released in Feb 2024) or newer (supporting slices and sync.Map)
  • Any major browser version released in 2021 or later (due to CSS features)

Components

Running

Adjust the build script corresponding to your platform by setting -ip and -port arguments. Then execute it:

Windows

build.bat

Linux

./build.sh

https - How to generate SSL keys

In order to secure incoming and outgoing traffic TLS is crucial

openssl req -newkey rsa:4096  -x509  -sha512  -days 365 -nodes -out certificate.pem -keyout privatekey.pem

Git comes with many preinstalled binaries among which is openssl
On Windows it can be found at Git/usr/bin/openssl.exe where Git is git's root installation directory

Additionally, to have your domain verified you can use a free certificate authority like: https://letsencrypt.org

Custom player

For a watch party to work smoothly it's crucial for the player to be able to distinguish between user-initiated and programmatic playback. Unfortunately the <video> element does not provide a mechanism to accomplish that and player libraries usually don't provide a callback. The most common approach is to try and control the state between events, such as onclick and onplay. Due to synchronization issues between clients and the single-threaded nature of JS it eventually leads to a disorganized codebase. Furthermore, most video players dispatch events by firing events asynchronously, causing them to be out of order.
Proper subtitle support is scarce:

  • no dynamic loading (only static)
  • no customization menus
  • no support for SRT (in-built)
  • no support for shifting

The default web subtitle API doesn't shine either. See WEBVTT API problems.

Prerequisites

Include player stylesheet - player.css

<link rel="stylesheet" href="web/css/player.css">

Optionally preload player script - custom_player.js

<body>
    // Webpage elements
    <script type="module" src="web/js/custom_player.js"></script>
</body>

Import the symbols in Javascript to use them directly

import {Player, Options} from "./js/custom_player.js";

API usage examples

Initialize and attach the player to any video element

let videoElement = document.getElementById("cats-video");
let options = new Options();
// Hide the buttons you don't need
options.hideNextButton = true;
options.hideDownloadButton = true;
// Change seeking to 10s
options.seekBy = 10;
// Passing options is optional
let player = new Player(videoElement, options);

Set title, video track and subtitle

player.setTitle("Agent327");
player.setVideoTrack("https://video.blender.org/static/web-videos/264ff760-803e-430e-8d81-15648e904183-720.mp4")
player.setSubtitle("subtitles/Agent327.vtt")

Instant callbacks:

// They're dispatched synchronously
player.onControlsPlay(() => {
    player.setToast("You clicked play.");
})

player.onControlsPause(() => {
    player.setToast("You clicked pause.");
})

player.onControlsSeeked((timestamp) => {
    player.setToast("You seeked to " + timestamp.toFixed(3));
})
// Toasts appear in the top right corner as text, also in fullscreen

Custom subtitle implementation

  • support for parsing SRT and VTT
  • proper cue content sanitization with DOMParser
  • addressed bottlenecks related to shifting, setting and parsing
  • ability to import subtitle files from disk
// Adds a subtitle and doesn't show it
player.addSubtitle("subtitles/Subtitle0.srt");
// Sets a subtitle (shows it)
player.setSubtitle("subtitles/Subtitle1.vtt");
// Shows the subtitle at the given index
player.enableSubtitleTrackAt(0);
// Shifts the currently selected subtitle (either backwards or forwards) by the given amount of seconds 
player.shiftCurrentSubtitleTrackBy(-5);

Problems with the standard subtitle API

It is terrible

  • The performance of hiding or showing a track is astonishingly horrible in Firefox

    • hiding a track with 3000 cues on a modern CPU takes about 3000 ms causing UI to be unresponsive
    • when a track is shown it takes roughly the same amount of time it took to hide it
    • adding more tracks only worsens the performance, which also progressively slows down VTT load times

  • Inconsistent styling across browsers

    • bouncy VTTCue.line setting on Firefox (does bound checks, ensuring cue stays within view)
    • changing style.fontSize in CSS rule may easily cause subtitles to go out of view on Firefox

  • Confusing and poorly designed API

    • no ability to set a track, instead you control TextTrack.mode for each track separately
    • a CSS stylesheet must be used for styling ::cue (not ideal for dynamic use cases)
    • dysfunctional or misnamed properties like:
      • VTTCue.vertical - represents the cue's writing direction, (could be writingDirection?)
      • VTTCue.line - in reality represents the vertical position of a cue
      • VTTCue.snapToLines - where false causes VTTCue.line to be interpreted as a % of the video size.
      • VTTCue.size - size as a % of the video size (yet it does not change the font size)
    • video.addTextTrack method must be used in Chromium otherwise, manually adding cues will have no effect
    • no canonical method for adding TextTrack from url, <track> element must be created and appended to <video>
    • textTracks don't maintain order if a track is appended internally (index corresponds to its position as a <track>)

  • No standardized approach to shifting causing inefficient solutions

    • every cue must be shifted in a shift-dependent order otherwise, the cues are instantly reordered
    • some subtitle languages (with 1000+ cues) cause dramatic performance drops during shifting
    • cues on Firefox often stack (pile on top of each other) after shifting and stay on screen after end time

iPhone's fullscreen and video controls nonsense

<video> element error differences

CHROMIUM FIREFOX
MEDIA_ELEMENT_ERROR: Format error 404: Not found
Empty src attribute NS_ERROR_DOM_INVALID
DEMUXER_ERROR_COULD_NOT_OPEN Failed to init decoder