Last active
April 19, 2022 21:35
-
-
Save Tushar-N/efdc30dcdcca2308167a90cb4f55bab8 to your computer and use it in GitHub Desktop.
Video player with live chat playback
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Video player with live chat playback |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----------------------------------------------------------------------------- // | |
// 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