Skip to content

Instantly share code, notes, and snippets.

@Tushar-N
Last active April 19, 2022 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tushar-N/efdc30dcdcca2308167a90cb4f55bab8 to your computer and use it in GitHub Desktop.
Save Tushar-N/efdc30dcdcca2308167a90cb4f55bab8 to your computer and use it in GitHub Desktop.
Video player with live chat playback
Video player with live chat playback
import re
import json
def parse_timestamp(ts):
if ts.count(':') == 1:
ts = f'0:{ts}'
ts = list(map(int, ts.split(':')))
t = ts[0]*3600 + ts[1]*60 + ts[2]
return t
'''
raw_chat_data copied/pasted from youtube. Example:
5:45
Person 1
​First comment yay!!!
17:06
Person 2
​Hi mom 🙂
19:01
Person 1
​This is a terrible video
'''
chat_data = open('raw_chat_data').read().strip().replace('\u200b', '')
chat_data = re.findall('([0-9]+:[0-9]+:?[0-9]+)\n(.*)\n(.*)\n', chat_data)
chat_data = [(parse_timestamp(x[0]),) + x for x in chat_data]
with open('chat_data.js', 'w') as f:
chat_json = json.dumps(chat_data, indent=2)
f.write(f'const chat_data = {chat_json}')
html {
background-color:#181818
}
.container {
width: 100%;
overflow: hidden;
}
#video-div {
width: 1440;
float: left;
}
#chatbox-div {
margin-left: 1440;
border-style: solid;
border-color: #aaaaaa;
height: 805px;
overflow: scroll;
}
::-webkit-scrollbar {
width: 6px;
border-left: 1px solid #aaaaaa;
}
::-webkit-scrollbar-thumb {
background-color: #aaaaaa;
}
#chatbox-div ul {
list-style-type:none;
margin: 0px;
padding: 4px;
}
#chatbox-div li {
padding: 4px;
}
.ts {
font-size: 11px;
color: rgba(255, 255, 255, 0.54);
}
.name {
color: rgba(255, 255, 255, 0.7);
font-weight: 500;
}
.comment {
color: #fff;
}
.hidden {
display: none;
}
<html>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="chat_data.js"></script>
<script src="player.js"></script>
<link rel="stylesheet" href="player.css">
</head>
<body>
<div class="container">
<div id="video-div">
<video id="video" width="1440" controls>
<source src="video.mp4" type="video/mp4">
</video>
</div>
<div id="chatbox-div">
<ul id="chatbox"><ul>
</div>
</div>
</body>
</html>
// ----------------------------------------------------------------------------- //
// util
// ----------------------------------------------------------------------------- //
function bisect(sortedList, el){
if(!sortedList.length) return 0;
if(sortedList.length == 1) {
return el > sortedList[0] ? 1 : 0;
}
let lbound = 0;
let rbound = sortedList.length - 1;
return bisect(lbound, rbound);
// note that this function depends on closure over lbound and rbound
// to work correctly
function bisect(lb, rb){
if(rb - lb == 1){
if(sortedList[lb] < el && sortedList[rb] >= el){
return lb + 1;
}
if(sortedList[lb] == el){
return lb;
}
}
if(sortedList[lb] > el){
return 0;
}
if(sortedList[rb] < el){
return sortedList.length
}
let midPoint = lb + (Math.floor((rb - lb) / 2));
let midValue = sortedList[midPoint];
if(el <= midValue){
rbound = midPoint
}
else if(el > midValue){
lbound = midPoint
}
return bisect(lbound, rbound);
}
}
// ----------------------------------------------------------------------------- //
// player
// ----------------------------------------------------------------------------- //
// chat data comes from chat_data.js
let timestamps = chat_data.map(x => x[0])
let last_visible = -1
function populate_chatbox(){
let chatbox = $('#chatbox')
for (let i=0; i<chat_data.length; i++){
let el = $(`
<li class="hidden" id="chat-${i}">
<span class="ts">${chat_data[i][1]}</span>
<span class="name">${chat_data[i][2]}</span>
<span class="comment">${chat_data[i][3]}</span>
</li>
`)
chatbox.append(el)
}
}
function update_comments(curr_idx, last_idx){
if (curr_idx < last_idx){
for (let i=curr_idx+1; i<chat_data.length; i++){
$(`#chat-${i}`).addClass('hidden')
}
}
else if (curr_idx > last_idx){
for (let i=last_idx; i<=curr_idx; i++){
$(`#chat-${i}`).removeClass('hidden')
}
}
let chatbox = $('#chatbox-div')
chatbox.scrollTop(chatbox.prop('scrollHeight'));
}
function init_video_listener(){
$('#video').on('timeupdate', function() {
let idx = bisect(timestamps, this.currentTime) - 1
if (idx != last_visible){
update_comments(idx, last_visible)
last_visible = idx
}
});
}
$(document).ready(function(){
populate_chatbox()
init_video_listener()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment