Python flask realtime in render html - python

How can i see data from realtime on my page? When I use these codes outside of flask, the listener waits for the data to come and prints it to the screen.
My python code:
import itertools
import time
from flask import Flask, Response, redirect, request, url_for
from TikTokLive import TikTokLiveClient
from TikTokLive.types.events import CommentEvent, ConnectEvent, GiftEvent
# Instantiate the client with the user's username
client: TikTokLiveClient = TikTokLiveClient(unique_id="#username")
app = Flask(__name__)
#client.on("connect")
async def on_connect(_: ConnectEvent):
print("Connected to Room ID:", client.room_id)
#client.on("gift")
async def on_gift(event: GiftEvent):
# If it's type 1 and the streak is over
if event.gift.gift_type == 1:
f"{event.user.uniqueId} sent {event.gift.repeat_count}x \"{event.gift.extended_gift.name}\" {event.gift.extended_gift.name}"
#app.route('/')
def index():
if request.headers.get('accept') == 'text/event-stream':
return Response(on_gift(GiftEvent), content_type='text/event-stream')
return redirect(url_for('static', filename='index.html'))
if __name__ == "__main__":
app.run(debug=True)
client.run()
My index.html page
<!doctype html>
<title>Server Send Events Demo</title>
<style>
#data {
text-align: center;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
if (!!window.EventSource) {
var source = new EventSource('/');
source.onmessage = function(e) {
$("#data").text(e.data);
}
}
</script>
<div id="data">nothing received yet</div>
My error

Related

How can I send data though socket-io without the client requesting first with python and flask

