anandtrex / collabdraw Goto Github PK
View Code? Open in Web Editor NEWHTML5 collaborative whiteboard
Home Page: http://anandtrex.github.io/collabdraw
License: Other
HTML5 collaborative whiteboard
Home Page: http://anandtrex.github.io/collabdraw
License: Other
Hi anandtrex,
Thanks very much for posting the code to this. I checked out the demo and it looks like a really neat app. I was trying to install this on Ubuntu and ran into an error while following your steps. When I ran ./run_tests.sh I get "ERROR: Failure: ImportError (No module named 'cairo._cairo')" and both tests fail. Do you by chance know how I could fix this? I've tried virtually everything.
Thanks a lot,
Arun
I am using your collabdraw application. It is very nice app. I have successfully created the session urls. As shown below,
import logging
import json
import os
import threading
import subprocess
import uuid
from zlib import compress
from urllib.parse import quote
from base64 import b64encode
import tornado.websocket
import tornado.web
import redis
from pystacia import read
from tools import hexColorToRGB, createCairoContext
import config
class RealtimeHandler(tornado.websocket.WebSocketHandler):
room_name = ''
token = ''
paths = []
redis_client = None
page_no = 1
num_pages = 1
def construct_key(self, namespace, key, *keys):
publish_key = ""
if len(keys) == 0:
publish_key = "%s:%s" % (namespace, key)
else:
publish_key = "%s:%s:%s" % (namespace, key, ":".join(keys))
return publish_key
def redis_listener(self, room_name, page_no):
self.logger.info("Starting listener thread for room %s" % room_name)
rr = redis.Redis(host=config.REDIS_IP_ADDRESS, port=config.REDIS_PORT, db=1)
r = rr.pubsub()
r.subscribe(self.construct_key(room_name, page_no))
for message in r.listen():
for listener in self.application.LISTENERS.get(room_name, {}).get(page_no, []):
self.logger.debug("Sending message to room %s" % room_name)
listener.send_message(message['data'])
def open(self):
self.logger = logging.getLogger('websocket')
self.logger.info("Open connection")
self.send_message(self.construct_message("ready"))
self.redis_client = redis.Redis(host=config.REDIS_IP_ADDRESS, db=2)
def on_message(self, message):
m = json.loads(message)
event = m.get('event', '').strip()
data = m.get('data', {})
self.logger.debug("Processing event %s" % event)
if not event:
self.logger.error("No event specified")
return
if event == "init":
self.logger.info("Initializing with room name %s" % self.room_name)
room_name = data.get('room', '')
if not room_name:
self.logger.error("Room name not provided. Can't initialize")
return
page_no = data.get('page', '1')
token = data.get('token', '')
find_token = self.redis_client.exists(token)
if find_token:
token = ''
self.init(room_name, page_no, token)
elif event == "draw-click":
singlePath = data['singlePath']
if not self.paths:
self.logger.debug("None")
self.paths = []
self.paths.extend(singlePath)
self.broadcast_message(self.construct_message("draw", {'singlePath': singlePath}))
if not self.token:
self.redis_client.set(self.construct_key(self.room_name, self.page_no), self.paths)
else:
self.redis_client.set(self.construct_key('token', self.token), self.paths)
elif event == "save":
self.redis_client.set(self.construct_key(self.room_name, self.page_no), self.paths)
self.init(self.room_name, self.page_no)
elif event == "savesession":
token = data['token']
self.paths = []
self.redis_client.set(self.construct_key('token', token), self.paths)
self.send_message(self.construct_message("token", {'token': token}))
#self.init(self.room_name, self.page_no)
elif event == "clear":
self.broadcast_message(self.construct_message("clear"))
self.redis_client.delete(self.construct_key(self.room_name, self.page_no))
elif event == "get-image":
if self.room_name != data['room'] or self.page_no != data['page']:
self.logger.warning("Room name %s and/or page no. %s doesn't match with current room name %s and/or page no. %s. Ignoring" % (data['room'],
data['page'], self.room_name, self.page_no))
image_url, width, height = self.get_image_data(self.room_name, self.page_no)
self.send_message(self.construct_message("image", {'url': image_url,
'width': width, 'height': height}))
elif event == "video":
self.make_video(self.room_name, self.page_no)
elif event == "new-page":
self.logger.info("num_pages was %d" % self.num_pages)
self.redis_client.set(self.construct_key("info", self.room_name, "npages"),
self.num_pages + 1)
self.num_pages += 1
self.logger.info("num_pages is now %d" % self.num_pages)
self.init(self.room_name, self.num_pages)
def on_close(self):
self.leave_room(self.room_name)
def construct_message(self, event, data = {}):
m = json.dumps({"event": event, "data": data})
return m
def broadcast_message(self, message):
if not self.token:
self.leave_room(self.room_name, False)
self.redis_client.publish(self.construct_key(self.room_name, self.page_no), message)
self.join_room(self.room_name)
else:
print('fe................##########')
self.leave_room('token', False)
self.redis_client.publish(self.construct_key('token', self.token), message)
self.join_room('token')
self.application.LISTENERS.setdefault('token', {}).setdefault(self.token, []).append(self)
def send_message(self, message):
if type(message) == type(b''):
self.logger.info("Decoding binary string")
message = message.decode('utf-8')
elif type(message) != type(''):
self.logger.info("Converting message from %s to %s" % (type(message),
type('')))
message = str(message)
message = b64encode(compress(bytes(quote(message), 'utf-8'), 9))
self.write_message(message)
def leave_room(self, room_name, clear_paths = True):
if not self.token:
self.logger.info("Leaving room %s" % room_name)
if self in self.application.LISTENERS.get(room_name, {}).get(self.page_no, []):
self.application.LISTENERS[room_name][self.page_no].remove(self)
if clear_paths:
self.paths = []
else:
self.logger.info("Leaving room %s" % room_name)
if self in self.application.LISTENERS.get(room_name, {}).get(self.token, []):
self.application.LISTENERS[room_name][self.token].remove(self)
if clear_paths:
self.paths = []
def join_room(self, room_name):
if not self.token:
self.logger.info("Joining room %s" % room_name)
self.application.LISTENERS.setdefault(room_name, {}).setdefault(self.page_no, []).append(self)
else:
self.logger.info("Joining room %s" % room_name)
self.application.LISTENERS.setdefault(room_name, {}).setdefault(self.token, []).append(self)
def init(self, room_name, page_no, token):
self.token = token
if not token:
self.logger.info("Initializing %s and %s" % (room_name, page_no))
if room_name not in self.application.LISTENERS or page_no not in self.application.LISTENERS[room_name]:
t = threading.Thread(target=self.redis_listener, args=(room_name, page_no))
t.start()
self.application.LISTENER_THREADS.setdefault(room_name, {}).setdefault(page_no, []).append(t)
self.leave_room(self.room_name)
self.room_name = room_name
self.page_no = page_no
self.join_room(self.room_name)
n_pages = self.redis_client.get(self.construct_key("info", self.room_name, "npages"))
if n_pages:
self.num_pages = int(n_pages.decode('utf-8'))
# First send the image if it exists
image_url, width, height = self.get_image_data(self.room_name, self.page_no)
self.send_message(self.construct_message("image", {'url': image_url,
'width': width, 'height': height}))
# Then send the paths
p = self.redis_client.get(self.construct_key(self.room_name, self.page_no))
if p:
self.paths = json.loads(p.decode('utf-8').replace("'", '"'))
else:
self.paths = []
self.logger.info("No data in database")
self.send_message(self.construct_message("draw-many",
{'datas': self.paths, 'npages': self.num_pages}))
else:
#if 'token' not in self.application.LISTENERS or token not in self.application.LISTENERS['token']:
# t = threading.Thread(target=self.redis_listener, args=('token', token))
# t.start()
# self.application.LISTENER_THREADS.setdefault('token', {}).setdefault(token, []).append(t)
self.token = token
self.leave_room('token', False)
self.join_room('token')
p = self.redis_client.get(self.construct_key('token', token))
if p:
self.paths = json.loads(p.decode('utf-8').replace("'", '"'))
else:
self.paths = []
self.logger.info("No data in database")
self.send_message(self.construct_message("draw-many",
{'datas': self.paths, 'npages': 0}))
def get_image_data(self, room_name, page_no):
image_url = "files/" + room_name + "/" + str(page_no) + "_image.png";
image_path = os.path.realpath(__file__).replace(__file__, '') + image_url
try:
image = read(image_path)
except IOError as e:
self.logger.error("Error %s while reading image at location %s" % (e, image_path))
return '', -1, -1
width, height = image.size
return image_url, width, height
def make_video(self, room_name, page_no):
p = self.redis_client.get(self.construct_key(room_name, page_no))
os.makedirs('tmp', exist_ok=True)
prefix = 'tmp/'+str(uuid.uuid4())
if p:
points = json.loads(p.decode('utf-8').replace("'",'"'))
i = 0
c = createCairoContext(920, 550)
for point in points:
c.set_line_width(float(point['lineWidth'].replace('px','')))
c.set_source_rgb(*hexColorToRGB(point['lineColor']))
if point['type'] == 'dragstart' or point['type'] == 'touchstart':
c.move_to(point['oldx'], point['oldy'])
elif point['type'] == 'drag' or point['type'] == 'touchmove':
c.move_to(point['oldx'], point['oldy'])
c.line_to(point['x'], point['y'])
c.stroke()
f = open(prefix+"_img_"+str(i)+".png", "wb")
c.get_target().write_to_png(f)
f.close()
i += 1
video_file_name = prefix+'_video.mp4'
retval = subprocess.call(['ffmpeg', '-f', 'image2', '-i', prefix+'_img_%d.png', video_file_name])
self.logger.info("Image for room %s and page %s successfully created. File name is %s" % (room_name, page_no, video_file_name))
if retval == 0:
# Clean up if successfull
cleanup_files = prefix+'_img_*'
self.logger.info("Cleaning up %s" % cleanup_files)
subprocess.call(['rm', cleanup_files])
But I got the error when draw the whiteboard app. I am using url this way http://localhost:4000/test/ggxeu and error is,
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.2/threading.py", line 740, in _bootstrap_inner
self.run()
File "/usr/lib/python3.2/threading.py", line 693, in run
self._target(_self._args, *_self._kwargs)
File "/home/nyros/Desktop/python3/collabdraw/websockethandler.py", line 43, in redis_listener
listener.send_message(message['data'])
File "/home/nyros/Desktop/python3/collabdraw/websockethandler.py", line 147, in send_message
self.write_message(message)
File "/home/nyros/Desktop/python3/venv3/lib/python3.2/site-packages/tornado/websocket.py", line 165, in write_message
self.ws_connection.write_message(message, binary=binary)
AttributeError: 'NoneType' object has no attribute 'write_message'
Please solve my problem.
When I walk through the instructions, the installation fails at step: pip install -r requirements.txt
I´ve tried this with Ubuntu 16.04 and Debian Jessie.
(venv) root@whiteboard:~/collabdraw# pip install -r requirements.txt
Collecting git+http://anongit.freedesktop.org/git/pycairo (from -r requirements.txt (line 6))
Cloning http://anongit.freedesktop.org/git/pycairo to /tmp/pip-suwlbwj5-build
Collecting decorator==3.4.0 (from -r requirements.txt (line 1))
Downloading decorator-3.4.0.tar.gz
Collecting pystacia==0.1 (from -r requirements.txt (line 2))
Downloading pystacia-0.1.tar.gz
Collecting redis==2.7.2 (from -r requirements.txt (line 3))
Downloading redis-2.7.2.tar.gz (50kB)
100% |################################| 51kB 3.5MB/s
Collecting six==1.2.0 (from -r requirements.txt (line 4))
Downloading six-1.2.0.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/root/collabdraw/venv/lib/python3.4/site-packages/setuptools/init.py", line 12, in
import setuptools.version
File "/root/collabdraw/venv/lib/python3.4/site-packages/setuptools/version.py", line 1, in
import pkg_resources
File "/root/collabdraw/venv/lib/python3.4/site-packages/pkg_resources/init.py", line 49, in
from six.moves import urllib, map, filter
ImportError: cannot import name 'urllib'
----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-ql40ch35/six/
(venv) root@whiteboard:~/collabdraw#
I am trying to install collabdraw and followed the requirements now I am getting this error. Did I miss something?
[collabdraw]# python main.py
Traceback (most recent call last):
File "main.py", line 9, in <module>
from websockethandler import RealtimeHandler
File "/root/collabdraw/websockethandler.py", line 14, in <module>
from pystacia import read
File "/root/virtualenvt/draw/lib/python3.2/site-packages/pystacia/__init__.py", line 155, in <module>
import pystacia.api.enum as enum_api
File "/root/virtualenvt/draw/lib/python3.2/site-packages/pystacia/api/enum.py", line 517, in <module>
from pystacia.util import memoized
File "/root/virtualenvt/draw/lib/python3.2/site-packages/pystacia/util.py", line 8, in <module>
from decorator import decorator
File "/root/virtualenvt/draw/lib/python3.2/site-packages/decorator-3.4.0-py3.2.egg/decorator.py", line 156
exec code in evaldict
^
SyntaxError: invalid syntax
Hi. Using both https://collabdraw.herokuapp.com/ and on a local install (Gentoo) give me the same behaviour - the wait spinner onyx-spinner and no response on any interface buttons. Is this application usable at the moment, considering no updates since October?
Just noticed now that the public URL does eventually go past the spinner and show me some quiggles that I presume someone else left there, but application is still nonresposive. Five minutes later local install is still spinning, no output on the console except favicon 404s.
Dependabot couldn't authenticate with https://pypi.python.org/simple/.
You can provide authentication details in your Dependabot dashboard by clicking into the account menu (in the top right) and selecting 'Config variables'.
Following the instructions which currently state:
RUNNING:
-------------
1. Run the redis server
2. Run "python main.py"
How do you run a redis server?
I just tried running "python main.py" and get an error message as follows:
# python main.py
Listening on port 5000
Open connection
INFO:websocket:Open connection
WARNING:tornado.access:404 GET /favicon.ico (192.168.1.100) 0.52ms
WARNING:tornado.access:404 GET /favicon.ico (192.168.1.100) 0.44ms
Processing event init
DEBUG:websocket:Processing event init
Initializing with room name
INFO:websocket:Initializing with room name
Initializing one and 1
INFO:websocket:Initializing one and 1
Starting listener thread for room one
INFO:websocket:Starting listener thread for room one
Leaving room
INFO:websocket:Leaving room
Joining room one
INFO:websocket:Joining room one
ERROR:tornado.application:Uncaught exception in /realtime/
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 232, in connect
sock = self._connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 244, in _connect
sock.connect((self.host, self.port))
socket.error: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/redis/client.py", line 393, in execute_command
connection.send_command(*args)
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 306, in send_command
self.send_packed_command(self.pack_command(*args))
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 288, in send_packed_command
self.connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 235, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting localhost:6379. Connection refused.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 232, in connect
sock = self._connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 244, in _connect
sock.connect((self.host, self.port))
socket.error: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/tornado/websocket.py", line 303, in wrapper
return callback(*args, **kwargs)
File "/usr/local/src/collabdraw/websockethandler.py", line 68, in on_message
self.init(room_name, page_no)
File "/usr/local/src/collabdraw/websockethandler.py", line 149, in init
n_pages = self.redis_client.get(self.construct_key("info", self.room_name, "npages"))
File "/usr/lib64/python3.2/site-packages/redis/client.py", line 616, in get
return self.execute_command('GET', name)
File "/usr/lib64/python3.2/site-packages/redis/client.py", line 397, in execute_command
connection.send_command(*args)
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 306, in send_command
self.send_packed_command(self.pack_command(*args))
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 288, in send_packed_command
self.connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 235, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting localhost:6379. Connection refused.Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 232, in connect
sock = self._connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 244, in _connect
sock.connect((self.host, self.port))
socket.error: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/redis/client.py", line 1643, in execute_command
connection.send_command(*args)
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 306, in send_command
self.send_packed_command(self.pack_command(*args))
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 288, in send_packed_command
self.connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 235, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting localhost:6379. Connection refused.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 232, in connect
sock = self._connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 244, in _connect
sock.connect((self.host, self.port))
socket.error: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib64/python3.2/threading.py", line 740, in _bootstrap_inner
self.run()
File "/usr/lib64/python3.2/threading.py", line 693, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/src/collabdraw/websockethandler.py", line 38, in redis_listener
r.subscribe(self.construct_key(room_name, page_no))
File "/usr/lib64/python3.2/site-packages/redis/client.py", line 1696, in subscribe
return self.execute_command('SUBSCRIBE', *channels)
File "/usr/lib64/python3.2/site-packages/redis/client.py", line 1648, in execute_command
connection.connect()
File "/usr/lib64/python3.2/site-packages/redis/connection.py", line 235, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting localhost:6379. Connection refused.
Leaving room one
INFO:websocket:Leaving room one
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.