My goal is for my Flask server to send the client data either every three seconds, or when a function is called. To do this I am using SocketIO. However based on some example code I am working with, it seems that I can only send data after a client requests something. I don't want the client to have to 'poll' to find if there is new data, so I want the server to push it when it is ready.
Here is what I tried so far. (some of the code is unnecessary since it is based off an example) This should use socketio to push the time to the client every few seconds.
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
socket.on('my_response', function(msg, cb) {
$('#log').text( $('<div/>').text(msg.data).html());
if (cb)
cb();
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
Python
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
clients = []
def update():
time.sleep(1)
emit('my_response',
{'data': time.time},
room=clients[0])
t=threading.Thread(target=update)
#socket_.on('connect')
def handle_connect():
print('Client connected')
clients.append(request.sid)
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
#socket_.on('my_event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': message['data'], 'count': session['receive_count']})
#socket_.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': time.time},
broadcast=True)
socket_.run(app,port=8050)
I try to run it but it gives me the error RuntimeError: Working outside of request context.
I fixed my code by following this tutorial: https://www.shanelynn.ie/asynchronous-updates-to-a-webpage-with-flask-and-socket-io/
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
def update():
time.sleep(1)
socket_.emit('my_response',
{'data': time.time()},
namespace='/test')
print("emitted")
update()
t=threading.Thread(target=update)
#socket_.on('connect', namespace='/test')
def handle_connect():
print('Client connected')
if not t.isAlive():
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
socket_.run(app,port=8070)
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
console.log(("test"));
socket.on('my_response', function(msg) {
$('#time').text( $('<div/>').text(msg.data).html());
console.log(msg);
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
I would like to point out that using recursion in this case is not the best choice.
you call the update function inside the update and do not have the completion of this process.
the best option would be to use a loop(as done in the link you attached)
def update():
while True:
time.sleep(1)
socket_.emit('my_response', {'data': time.time()}, namespace='/test')
print("emitted")
t=threading.Thread(target=update)
also, it would be better to write "while is_work_var" instead of "while True"

how to get connected clients in flask

hi i need to display total number of connected clients on my flask app i write this code for checking connected and disconnected connections.
app = Flask(__name__)
socketio = SocketIO(app)
clients = []
#socketio.on('connect', namespace='/')
def connect():
clients.append(request.namespace)
#socketio.on('disconnect', namespace='/')
def disconnect():
clients.remove(request.namespace)
then i render template like this
return render_template_string(TABLE_TEMPLATE, data=data, clients=len(clients))
In html part i call like this
<h1>{{ clients }} </h1>
but on webpage it keep showing 0 even client is connect i get output from client and it is connected it should print 1 2 depends how many clients are connected. even if i print this print(len(clients)) it return 0. even my client is connect and i get output.
this is my updated code
from flask import Flask, request, render_template_string
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app, logge=True)
clients = 0
#socketio.on("connect", namespace="/")
def connect():
# global variable as it needs to be shared
global clients
clients += 1
# emits a message with the user count anytime someone connects
emit("users", {"user_count": clients}, broadcast=True)
#socketio.on("disconnect", namespace="/")
def disconnect():
global clients
clients -= 1
emit("users", {"user_count": clients}, broadcast=True)
TABLE_TEMPLATE = """
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var namespace = '/';
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
// Update the counter when a new user connects
socket.on('users', function(users) {
userCount = document.getElementById('user_counter');
userCount.innerHTML = users.user_count;
});
});
</script>
<h1 id='user_counter'></h1>
<style>
table, th, td {
border: 1px solid black;
}
</style>
<table style="width: 100%">
<thead>
<th>Client</th>
<th>IP</th>
<th>Status</th>
</thead>
<tbody>
{% for row in data %}
<tr>
<td><center>{{ row.client }}</td></center>
<td><center>{{ row.ip }}</td></center>
<td><center>{{ row.status }}</td></center>
</tr>
{% endfor %}
</tbody>
</table>
"""
#app.route("/device_add", methods=['POST'])
def device_add():
name = request.args.get('name')
with open('logs.log', 'a') as f:
f.write(f'{name} Connected USB from IP: {request.remote_addr} \n')
return 'ok'
#app.route("/device_remove", methods=['POST'])
def device_remove():
name = request.args.get('name')
with open('logs.log', 'a') as f:
f.write(f'{name} Disconnected USB from IP: {request.remote_addr}\n')
return 'ok'
#app.route("/", methods=['GET'])
def device_list():
keys = ['client', 'ip', 'status']
data = []
with open('logs.log', 'r') as f:
for line in f:
row = line.split()
data.append(dict(zip(keys, [row[0], row[-1], row[1]])))
return render_template_string(TABLE_TEMPLATE, data=data)
if __name__ == "__main__":
socketio.run(app)
Client Side :
import requests
import subprocess, string, time
import os
url = 'http://127.0.0.1:5000/'
name = os.uname()[1]
def on_device_add():
requests.post(f'{url}/device_add?name={name}')
def on_device_remove():
requests.post(f'{url}/device_remove?name={name}')
def detect_device(previous):
total = subprocess.run('lsblk | grep disk | wc -l', shell=True, stdout=subprocess.PIPE).stdout
time.sleep(3)
# if condition if new device add
if total > previous:
on_device_add()
# if no new device add or remove
elif total == previous:
detect_device(previous)
# if device remove
else:
on_device_remove()
# Infinite loop to keep client running.
while True:
detect_device(subprocess.run(' lsblk | grep disk | wc -l', shell=True , stdout=subprocess.PIPE).stdout)
After reading a bit of socket.io documentation I've managed to spot the problems in your code.
Not a problem per-se, but incrementing/decrementing an int counter is more than enough for this use case. Secondly, you don't have to pass that counter to the render_template call as you're basically passing the user count before the conenct event has had the opportunity to fire. You should emit a message (in this example with a users topic) that will inform your page that something has changed:
from flask import Flask, request, render_template_string
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app, logge=True)
clients = 0
#socketio.on("connect", namespace="/")
def connect():
# global variable as it needs to be shared
global clients
clients += 1
# emits a message with the user count anytime someone connects
emit("users", {"user_count": clients}, broadcast=True)
#socketio.on("disconnect", namespace="/")
def disconnect():
global clients
clients -= 1
emit("users", {"user_count": clients}, broadcast=True)
Moreover, you didn't open a connection to the socket in your template, this allows you to listen to the messages emitted by your socketio decorators and update all connected clients. You will also need to write a bit of javascript to specify that the counter needs to be updated anytime a user connects/disconnects.
<!-- Remember to import socketio library -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var namespace = '/';
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
// Update the counter when a new user connects
socket.on('users', function(users) {
userCount = document.getElementById('user_counter');
userCount.innerHTML = users.user_count;
});
});
</script>
<h1 id='user_counter'></h1>
<!-- the rest of your template -->
This being said, you don't need to pass the counter value in your render_template call.
Also, from flask-socketio docs, it seems to be good practice to start your app in the following way:
if __name__ == "__main__":
socketio.run(app)
Here a link to an edited version of your example.

Why are requests with same url processed synchronously?

I thought it was a quirk of the framework I was using, so I tested with another framework, but the result is the same: sending a burst of requests will process them asynchronously as expected when the urls are different, but will queue the requests with the same url and process them synchronously.
Why are some requests processed synchronously and some asynchronously?
Below the code to test with both Flask and CherryPy. Visiting localhost:5000 with Flask and localhost:8080 with CherryPy will load a page with JavaScript that will send a burst of 16 requests. The requests have an unused parameter in the query string which can have 4 different values, so the server will receive 4 different requests, each 4 times, for a total of 16 requests.
The server starts processing the first 4 requests for each parameter value, after finishing them will start processing the next 4 requests, etc.
Why aren't all 16 requests processed at the same time?
Flask
import flask
import time
import threading
import datetime
app = flask.Flask(__name__)
def log(txt):
print(' {} {:6d} {}'.format(datetime.datetime.now().strftime('%H:%M:%S.%f'),
threading.current_thread().ident,
txt))
#app.route('/')
def index():
log('index')
return """<!DOCTYPE HTML>
<html>
<head>
<script>
setTimeout(function(){{
for(i=0; i<16; i++) {{
xhttp = new XMLHttpRequest();
xhttp.open("GET", "test/" + i%4, true);
xhttp.send();
}}
}}, 2000);
</script>
</head>
<body>
hello
</body>
</html>"""
#app.route('/test/<x>')
def test(x):
log('test{}'.format(x))
time.sleep(3)
log(' test{}'.format(x))
return 'OK'
app.run(threaded=True)
CherryPy
import cherrypy
import time
import datetime
import threading
def log(txt):
print(' {} {:6d} {}'.format(datetime.datetime.now().strftime('%H:%M:%S.%f'),
threading.current_thread().ident,
txt))
class Root:
#cherrypy.expose
def index(self):
log('index')
return """<!DOCTYPE HTML>
<html>
<head>
<script>
setTimeout(function(){{
for(i=0; i<16; i++) {{
xhttp = new XMLHttpRequest();
xhttp.open("GET", "test?x=" + i%4, true);
xhttp.send();
}}
}}, 2000);
</script>
</head>
<body>
hello
</body>
</html>"""
#cherrypy.expose
def test(self, x):
log('test{}'.format(x))
time.sleep(3)
log(' test{}'.format(x))
return 'OK'
if __name__ == '__main__':
cherrypy.quickstart(Root())

Flask and Flask-SocketIO [duplicate]

This question already has an answer here:
Flask: A RESTful API and SocketIO Server
(1 answer)
Closed 7 years ago.
This seems like a very simple problem but it's got me confused nonetheless, I have a Flask application that serves up a webpage and communicates with that page via Socket.io. The Flask application looks like this:
app = Flask(__name__)
socketio = SocketIO(app)
#socketio.on_error()
def error_handler(e):
print e
#this fires
#socketio.on("connect")
def connect():
print "connected"
#this does not
#socketio.on('test')
def test_handler(message):
print "TEST WORKS"
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')
socketio.run(app)
My page itself is very simple:
<!doctype html>
<html>
<head>
<title>Flask Socket.IO test</title>
<style>
body { font: 13px Helvetica, Arial; }
</style>
<script src="socket.io-1.4.3.js"></script>
<script src="jquery-2.2.0.min.js"></script>
<script>
var socket = io("192.168.42.1:5000");
socket.on('connect', function() {
console.log(" connected ");
});
$(document).ready( function() {
$( '.startBtn' ).click(function() {
console.log("ok");
socket.emit('test', {data:"start!"});
});
});
</script>
</head>
<body>
<div class="startBtn">
<h1>START</h1>
</div>
</body>
</html>
I see on both sides that they connect (i.e. the connect event is triggered on both sides) but nothing that I send from the page to the server is received. I'm guessing somehow I have things misconfigured but the connection being established makes me think otherwise.
So the problem seems to be in how I was setting up the Flask application and socketio. Changing it to this:
app = Flask(__name__)
socketio = SocketIO(app, async_mode='eventlet')
#app.route('/')
def index():
return render_template('index.html')
#socketio.on('test')
def josh_test(message):
print "test"
if __name__ == '__main__':
socketio.run(app, debug=True)
it now all works fantastically with no changes to the HTML file. My previous version had:
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')
socketio.run(app)
and that was what was causing the problems.

socket.io upgraded from 0.9.6 to 1.3.5 sends wrong requests

I had a small sandbox server on gevent-socketio.
The backend was
from flask import Flask, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.server import SocketIOServer
class FirstNamespace(BaseNamespace):
def on_make_upper_hook(self, msg):
response = {'response from make_upper':msg.upper()}
self.emit('response_channel', response)
return True
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def index():
return render_template('index.html')
#app.route('/socket.io/')
#app.route('/socket.io/<path:remaining>')
def socketio_endpoint(remaining=None):
print('remaining path: {}'.format(remaining))
socketio_manage(request.environ,
{'/chat': FirstNamespace})
return 'ok'
if __name__ == '__main__':
SocketIOServer(('0.0.0.0', 8080), app,
resource="socket.io").serve_forever()
And the frontend (with socket.io 0.9.6) was
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="static/socket.io.js"></script>
<script>
var socket = io.connect('/chat');
socket.on('connect', function () {
console.log('socket connected');
});
</script>
</head>
<body>
</body>
</html>
In the browser console I saw that connection went fine. The server was also fine:
127.0.0.1 - - [2015-07-05 10:30:55] "GET / HTTP/1.1" 200 420 0.006791
remaining path: 1/websocket/683868734428
But!
When I tried to upgrade socket.io like so:
<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
<script>
var socket = io('http://localhost:8080/chat');
socket.on('connect', function () {
console.log('socket connected');
});
</script>
I started to get client errors
GET http://localhost:8080/socket.io/?EIO=3&transport=polling&t=1436081649609-0 500 (Internal Server Error)
and server errors:
File "/Users/1111/.virtualenvs/stuff/lib/python2.7/site-packages/socketio/__init__.py",
line 67, in socketio_manage
socket = environ['socketio']
KeyError: 'socketio'
So somehow my precious socketio key disappeared from request.environ and I don't know how to get it back. How to fix this?

Categories

Resources