Be notified in real-time on any activity (for instance connection, cursor move, typing...) #3

Open
opened 2024-03-01 17:34:56 +01:00 by Benjamin_Loison · 82 comments

This would enable us to backup Overleaf content without regular git pull, see OC3K crontab.

Related to #11, Overleaf_history_exporter and Improve_websites_thanks_to_open_source/issues/770.

+74

This would enable us to backup Overleaf content without regular `git pull`, see OC3K `crontab`. Related to #11, [Overleaf_history_exporter](https://codeberg.org/Benjamin_Loison/Overleaf_history_exporter) and [Improve_websites_thanks_to_open_source/issues/770](https://codeberg.org/Benjamin_Loison/Improve_websites_thanks_to_open_source/issues/770). +74
Benjamin_Loison changed title from Be notified in real-time on any activity (for instance cursor move, typing...) to Be notified in real-time on any activity (for instance connection, cursor move, typing...) 2024-03-01 20:44:07 +01:00
Author
Owner

DuckDuckGo and Google search Overleaf WebSocket.

DuckDuckGo and Google search *Overleaf WebSocket*.
Author
Owner

Most results seem about hosting our own instance.

https://www.overleaf.com/socket.io/1/websocket/

XXXXXXXXXXXXXXXXXXXX:60:60:websocket,flashsocket,htmlfile,xhr-polling,jsonp-polling

first part changes everytime. Same for https://www.overleaf.com/socket.io/1/ and https://www.overleaf.com/socket.io/1/uzgxahft/ uzgxahft being a random string I generated.

There is no further request on Firefox when loading this webpage.

Most results seem about hosting our own instance. https://www.overleaf.com/socket.io/1/websocket/ ``` XXXXXXXXXXXXXXXXXXXX:60:60:websocket,flashsocket,htmlfile,xhr-polling,jsonp-polling ``` first part changes everytime. Same for https://www.overleaf.com/socket.io/1/ and https://www.overleaf.com/socket.io/1/uzgxahft/ `uzgxahft` being a random string I generated. There is no further request on Firefox when loading this webpage.
Author
Owner

Should investigate these keywords. Well these seem to be old technologies except websocket.

Should investigate these keywords. Well these seem to be old technologies except `websocket`.
Author
Owner

Let us read Wikipedia: WebSocket (1265617417). I quite completely read it.

Let us read [Wikipedia: WebSocket (1265617417)](https://en.wikipedia.org/w/index.php?title=WebSocket&oldid=1265617417). I quite completely read it.
Author
Owner
Related to [Benjamin-Loison/Contracts-management-website/issues/1](https://github.com/Benjamin-Loison/Contracts-management-website/issues/1).
Author
Owner
curl -I https://www.overleaf.com/socket.io/1/websocket/
Output:
HTTP/2 200 
content-type: text/plain
date: Sun, 02 Mar 2025 23:30:05 GMT
via: 1.1 google
set-cookie: GCLB=XXXXXXXXXXXXXXXX; path=/; HttpOnly
alt-svc: clear

```bash curl -I https://www.overleaf.com/socket.io/1/websocket/ ``` <details> <summary>Output:</summary> ``` HTTP/2 200 content-type: text/plain date: Sun, 02 Mar 2025 23:30:05 GMT via: 1.1 google set-cookie: GCLB=XXXXXXXXXXXXXXXX; path=/; HttpOnly alt-svc: clear ``` </details>
Author
Owner

DuckDuckGo search "Protocol "wss" not supported or disabled in libcurl".

The Stack Overflow answer 47861907:

Shell:
benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c "wss://ws-feed.gdax.com"
error: Unexpected server response: 526
> benjamin_loison@Benjamin-Loison-HP-Debian:~$
Shell:
benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/'
error: Unexpected server response: 502
> benjamin_loison@Benjamin-Loison-HP-Debian:~$
Shell:
benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/'
Connected (press CTRL+C to quit)
< 7:::1+0
Disconnected (code: 1006, reason: "")
DuckDuckGo search `"Protocol "wss" not supported or disabled in libcurl"`. [The Stack Overflow answer 47861907](https://stackoverflow.com/a/47861907): <details> <summary>Shell:</summary> ``` benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c "wss://ws-feed.gdax.com" error: Unexpected server response: 526 > benjamin_loison@Benjamin-Loison-HP-Debian:~$ ``` </details> <details> <summary>Shell:</summary> ``` benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/' error: Unexpected server response: 502 > benjamin_loison@Benjamin-Loison-HP-Debian:~$ ``` </details> <details> <summary>Shell:</summary> ``` benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/' Connected (press CTRL+C to quit) < 7:::1+0 Disconnected (code: 1006, reason: "") ``` </details>
Author
Owner
Shell:
benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX'
error: Unexpected server response: 502
> benjamin_loison@Benjamin-Loison-HP-Debian:~$
Shell:
benjamin_loison@Benjamin-Loison-HP-Debian:~$  wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX'
Connected (press CTRL+C to quit)
Disconnected (code: 1006, reason: "")
<details> <summary>Shell:</summary> ``` benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX' error: Unexpected server response: 502 > benjamin_loison@Benjamin-Loison-HP-Debian:~$ ``` </details> <details> <summary>Shell:</summary> ``` benjamin_loison@Benjamin-Loison-HP-Debian:~$ wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX' Connected (press CTRL+C to quit) Disconnected (code: 1006, reason: "") ``` </details>
Author
Owner
Bash script:
 curl 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=YYYYYYYYYYYYYYYYYYYYYYYY' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br, zstd' -H 'Sec-WebSocket-Version: 13' -H 'Origin: https://www.overleaf.com' -H 'Sec-WebSocket-Extensions: permessage-deflate' -H 'Sec-WebSocket-Key: ZZZZZZZZZZZZZZZZZZZZZZZZ' -H 'Connection: keep-alive, Upgrade' -H 'Cookie: oa=0; overleaf_session2=s%3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB%2BCCCCCC; GCLB=DDDDDDDDDDDDDDDD' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: websocket' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' -H 'Upgrade: websocket'
curl: (1) Protocol "wss" not supported or disabled in libcurl
<details> <summary>Bash script:</summary> ```bash curl 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=YYYYYYYYYYYYYYYYYYYYYYYY' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br, zstd' -H 'Sec-WebSocket-Version: 13' -H 'Origin: https://www.overleaf.com' -H 'Sec-WebSocket-Extensions: permessage-deflate' -H 'Sec-WebSocket-Key: ZZZZZZZZZZZZZZZZZZZZZZZZ' -H 'Connection: keep-alive, Upgrade' -H 'Cookie: oa=0; overleaf_session2=s%3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB%2BCCCCCC; GCLB=DDDDDDDDDDDDDDDD' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: websocket' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' -H 'Upgrade: websocket' ``` </details> ``` curl: (1) Protocol "wss" not supported or disabled in libcurl ```
Author
Owner

https://socket.io

socket.io

About

Realtime application framework (Node.JS server)

so it won't help much it seems.

https://socket.io/docs/v4/client-api/ may help.

https://socket.io [socket.io](https://github.com/socketio/socket.io) > About > Realtime application framework (Node.JS server) so it won't help much it seems. https://socket.io/docs/v4/client-api/ may help.
Author
Owner
 wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX'
Output:
Connected (press CTRL+C to quit)
< 7:::1+0
Disconnected (code: 1006, reason: "")
```bash wscat -c 'wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX' ``` <details> <summary>Output:</summary> ``` Connected (press CTRL+C to quit) < 7:::1+0 Disconnected (code: 1006, reason: "") ``` </details>
Author
Owner

DuckDuckGo search socket.io Python client.

DuckDuckGo search *socket.io Python client*.
Author
Owner

https://pypi.org/project/python-socketio/
python-socketio has 4.1 k stars and last commit was 3 days ago.

https://pypi.org/project/python-socketio/ [python-socketio](https://github.com/miguelgrinberg/python-socketio) has 4.1 k stars and last commit was 3 days ago.
Author
Owner

The Socket.IO protocol has been through a number of revisions, and some of these introduced backward incompatible changes, which means that the client and the server must use compatible versions for everything to work.

Source: python-socketio/blob/781dc9a0305f6a795a0467ebc795f4b765084a0d/README.md#version-compatibility

> The Socket.IO protocol has been through a number of revisions, and some of these introduced backward incompatible changes, which means that the client and the server must use compatible versions for everything to work. Source: [python-socketio/blob/781dc9a0305f6a795a0467ebc795f4b765084a0d/README.md#version-compatibility](https://github.com/miguelgrinberg/python-socketio/blob/781dc9a0305f6a795a0467ebc795f4b765084a0d/README.md#version-compatibility)
Author
Owner
https://python-socketio.readthedocs.io/en/stable/client.html
Author
Owner
pip install "python-socketio[client]"
Output:
Requirement already satisfied: python-socketio[client] in ./venv/lib/python3.11/site-packages (5.11.3)
Requirement already satisfied: bidict>=0.21.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (0.23.1)
Requirement already satisfied: python-engineio>=4.8.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (4.11.2)
Requirement already satisfied: requests>=2.21.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (2.32.3)
Requirement already satisfied: websocket-client>=0.54.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (1.8.0)
Requirement already satisfied: simple-websocket>=0.10.0 in ./venv/lib/python3.11/site-packages (from python-engineio>=4.8.0->python-socketio[client]) (1.1.0)
Requirement already satisfied: charset-normalizer<4,>=2 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (3.4.1)
Requirement already satisfied: idna<4,>=2.5 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (2025.1.31)
Requirement already satisfied: wsproto in ./venv/lib/python3.11/site-packages (from simple-websocket>=0.10.0->python-engineio>=4.8.0->python-socketio[client]) (1.2.0)
Requirement already satisfied: h11<1,>=0.9.0 in ./venv/lib/python3.11/site-packages (from wsproto->simple-websocket>=0.10.0->python-engineio>=4.8.0->python-socketio[client]) (0.14.0)
```bash pip install "python-socketio[client]" ``` <details> <summary>Output:</summary> ``` Requirement already satisfied: python-socketio[client] in ./venv/lib/python3.11/site-packages (5.11.3) Requirement already satisfied: bidict>=0.21.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (0.23.1) Requirement already satisfied: python-engineio>=4.8.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (4.11.2) Requirement already satisfied: requests>=2.21.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (2.32.3) Requirement already satisfied: websocket-client>=0.54.0 in ./venv/lib/python3.11/site-packages (from python-socketio[client]) (1.8.0) Requirement already satisfied: simple-websocket>=0.10.0 in ./venv/lib/python3.11/site-packages (from python-engineio>=4.8.0->python-socketio[client]) (1.1.0) Requirement already satisfied: charset-normalizer<4,>=2 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (3.4.1) Requirement already satisfied: idna<4,>=2.5 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (3.10) Requirement already satisfied: urllib3<3,>=1.21.1 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (2.3.0) Requirement already satisfied: certifi>=2017.4.17 in ./venv/lib/python3.11/site-packages (from requests>=2.21.0->python-socketio[client]) (2025.1.31) Requirement already satisfied: wsproto in ./venv/lib/python3.11/site-packages (from simple-websocket>=0.10.0->python-engineio>=4.8.0->python-socketio[client]) (1.2.0) Requirement already satisfied: h11<1,>=0.9.0 in ./venv/lib/python3.11/site-packages (from wsproto->simple-websocket>=0.10.0->python-engineio>=4.8.0->python-socketio[client]) (0.14.0) ``` </details>
Author
Owner
Python script:
import socketio

# standard Python
with socketio.SimpleClient() as sio:
    # ... connect to a server and use the client
    # ... no need to manually disconnect!
    sio.connect('https://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX', transports = ['websocket'])
    event = sio.receive()
    print(f'received event: "{event[0]}" with arguments {event[1:]}')
Output:
Traceback (most recent call last):
  File "<tmp 3>", line 7, in <module>
    sio.connect('wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX', transports = ['websocket'])
  File "/home/benjamin_loison/venv/lib/python3.11/site-packages/socketio/simple_client.py", line 82, in connect
    self.client.connect(url, headers=headers, auth=auth,
  File "/home/benjamin_loison/venv/lib/python3.11/site-packages/socketio/client.py", line 159, in connect
    raise exceptions.ConnectionError(exc.args[0]) from None
socketio.exceptions.ConnectionError: Connection error

same if use https://.

<details> <summary>Python script:</summary> ```python import socketio # standard Python with socketio.SimpleClient() as sio: # ... connect to a server and use the client # ... no need to manually disconnect! sio.connect('https://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX', transports = ['websocket']) event = sio.receive() print(f'received event: "{event[0]}" with arguments {event[1:]}') ``` </details> <details> <summary>Output:</summary> ``` Traceback (most recent call last): File "<tmp 3>", line 7, in <module> sio.connect('wss://www.overleaf.com/socket.io/1/websocket/XXXXXXXXXXXXXXXXXXXX?projectId=XXXXXXXXXXXXXXXXXXXXXXXX', transports = ['websocket']) File "/home/benjamin_loison/venv/lib/python3.11/site-packages/socketio/simple_client.py", line 82, in connect self.client.connect(url, headers=headers, auth=auth, File "/home/benjamin_loison/venv/lib/python3.11/site-packages/socketio/client.py", line 159, in connect raise exceptions.ConnectionError(exc.args[0]) from None socketio.exceptions.ConnectionError: Connection error ``` </details> same if use `https://`.
Author
Owner

DuckDuckGo and Google search Python Overleaf client.

pyoverleaf does not seem helpful.

DuckDuckGo and Google search *Python Overleaf client*. [pyoverleaf](https://github.com/jkulhanek/pyoverleaf) does not seem helpful.
Author
Owner

https://www.overleaf.com/socket.io/

Welcome to socket.io.

https://www.overleaf.com/socket.io/ *Welcome to socket.io.*
Author
Owner

https://plmlatex.math.cnrs.fr/project let see when and who did last edit.

https://plmlatex.math.cnrs.fr/project let see when and who did last edit.
Author
Owner

May help #21.

May help #21.
Author
Owner

The Stack Overflow answer 4152986 helps to add some logs.

[The Stack Overflow answer 4152986](https://stackoverflow.com/a/4152986) helps to add some logs.
Author
Owner
notifyOnNewOverleafActivity.py.log:
2025-09-30 15:35:50.061342 ...
...
2025-10-02 07:59:30.409043 message='2::'
2025-10-02 07:59:56.346952 message='2::'
2025-10-02 08:03:14.232271 message='1::'
<details> <summary><code>notifyOnNewOverleafActivity.py.log</code>:</summary> ``` 2025-09-30 15:35:50.061342 ... ... 2025-10-02 07:59:30.409043 message='2::' 2025-10-02 07:59:56.346952 message='2::' 2025-10-02 08:03:14.232271 message='1::' ``` </details>
Author
Owner
'5:::{"name":"clientTracking.clientUpdated","args":[{"row":14,"column":12,"doc_id":"XXXXXXXXXXXXXXXXXXXXXXXX","id":"X.XXXXXXXXXXXXXXXXXXXX","user_id":"XXXXXXXXXXXXXXXXXXXXXXXX","email":"benjamin.loison@CENSORED.fr","name":"Benjamin Loison"}]}'
``` '5:::{"name":"clientTracking.clientUpdated","args":[{"row":14,"column":12,"doc_id":"XXXXXXXXXXXXXXXXXXXXXXXX","id":"X.XXXXXXXXXXXXXXXXXXXX","user_id":"XXXXXXXXXXXXXXXXXXXXXXXX","email":"benjamin.loison@CENSORED.fr","name":"Benjamin Loison"}]}' ```
Author
Owner
head -c 27 notifyOnNewRealTimeOverleafActivity.py.log 
2025-09-30 15:35:50.061342 
grep -v "message='2::'" notifyOnNewRealTimeOverleafActivity.py.log | tail -n 1 | head -c 27
2025-10-06 14:53:09.098792 
tail -n 3 notifyOnNewRealTimeOverleafActivity.py.log
Output:
2025-10-06 15:01:00.706347 message='2::'
2025-10-06 15:01:25.734648 message='2::'
2025-10-06 15:01:50.762080 message='2::'

So it seems to have broke after 8 minutes and 41 seconds.

Note that in theory there is no need to do anything as I just read the network of a relatively regular web-browser.

``` head -c 27 notifyOnNewRealTimeOverleafActivity.py.log ``` ``` 2025-09-30 15:35:50.061342 ``` ``` grep -v "message='2::'" notifyOnNewRealTimeOverleafActivity.py.log | tail -n 1 | head -c 27 ``` ``` 2025-10-06 14:53:09.098792 ``` ``` tail -n 3 notifyOnNewRealTimeOverleafActivity.py.log ``` <details> <summary>Output:</summary> ``` 2025-10-06 15:01:00.706347 message='2::' 2025-10-06 15:01:25.734648 message='2::' 2025-10-06 15:01:50.762080 message='2::' ``` </details> So it seems to have broke after 8 minutes and 41 seconds. Note that in theory there is no need to do anything as I just read the network of a relatively regular web-browser.
Author
Owner

Check if no response since 50 seconds seems to be a good way to be notified when the WebSocket does not work anymore.

Alternatively should check if after the same amount of time face again this issue.

Check if no response since 50 seconds seems to be a good way to be notified when the WebSocket does not work anymore. Alternatively should check if after the same amount of time face again this issue.
Author
Owner
ps aux | grep '[n]otifyOnNewRealTimeOverleafActivity.py'
Output:
benjami+    1648  0.0  0.0   7764  3016 ?        Ss   Oct05   0:00 /usr/bin/bash /usr/bin/my_cron_shell -c cd notifications/ && ~/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py
benjami+    1676  0.0  0.0   7024   880 ?        S    Oct05   0:00 /usr/bin/bash /usr/bin/my_cron_shell -c cd notifications/ && ~/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py
benjami+    1679  0.0  0.3  43384 29600 ?        S    Oct05   0:11 /home/benjamin_loison/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py
pkill -f notifyOnNewRealTimeOverleafActivity.py
ps aux | grep '[n]otifyOnNewRealTimeOverleafActivity.py'

Would be interesting to see the window to see if there is any prompt.

Note that my approach may not be stable if the server is not stable, as a regular user may see a prompt, possibly with a button, or just reload the webpage.

```bash ps aux | grep '[n]otifyOnNewRealTimeOverleafActivity.py' ``` <details> <summary>Output:</summary> ``` benjami+ 1648 0.0 0.0 7764 3016 ? Ss Oct05 0:00 /usr/bin/bash /usr/bin/my_cron_shell -c cd notifications/ && ~/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py benjami+ 1676 0.0 0.0 7024 880 ? S Oct05 0:00 /usr/bin/bash /usr/bin/my_cron_shell -c cd notifications/ && ~/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py benjami+ 1679 0.0 0.3 43384 29600 ? S Oct05 0:11 /home/benjamin_loison/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py ``` </details> ``` pkill -f notifyOnNewRealTimeOverleafActivity.py ``` ```bash ps aux | grep '[n]otifyOnNewRealTimeOverleafActivity.py' ``` Would be interesting to see the window to see if there is any prompt. Note that my approach may not be stable if the server is not stable, as a regular user may see a prompt, possibly with a button, or just reload the webpage.
Author
Owner
Python script:
import time
import threading

UPDATE_DELAY = 1
THRESHOLD = UPDATE_DELAY * 2
NORMAL_BEHAVIOR_PERIOD = 3 * THRESHOLD
lastTime = None

def verifyLastTime():
    while True:
        currentTime = time.time()
        print(f'Checking at {currentTime}')
        if lastTime is not None and lastTime < currentTime - THRESHOLD:
            print('No more update!')
            break
        # Sleeping just enough depending on `lastTime` may not be efficient, but mid range would be interesting.
        time.sleep(UPDATE_DELAY)

thread = threading.Thread(target = verifyLastTime)
thread.start()

for _ in range(NORMAL_BEHAVIOR_PERIOD):
    lastTime = time.time()
    print(f'{lastTime=}')
    time.sleep(UPDATE_DELAY)

thread.join()
Output:
Checking at 1759915873.3947287
lastTime=1759915873.3947918
lastTime=1759915874.3950238
Checking at 1759915874.3957164
lastTime=1759915875.3955274
Checking at 1759915875.3961875
lastTime=1759915876.3960567
Checking at 1759915876.3965623
lastTime=1759915877.396563
Checking at 1759915877.396842
lastTime=1759915878.3968308
Checking at 1759915878.3970864
Checking at 1759915879.3973584
Checking at 1759915880.3978689
No more update!

works as wanted.

<details> <summary>Python script:</summary> ```python import time import threading UPDATE_DELAY = 1 THRESHOLD = UPDATE_DELAY * 2 NORMAL_BEHAVIOR_PERIOD = 3 * THRESHOLD lastTime = None def verifyLastTime(): while True: currentTime = time.time() print(f'Checking at {currentTime}') if lastTime is not None and lastTime < currentTime - THRESHOLD: print('No more update!') break # Sleeping just enough depending on `lastTime` may not be efficient, but mid range would be interesting. time.sleep(UPDATE_DELAY) thread = threading.Thread(target = verifyLastTime) thread.start() for _ in range(NORMAL_BEHAVIOR_PERIOD): lastTime = time.time() print(f'{lastTime=}') time.sleep(UPDATE_DELAY) thread.join() ``` </details> <details> <summary>Output:</summary> ``` Checking at 1759915873.3947287 lastTime=1759915873.3947918 lastTime=1759915874.3950238 Checking at 1759915874.3957164 lastTime=1759915875.3955274 Checking at 1759915875.3961875 lastTime=1759915876.3960567 Checking at 1759915876.3965623 lastTime=1759915877.396563 Checking at 1759915877.396842 lastTime=1759915878.3968308 Checking at 1759915878.3970864 Checking at 1759915879.3973584 Checking at 1759915880.3978689 No more update! ``` </details> works as wanted.
Author
Owner
[Benjamin_Loison/playwright/issues/3](https://codeberg.org/Benjamin_Loison/playwright/issues/3) would help.
Author
Owner
Output:
...
2025-10-09 00:40:37.703382 Checking at 1759963237.7033584
2025-10-09 00:40:40.789355 message='2::'
2025-10-09 00:41:02.703878 Checking at 1759963262.7038379
2025-10-09 00:41:05.821398 message='2::'
2025-10-09 00:41:27.704372 Checking at 1759963287.704344
2025-10-09 00:41:30.851719 message='2::'
<details> <summary>Output:</summary> ``` ... 2025-10-09 00:40:37.703382 Checking at 1759963237.7033584 2025-10-09 00:40:40.789355 message='2::' 2025-10-09 00:41:02.703878 Checking at 1759963262.7038379 2025-10-09 00:41:05.821398 message='2::' 2025-10-09 00:41:27.704372 Checking at 1759963287.704344 2025-10-09 00:41:30.851719 message='2::' ``` </details>
Author
Owner
grep '2::' notifyOnNewRealTimeOverleafActivity.py.log | tail -n 3
Output:
2025-10-10 00:41:14.054577 message='2::'
2025-10-10 00:41:39.084075 message='2::'
2025-10-10 00:42:04.114034 message='2::'
```bash grep '2::' notifyOnNewRealTimeOverleafActivity.py.log | tail -n 3 ``` <details> <summary>Output:</summary> ``` 2025-10-10 00:41:14.054577 message='2::' 2025-10-10 00:41:39.084075 message='2::' 2025-10-10 00:42:04.114034 message='2::' ``` </details>
Author
Owner
tail -n 3 notifyOnNewRealTimeOverleafActivity.py.log
Output:
2025-10-13 16:15:02.594978 Checking at 1760364902.5949585
2025-10-13 16:15:27.595367 Checking at 1760364927.5953403
2025-10-13 16:15:52.595749 Checking at 1760364952.5957308
grep 'No more Overleaf ping' notifyOnNewRealTimeOverleafActivity.py.log

does not return anything.

An alternative would to schedule a notification function and abort it and start a new one when receive a heartbeat.

grep 'message' notifyOnNewRealTimeOverleafActivity.py.log | tail -n 3
Output:
2025-10-10 00:41:14.054577 message='2::'
2025-10-10 00:41:39.084075 message='2::'
2025-10-10 00:42:04.114034 message='2::'

Now using:

print(f'Checking at {currentTime} ({lastTime=})')
./notifyOnNewRealTimeOverleafActivity.py
Output:
2025-10-13 16:19:53.445716 message='6:::3+[null,[{"last_updated_at":"1760365192431","user_id":"XXXXXXXXXXXXXXXXXXXXXXXX","first_name":"Benjamin","last_name":"Loison","email":"benjamin.loison@CENSORED.fr","cursorData":{"row":1088,"column":0,"doc_id":"XXXXXXXXXXXXXXXXXXXXXXXX"},"connected":true,"client_id":"P.XX_XXXXXXXXXXXXXXXXX","client_age":1.001},{"last_updated_at":"1760365192431","user_id":"anonymous-user","first_name":"","last_name":"","email":"","connected":true,"client_id":"P.XXXXXXXXXXXXXXXXXX-X","client_age":1.001}]]'
2025-10-13 16:19:58.916655 Checking at 1760365198.9166238 (lastTime=None)
2025-10-13 16:20:02.831867 message='6:::2+[{"message":"Something went wrong in real-time service"}]'
2025-10-13 16:20:03.585845 message='2::'
2025-10-13 16:20:23.917074 Checking at 1760365223.9170516 (lastTime=None)
2025-10-13 16:20:28.617499 message='2::'
2025-10-13 16:20:48.917365 Checking at 1760365248.9173453 (lastTime=None)
``` tail -n 3 notifyOnNewRealTimeOverleafActivity.py.log ``` <details> <summary>Output:</summary> ``` 2025-10-13 16:15:02.594978 Checking at 1760364902.5949585 2025-10-13 16:15:27.595367 Checking at 1760364927.5953403 2025-10-13 16:15:52.595749 Checking at 1760364952.5957308 ``` </details> ```bash grep 'No more Overleaf ping' notifyOnNewRealTimeOverleafActivity.py.log ``` does not return anything. An alternative would to schedule a notification function and abort it and start a new one when receive a heartbeat. ```bash grep 'message' notifyOnNewRealTimeOverleafActivity.py.log | tail -n 3 ``` <details> <summary>Output:</summary> ``` 2025-10-10 00:41:14.054577 message='2::' 2025-10-10 00:41:39.084075 message='2::' 2025-10-10 00:42:04.114034 message='2::' ``` </details> Now using: ```python print(f'Checking at {currentTime} ({lastTime=})') ``` ```bash ./notifyOnNewRealTimeOverleafActivity.py ``` <details> <summary>Output:</summary> ``` 2025-10-13 16:19:53.445716 message='6:::3+[null,[{"last_updated_at":"1760365192431","user_id":"XXXXXXXXXXXXXXXXXXXXXXXX","first_name":"Benjamin","last_name":"Loison","email":"benjamin.loison@CENSORED.fr","cursorData":{"row":1088,"column":0,"doc_id":"XXXXXXXXXXXXXXXXXXXXXXXX"},"connected":true,"client_id":"P.XX_XXXXXXXXXXXXXXXXX","client_age":1.001},{"last_updated_at":"1760365192431","user_id":"anonymous-user","first_name":"","last_name":"","email":"","connected":true,"client_id":"P.XXXXXXXXXXXXXXXXXX-X","client_age":1.001}]]' 2025-10-13 16:19:58.916655 Checking at 1760365198.9166238 (lastTime=None) 2025-10-13 16:20:02.831867 message='6:::2+[{"message":"Something went wrong in real-time service"}]' 2025-10-13 16:20:03.585845 message='2::' 2025-10-13 16:20:23.917074 Checking at 1760365223.9170516 (lastTime=None) 2025-10-13 16:20:28.617499 message='2::' 2025-10-13 16:20:48.917365 Checking at 1760365248.9173453 (lastTime=None) ``` </details>
Author
Owner

global lastTime was missing in server_message_handler.

`global lastTime` was missing in `server_message_handler`.
Author
Owner
grep -vE "(Checking at |message='2::')" notifyOnNewRealTimeOverleafActivity.py.log | tail -n 1
2025-10-18 18:12:23.204476 message='6:::3+[null,[{"last_updated_at":"1760803942124","user_id":"anonymous-user","first_name":"","last_name":"","email":"","connected":true,"client_id":"P.XXXXXXXXXXXXXXXXXXXX","client_age":1.003}]]'
tail -n 6 notifyOnNewRealTimeOverleafActivity.py.log
Output:
2025-10-19 18:11:32.605908 message='2::'
2025-10-19 18:11:50.161417 Checking at 1760890310.1613894 (lastTime=1760890292.6060472)
2025-10-19 18:11:57.633351 message='2::'
2025-10-19 18:12:15.161793 Checking at 1760890335.161772 (lastTime=1760890317.6334727)
2025-10-19 18:12:40.162049 Checking at 1760890360.162032 (lastTime=1760890317.6334727)
2025-10-19 18:13:05.162317 Checking at 1760890385.1622937 (lastTime=1760890317.6334727)

It seems correct as I restarted yesterday OverClock3000.

```bash grep -vE "(Checking at |message='2::')" notifyOnNewRealTimeOverleafActivity.py.log | tail -n 1 ``` ``` 2025-10-18 18:12:23.204476 message='6:::3+[null,[{"last_updated_at":"1760803942124","user_id":"anonymous-user","first_name":"","last_name":"","email":"","connected":true,"client_id":"P.XXXXXXXXXXXXXXXXXXXX","client_age":1.003}]]' ``` ``` tail -n 6 notifyOnNewRealTimeOverleafActivity.py.log ``` <details> <summary>Output:</summary> ``` 2025-10-19 18:11:32.605908 message='2::' 2025-10-19 18:11:50.161417 Checking at 1760890310.1613894 (lastTime=1760890292.6060472) 2025-10-19 18:11:57.633351 message='2::' 2025-10-19 18:12:15.161793 Checking at 1760890335.161772 (lastTime=1760890317.6334727) 2025-10-19 18:12:40.162049 Checking at 1760890360.162032 (lastTime=1760890317.6334727) 2025-10-19 18:13:05.162317 Checking at 1760890385.1622937 (lastTime=1760890317.6334727) ``` </details> It seems correct as I restarted yesterday OverClock3000.
Author
Owner

Next ping was expected at 2025-10-19 18:12:22.633351 which is quite exactly 24 hours after.

I am a bit surprised as I had the feeling that it was previously running for longer.

Benjamin-Loison/cpython/issues/29 would help, as the script is still running...

Next ping was expected at *2025-10-19 18:12:22.633351* which is quite exactly 24 hours after. I am a bit surprised as I had the feeling that it was previously running for longer. [Benjamin-Loison/cpython/issues/29](https://github.com/Benjamin-Loison/cpython/issues/29) would help, as the script is still running...
Author
Owner

The 24 hours duration is not due to page.wait_for_timeout, but this Python statement is maybe involved in not stopping the script.

The 24 hours duration is not due to `page.wait_for_timeout`, but this Python statement is maybe involved in not stopping the script.
Author
Owner

I restarted the script to see if again face the issue after 24 hours.

I restarted the script to see if again face the issue after 24 hours.
Author
Owner

I forgot to save the simplified script reproducing the non stopping behavior.

I forgot to save the simplified script reproducing the non stopping behavior.
Author
Owner

To be notified when someone leaves, as if do not write continuously can't know.

2025-10-20 22:40:34.362884 message='5:::{"name":"clientTracking.clientDisconnected","args":["P.XXXXXXXXXXXXXXXXXXXX"]}'

As possibly not multiple persons connected, just knowing that someone left if already nice.

People already connected when the script starts are sent as last_updated_at, see #issuecomment-4363.

args matches id, see #issuecomment-4343. Same if already connected with client_id.

Note that such id changes from a connection to the other for the same authenticated user.

Could maintain a dict to leverage the associated identity.

To be notified when someone leaves, as if do not write continuously can't know. ``` 2025-10-20 22:40:34.362884 message='5:::{"name":"clientTracking.clientDisconnected","args":["P.XXXXXXXXXXXXXXXXXXXX"]}' ``` As possibly not multiple persons connected, just knowing that someone left if already nice. People already connected when the script starts are sent as `last_updated_at`, see [#issuecomment-4363](#issuecomment-4363). `args` matches `id`, see [#issuecomment-4343](#issuecomment-4343). Same if already connected with `client_id`. Note that such id changes from a connection to the other for the same authenticated user. Could maintain a `dict` to leverage the associated identity.
Author
Owner

For the initialization:

print(json.dumps(json.loads(pyperclip.paste()), indent = 4))
Output:
[
    null,
    [
        {
            "last_updated_at": "1760992807670",
            "user_id": "XXXXXXXXXXXXXXXXXXXXXXXX",
            "first_name": "Benjamin",
            "last_name": "Loison",
            "email": "benjamin.loison@CENSORED.fr",
            "cursorData": {
                "row": CENSORED,
                "column": CENSORED,
                "doc_id": "XXXXXXXXXXXXXXXXXXXXXXXX"
            },
            "connected": true,
            "client_id": "P.XXXXXXXXXXXXXXXXXXXX",
            "client_age": 1.002
        },
        {
            "last_updated_at": "1760992807670",
            "user_id": "anonymous-user",
            "first_name": "",
            "last_name": "",
            "email": "",
            "connected": true,
            "client_id": "P.YYYYYYYYYYYYYYYYYYYY",
            "client_age": 1.003
        }
    ]
]
For the initialization: ```python print(json.dumps(json.loads(pyperclip.paste()), indent = 4)) ``` <details> <summary>Output:</summary> ```json [ null, [ { "last_updated_at": "1760992807670", "user_id": "XXXXXXXXXXXXXXXXXXXXXXXX", "first_name": "Benjamin", "last_name": "Loison", "email": "benjamin.loison@CENSORED.fr", "cursorData": { "row": CENSORED, "column": CENSORED, "doc_id": "XXXXXXXXXXXXXXXXXXXXXXXX" }, "connected": true, "client_id": "P.XXXXXXXXXXXXXXXXXXXX", "client_age": 1.002 }, { "last_updated_at": "1760992807670", "user_id": "anonymous-user", "first_name": "", "last_name": "", "email": "", "connected": true, "client_id": "P.YYYYYYYYYYYYYYYYYYYY", "client_age": 1.003 } ] ] ``` </details>
Author
Owner

Could merge {first,last}_name at initializion to have name if connected later.
Can first rely on email.

Could merge `{first,last}_name` at initializion to have `name` if connected later. Can first rely on `email`.
Author
Owner

Let us consider that Benjamin-Loison/matrix-commander/issues/16 may take multiple seconds and people may leave faster, as may have concurrency of him coming back for instance, well it would be using different ids.
So let us prefer keeping for too long memory content, but notify faster.

Let us consider that [Benjamin-Loison/matrix-commander/issues/16](https://github.com/Benjamin-Loison/matrix-commander/issues/16) may take multiple seconds and people may leave faster, as may have concurrency of him coming back for instance, well it would be using different ids. So let us prefer keeping for too long memory content, but notify _faster_.
Author
Owner

Note that if the person being connected when the script starts is DO_NOT_NOTIFY_ON_EMAIL, then it is not an issue.

Note that if the person being connected when the script starts is `DO_NOT_NOTIFY_ON_EMAIL`, then it is not an issue.
Author
Owner
Related to [Benjamin_Loison/WhatsApp/issues/30#issuecomment-7874381](https://codeberg.org/Benjamin_Loison/WhatsApp/issues/30#issuecomment-7874381).
Author
Owner

headless = False may help figure out what's wrong to not receiving a ping anymore.

`headless = False` may help figure out what's wrong to not receiving a ping anymore.
Author
Owner
Output:
2025-10-24 22:09:51.217163 message='2::'
2025-10-24 22:10:07.306708 Checking at 1761336607.306685 (lastTime=1761336591.217317)
2025-10-24 22:10:10.633754 message='5:::{"name":"clientTracking.clientDisconnected","args":["P.XXXXXXXXXXXXXXX-XXXX"]}'
2025-10-24 22:10:32.307053 Checking at 1761336632.307014 (lastTime=1761336610.6339831)
2025-10-24 22:10:53.155093 message='2::'
2025-10-24 22:10:57.307489 Checking at 1761336657.3074548 (lastTime=1761336653.1552126)
2025-10-24 22:11:22.307933 Checking at 1761336682.3078983 (lastTime=1761336653.1552126)
2025-10-24 22:11:47.308454 Checking at 1761336707.308422 (lastTime=1761336653.1552126)
2025-10-24 22:13:33.741368 message='1::'
2025-10-24 22:13:33.742285 message='5:::{"name":"connectionAccepted","args":[null,"P.XXXXXXXXXXXXXXXXXXXX"]}'
2025-10-24 22:13:33.930110 message='6:::4+[null,{"_id":"CENSORED...

So 2:: does not seem mandatory.

Disconnection seems related, but not clearly as there is still a single heartbeat after.

<details> <summary>Output:</summary> ``` 2025-10-24 22:09:51.217163 message='2::' 2025-10-24 22:10:07.306708 Checking at 1761336607.306685 (lastTime=1761336591.217317) 2025-10-24 22:10:10.633754 message='5:::{"name":"clientTracking.clientDisconnected","args":["P.XXXXXXXXXXXXXXX-XXXX"]}' 2025-10-24 22:10:32.307053 Checking at 1761336632.307014 (lastTime=1761336610.6339831) 2025-10-24 22:10:53.155093 message='2::' 2025-10-24 22:10:57.307489 Checking at 1761336657.3074548 (lastTime=1761336653.1552126) 2025-10-24 22:11:22.307933 Checking at 1761336682.3078983 (lastTime=1761336653.1552126) 2025-10-24 22:11:47.308454 Checking at 1761336707.308422 (lastTime=1761336653.1552126) 2025-10-24 22:13:33.741368 message='1::' 2025-10-24 22:13:33.742285 message='5:::{"name":"connectionAccepted","args":[null,"P.XXXXXXXXXXXXXXXXXXXX"]}' 2025-10-24 22:13:33.930110 message='6:::4+[null,{"_id":"CENSORED... ``` </details> So `2::` does not seem mandatory. Disconnection seems related, but not clearly as there is still a single heartbeat after.
Author
Owner

Thanks to logs if not notified should be able to investigate why.

Thanks to logs if not notified should be able to investigate why.
Author
Owner

image.png

L'éditeur a été déconnecté. Cliquez n'importe où pour vous reconnecter

Clicking top bar between Menu and the document title.

image.png

Actualiser l'éditeur (not the language mix):

image.png

So can make the necessary to always have:

2025-10-28 16:46:37.858067 message='2::'
2025-10-28 16:47:02.894618 message='2::'

which seems more reliable.

Related to Benjamin_Loison/gnome-screenshot/-/issues/17#note_2589157.

![image.png](/attachments/98010b81-d727-443b-baab-cb86933b2eb6) > L'éditeur a été déconnecté. Cliquez n'importe où pour vous reconnecter Clicking top bar between *Menu* and the document title. ![image.png](/attachments/81d3d514-1224-4ebd-8b0d-e760cf45e6c3) *Actualiser l'éditeur* (not the language mix): ![image.png](/attachments/5490d42f-7fdd-47d1-8589-7b65d0fcc642) So can make the necessary to always have: ``` 2025-10-28 16:46:37.858067 message='2::' 2025-10-28 16:47:02.894618 message='2::' ``` which seems more reliable. Related to [Benjamin_Loison/gnome-screenshot/-/issues/17#note_2589157](https://gitlab.gnome.org/Benjamin_Loison/gnome-screenshot/-/issues/17#note_2589157).
Author
Owner

Using python3 -i to possibly give orders to PlayWright, but I doubt so.

Using `python3 -i` to possibly give orders to PlayWright, but I doubt so.
Author
Owner
Error:
2025-11-08 19:35:21.220362 Checking at 1762626921.2203474 (lastTime=None)
Traceback (most recent call last):
  File "/home/benjamin_loison/notifications/notifyOnNewRealTimeOverleafActivity.py", line 80, in <module>
    browser = p.firefox.launch(headless = False)
  File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/sync_api/_generated.py", line 14502, in launch
    self._sync(
    ~~~~~~~~~~^
        self._impl_obj.launch(
        ^^^^^^^^^^^^^^^^^^^^^^
    ...<17 lines>...
        )
        ^
    )
    ^
  File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_sync_base.py", line 115, in _sync
    return task.result()
           ~~~~~~~~~~~^^
  File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_browser_type.py", line 98, in launch
    await self._channel.send(
        "launch", TimeoutSettings.launch_timeout, params
    )
  File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py", line 69, in send
    return await self._connection.wrap_api_call(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<3 lines>...
    )
    ^
  File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py", line 558, in wrap_api_call
    raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None
playwright._impl._errors.TargetClosedError: BrowserType.launch: Target page, context or browser has been closed
Browser logs:

╔════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Looks like you launched a headed browser without having a XServer running.                     ║
║ Set either 'headless: true' or use 'xvfb-run <your-playwright-app>' before running Playwright. ║
║                                                                                                ║
║ <3 Playwright Team                                                                             ║
╚════════════════════════════════════════════════════════════════════════════════════════════════╝
Call log:
  - <launching> /home/benjamin_loison/.cache/ms-playwright/firefox-1490/firefox/firefox -no-remote -wait-for-browser -foreground -profile /tmp/playwright_firefoxdev_profile-9l8V90 -juggler-pipe -silent
  - <launched> pid=2833
  - [pid=2833][err] Error: no DISPLAY environment variable specified
  - [pid=2833] <process did exit: exitCode=1, signal=null>
  - [pid=2833] starting temporary directories cleanup
  - [pid=2833] <gracefully close start>
  - [pid=2833] finished temporary directories cleanup
  - [pid=2833] <gracefully close end>

Exception in thread Thread-1 (verifyLastTime):
Traceback (most recent call last):
  File "/usr/lib/python3.13/threading.py", line 1043, in _bootstrap_inner
    self.run()
    ~~~~~~~~^^
  File "/usr/lib/python3.13/threading.py", line 994, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benjamin_loison/notifications/notifyOnNewRealTimeOverleafActivity.py", line 69, in verifyLastTime
    print(f'Checking at {currentTime} ({lastTime=})')
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benjamin_loison/notifications/notifyOnNewRealTimeOverleafActivity.py", line 27, in print
    with open(f'{os.path.basename(__file__)}.log', 'a') as f:
                                  ^^^^^^^^
NameError: name '__file__' is not defined. Did you mean: '__name__'?
2025-11-08 19:35:45.985338 Checking at 1762626945.985241 (lastTime=None)
on OverClock3000 reboot due to crontab:
@reboot cd notifications/ && ~/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py

it is probably due to headless = False.

Via RDP I don't have this issue.

<details> <summary>Error:</summary> ``` 2025-11-08 19:35:21.220362 Checking at 1762626921.2203474 (lastTime=None) Traceback (most recent call last): File "/home/benjamin_loison/notifications/notifyOnNewRealTimeOverleafActivity.py", line 80, in <module> browser = p.firefox.launch(headless = False) File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/sync_api/_generated.py", line 14502, in launch self._sync( ~~~~~~~~~~^ self._impl_obj.launch( ^^^^^^^^^^^^^^^^^^^^^^ ...<17 lines>... ) ^ ) ^ File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_sync_base.py", line 115, in _sync return task.result() ~~~~~~~~~~~^^ File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_browser_type.py", line 98, in launch await self._channel.send( "launch", TimeoutSettings.launch_timeout, params ) File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py", line 69, in send return await self._connection.wrap_api_call( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<3 lines>... ) ^ File "/home/benjamin_loison/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py", line 558, in wrap_api_call raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None playwright._impl._errors.TargetClosedError: BrowserType.launch: Target page, context or browser has been closed Browser logs: ╔════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ Looks like you launched a headed browser without having a XServer running. ║ ║ Set either 'headless: true' or use 'xvfb-run <your-playwright-app>' before running Playwright. ║ ║ ║ ║ <3 Playwright Team ║ ╚════════════════════════════════════════════════════════════════════════════════════════════════╝ Call log: - <launching> /home/benjamin_loison/.cache/ms-playwright/firefox-1490/firefox/firefox -no-remote -wait-for-browser -foreground -profile /tmp/playwright_firefoxdev_profile-9l8V90 -juggler-pipe -silent - <launched> pid=2833 - [pid=2833][err] Error: no DISPLAY environment variable specified - [pid=2833] <process did exit: exitCode=1, signal=null> - [pid=2833] starting temporary directories cleanup - [pid=2833] <gracefully close start> - [pid=2833] finished temporary directories cleanup - [pid=2833] <gracefully close end> Exception in thread Thread-1 (verifyLastTime): Traceback (most recent call last): File "/usr/lib/python3.13/threading.py", line 1043, in _bootstrap_inner self.run() ~~~~~~~~^^ File "/usr/lib/python3.13/threading.py", line 994, in run self._target(*self._args, **self._kwargs) ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/benjamin_loison/notifications/notifyOnNewRealTimeOverleafActivity.py", line 69, in verifyLastTime print(f'Checking at {currentTime} ({lastTime=})') ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/benjamin_loison/notifications/notifyOnNewRealTimeOverleafActivity.py", line 27, in print with open(f'{os.path.basename(__file__)}.log', 'a') as f: ^^^^^^^^ NameError: name '__file__' is not defined. Did you mean: '__name__'? 2025-11-08 19:35:45.985338 Checking at 1762626945.985241 (lastTime=None) ``` </details> <details> <summary>on OverClock3000 reboot due to <code>crontab</code>:</summary> ``` @reboot cd notifications/ && ~/venv/bin/python3 notifyOnNewRealTimeOverleafActivity.py ``` </details> it is probably due to `headless = False`. Via RDP I don't have this issue.
Author
Owner
[Benjamin_Loison/playwright/issues/8](https://codeberg.org/Benjamin_Loison/playwright/issues/8) would help.
Author
Owner
[Benjamin-Loison/pyzo/issues/91](https://github.com/Benjamin-Loison/pyzo/issues/91) would help.
Author
Owner
Output:
2025-12-05 16:21:25.022162 Checking at 1764948085.0221505 (lastTime=None)
2025-12-05 16:21:27.549076 Waiting forever
2025-12-05 16:21:28.537845 message='1::'
...
2025-12-06 12:31:52.599574 Checking at 1765020712.5995438 (lastTime=1765020706.4025095)
2025-12-06 12:32:11.431761 message='2::'
2025-12-06 12:32:17.600688 Checking at 1765020737.6006556 (lastTime=1765020731.4321938)
2025-12-06 12:32:42.601490 Checking at 1765020762.6014564 (lastTime=1765020731.4321938)
2025-12-06 12:33:07.602441 Checking at 1765020787.6024113 (lastTime=1765020731.4321938)
2025-12-06 12:34:31.562543 message='1::'
2025-12-06 12:34:31.565544 message='5:::{"name":"connectionAccepted","args":[null,"P.XXXXXXXXXXXX-XXXXXXX"]}'
2025-12-06 12:34:31.838339 message='6:::4+[null,{"_id":"XXXXXXXXXXXXXXXXXXXXXXXX","name":"CENSORED_DOCUMENT_TITLE","rootDoc_id":"
...
2025-12-06 12:34:56.560277 message='2::'
2025-12-06 12:35:21.592516 message='2::'
2025-12-06 12:35:46.629341 message='2::'
...

Would be nice that if get ping back, then re-enable watcher.

Maybe it is the time of PDF compilation or similar, but I doubt so.

The absence of information let me believe that it is an anonymous connection.

<details> <summary>Output:</summary> ``` 2025-12-05 16:21:25.022162 Checking at 1764948085.0221505 (lastTime=None) 2025-12-05 16:21:27.549076 Waiting forever 2025-12-05 16:21:28.537845 message='1::' ... 2025-12-06 12:31:52.599574 Checking at 1765020712.5995438 (lastTime=1765020706.4025095) 2025-12-06 12:32:11.431761 message='2::' 2025-12-06 12:32:17.600688 Checking at 1765020737.6006556 (lastTime=1765020731.4321938) 2025-12-06 12:32:42.601490 Checking at 1765020762.6014564 (lastTime=1765020731.4321938) 2025-12-06 12:33:07.602441 Checking at 1765020787.6024113 (lastTime=1765020731.4321938) 2025-12-06 12:34:31.562543 message='1::' 2025-12-06 12:34:31.565544 message='5:::{"name":"connectionAccepted","args":[null,"P.XXXXXXXXXXXX-XXXXXXX"]}' 2025-12-06 12:34:31.838339 message='6:::4+[null,{"_id":"XXXXXXXXXXXXXXXXXXXXXXXX","name":"CENSORED_DOCUMENT_TITLE","rootDoc_id":" ... 2025-12-06 12:34:56.560277 message='2::' 2025-12-06 12:35:21.592516 message='2::' 2025-12-06 12:35:46.629341 message='2::' ... ``` </details> Would be nice that if get ping back, then re-enable watcher. Maybe it is the time of PDF compilation or similar, but I doubt so. The absence of information let me believe that it is an anonymous connection.
Author
Owner
help(p)
Output:
Help on PlaywrightContextManager in module playwright.sync_api._context_manager object:

class PlaywrightContextManager(builtins.object)
 |  PlaywrightContextManager() -> None
 |
 |  Methods defined here:
 |
 |  __enter__(self) -> playwright.sync_api._generated.Playwright
 |
 |  __exit__(self, *args: Any) -> None
 |
 |  __init__(self) -> None
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  start(self) -> playwright.sync_api._generated.Playwright
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables
 |
 |  __weakref__
 |      list of weak references to the object
p = sync_playwright().start()

works as wanted.

```python help(p) ``` <details> <summary>Output:</summary> ``` Help on PlaywrightContextManager in module playwright.sync_api._context_manager object: class PlaywrightContextManager(builtins.object) | PlaywrightContextManager() -> None | | Methods defined here: | | __enter__(self) -> playwright.sync_api._generated.Playwright | | __exit__(self, *args: Any) -> None | | __init__(self) -> None | Initialize self. See help(type(self)) for accurate signature. | | start(self) -> playwright.sync_api._generated.Playwright | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables | | __weakref__ | list of weak references to the object ``` </details> ```python p = sync_playwright().start() ``` works as wanted.
Author
Owner
Python script:
from playwright.async_api import async_playwright

p = async_playwright().start()
browser = p.firefox.launch(headless = False)
Output:
Traceback (most recent call last):
  File "<tmp 1>", line 6, in <module>
    browser = p.firefox.launch(headless = False)
              ^^^^^^^^^
AttributeError: 'coroutine' object has no attribute 'firefox'
<details> <summary>Python script:</summary> ```python from playwright.async_api import async_playwright p = async_playwright().start() browser = p.firefox.launch(headless = False) ``` </details> <details> <summary>Output:</summary> ``` Traceback (most recent call last): File "<tmp 1>", line 6, in <module> browser = p.firefox.launch(headless = False) ^^^^^^^^^ AttributeError: 'coroutine' object has no attribute 'firefox' ``` </details>
Author
Owner
browser = await p.firefox.launch(headless = False)
  File "<tmp 1>", line 6
SyntaxError: 'await' outside function
```python browser = await p.firefox.launch(headless = False) ``` ``` File "<tmp 1>", line 6 SyntaxError: 'await' outside function ```
Author
Owner
Python script:
from playwright.async_api import async_playwright
import asyncio

p = async_playwright().start()
browser = asyncio.run(p.firefox.launch(headless = False))
Output:
Traceback (most recent call last):
  File "<tmp 1>", line 7, in <module>
    browser = asyncio.run(p.firefox.launch(headless = False))
                          ^^^^^^^^^
AttributeError: 'coroutine' object has no attribute 'firefox'
<details> <summary>Python script:</summary> ```python from playwright.async_api import async_playwright import asyncio p = async_playwright().start() browser = asyncio.run(p.firefox.launch(headless = False)) ``` </details> <details> <summary>Output:</summary> ``` Traceback (most recent call last): File "<tmp 1>", line 7, in <module> browser = asyncio.run(p.firefox.launch(headless = False)) ^^^^^^^^^ AttributeError: 'coroutine' object has no attribute 'firefox' ``` </details>
Author
Owner
help(async_playwright())
Output:
Help on PlaywrightContextManager in module playwright.async_api._context_manager object:

class PlaywrightContextManager(builtins.object)
 |  PlaywrightContextManager() -> None
 |
 |  Methods defined here:
 |
 |  async __aenter__(self) -> playwright.async_api._generated.Playwright
 |
 |  async __aexit__(self, *args: Any) -> None
 |
 |  __init__(self) -> None
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  async start(self) -> playwright.async_api._generated.Playwright
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables
 |
 |  __weakref__
 |      list of weak references to the object
```python help(async_playwright()) ``` <details> <summary>Output:</summary> ``` Help on PlaywrightContextManager in module playwright.async_api._context_manager object: class PlaywrightContextManager(builtins.object) | PlaywrightContextManager() -> None | | Methods defined here: | | async __aenter__(self) -> playwright.async_api._generated.Playwright | | async __aexit__(self, *args: Any) -> None | | __init__(self) -> None | Initialize self. See help(type(self)) for accurate signature. | | async start(self) -> playwright.async_api._generated.Playwright | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables | | __weakref__ | list of weak references to the object ``` </details>
Author
Owner
Python script:
from playwright.async_api import async_playwright
import asyncio

#with sync_playwright() as p:

p = asyncio.run(async_playwright().start())
browser = asyncio.run(p.firefox.launch(headless = False))
page = asyncio.run(browser.new_page())

asyncio.run(page.goto('https://example.com'))

# To distinguish executions.
print('Waiting forever')
while True:
    asyncio.run(page.wait_for_timeout(2 ** 31 - 1))

hangs at browser = asyncio.run(p.firefox.launch(headless = False)).

<details> <summary>Python script:</summary> ```python from playwright.async_api import async_playwright import asyncio #with sync_playwright() as p: p = asyncio.run(async_playwright().start()) browser = asyncio.run(p.firefox.launch(headless = False)) page = asyncio.run(browser.new_page()) asyncio.run(page.goto('https://example.com')) # To distinguish executions. print('Waiting forever') while True: asyncio.run(page.wait_for_timeout(2 ** 31 - 1)) ``` </details> hangs at `browser = asyncio.run(p.firefox.launch(headless = False))`.
Author
Owner

Maybe a single asyncio.run is intended.

Maybe a single `asyncio.run` is intended.
Author
Owner
help(asyncio.run)
Output:
Help on function run in module asyncio.runners:

run(main, *, debug=None, loop_factory=None)
    Execute the coroutine and return the result.

    This function runs the passed coroutine, taking care of
    managing the asyncio event loop, finalizing asynchronous
    generators and closing the default executor.

    This function cannot be called when another asyncio event loop is
    running in the same thread.

    If debug is True, the event loop will be run in debug mode.
    If loop_factory is passed, it is used for new event loop creation.

    This function always creates a new event loop and closes it at the end.
    It should be used as a main entry point for asyncio programs, and should
    ideally only be called once.

    The executor is given a timeout duration of 5 minutes to shutdown.
    If the executor hasn't finished within that duration, a warning is
    emitted and the executor is closed.

    Example:

        async def main():
            await asyncio.sleep(1)
            print('hello')

        asyncio.run(main())
Excerpt:
    This function cannot be called when another asyncio event loop is
    running in the same thread.
...
    This function always creates a new event loop and closes it at the end.
    It should be used as a main entry point for asyncio programs, and should
    ideally only be called once.
```python help(asyncio.run) ``` <details> <summary>Output:</summary> ``` Help on function run in module asyncio.runners: run(main, *, debug=None, loop_factory=None) Execute the coroutine and return the result. This function runs the passed coroutine, taking care of managing the asyncio event loop, finalizing asynchronous generators and closing the default executor. This function cannot be called when another asyncio event loop is running in the same thread. If debug is True, the event loop will be run in debug mode. If loop_factory is passed, it is used for new event loop creation. This function always creates a new event loop and closes it at the end. It should be used as a main entry point for asyncio programs, and should ideally only be called once. The executor is given a timeout duration of 5 minutes to shutdown. If the executor hasn't finished within that duration, a warning is emitted and the executor is closed. Example: async def main(): await asyncio.sleep(1) print('hello') asyncio.run(main()) ``` </details> <details> <summary>Excerpt:</summary> ``` This function cannot be called when another asyncio event loop is running in the same thread. ... This function always creates a new event loop and closes it at the end. It should be used as a main entry point for asyncio programs, and should ideally only be called once. ``` </details>
Author
Owner

The issue with a function is that interrupting it does not make code interaction possible it seems.

The issue with a function is that interrupting it does not make code interaction possible it seems.
Author
Owner

DuckDuckGo and Google search asyncio.run, Playwright interactively, Playwright async interactively and Python Playwright async interactive mode.

https://playwright.dev/docs/test-ui-mode does not seem to help.

https://chat.mistral.ai/chat/4a4cd652-5296-4667-8af3-992f8201ada6

Related to Improve_websites_thanks_to_open_source/issues/2806.

DuckDuckGo and Google search *asyncio.run*, *Playwright interactively*, *Playwright async interactively* and *Python Playwright async interactive mode*. https://playwright.dev/docs/test-ui-mode does not seem to help. https://chat.mistral.ai/chat/4a4cd652-5296-4667-8af3-992f8201ada6 Related to [Improve_websites_thanks_to_open_source/issues/2806](https://codeberg.org/Benjamin_Loison/Improve_websites_thanks_to_open_source/issues/2806).
Author
Owner
Warning:
<tmp 1>+5:1: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()
<details> <summary>Warning:</summary> ``` <tmp 1>+5:1: DeprecationWarning: There is no current event loop loop = asyncio.get_event_loop() ``` </details>
Author
Owner
https://chatgpt.com/share/6935f6a2-5444-8002-a4d6-d299fb2495c2
Author
Owner
In JupyterLab:
from playwright.async_api import async_playwright

p = await async_playwright().start()
browser = await p.firefox.launch(headless=False)

does not hang, but does not show any window.

<details> <summary>In JupyterLab:</summary> ```python from playwright.async_api import async_playwright p = await async_playwright().start() browser = await p.firefox.launch(headless=False) ``` </details> does not hang, but does not show any window.
Author
Owner
I was about to send to the person:
-----BEGIN PGP MESSAGE-----

hF4DTQa9Wom5MBgSAQdArVmapRinF4lsSfTCyFY6OfQHzpS/apayE/KXQRocCmIw
Bqocdu4oXRV7G2UCEqC5cppdI2nyNwKFpyXFgv36jlyuu9D0ZixBJHRJ1OeGvX+n
0oAB+rxl/Y+4zWj80V5jDvtdzNVCIxZL3Pjs2Vug4FlAI4nKIMCu49Q2FeRIonu3
1uCQOGLnULLyacD5PaKHRblY03aWG58loFRM7iCYvGopEYpl2Z1LjdkFEjkgT4Bq
08kpMNbZeSR+1ufj+qwwj0HM6Jpt28FVxAYAmNHD+b9hCg==
=3YOT
-----END PGP MESSAGE-----
Message:

Salut, tu sais comment utiliser Playwright en mode interactif Python (de sorte à ce que je puisse exécuter des instructions Playwright que j'écris durant l'exécution en fonction de comment réagit le site Internet) ?

from playwright.async_api import async_playwright

p = await async_playwright().start()
browser = await p.firefox.launch(headless=False)

async est nécessaire dans Pyzo et Jupyter Notebook.
await p.firefox.launch(headless=False) n'ouvre pas de fenêtre graphique contrairement à:

Notes personnelles: Benjamin_Loison/overleaf/issues/3#issuecomment-4631.

Python script:
from playwright.async_api import async_playwright

p = await async_playwright().start()
browser = await p.firefox.launch(headless = False)
page = await browser.new_page()
await page.goto("https://example.com")

works as wanted in JupyterLab.

There is no need to await page.wait_for_timeout or similar for the cell output to show ongoing WebSocket traffic, while the cell execution is shown as finished as wanted.

<details> <summary>I was about to send to the person:</summary> ``` -----BEGIN PGP MESSAGE----- hF4DTQa9Wom5MBgSAQdArVmapRinF4lsSfTCyFY6OfQHzpS/apayE/KXQRocCmIw Bqocdu4oXRV7G2UCEqC5cppdI2nyNwKFpyXFgv36jlyuu9D0ZixBJHRJ1OeGvX+n 0oAB+rxl/Y+4zWj80V5jDvtdzNVCIxZL3Pjs2Vug4FlAI4nKIMCu49Q2FeRIonu3 1uCQOGLnULLyacD5PaKHRblY03aWG58loFRM7iCYvGopEYpl2Z1LjdkFEjkgT4Bq 08kpMNbZeSR+1ufj+qwwj0HM6Jpt28FVxAYAmNHD+b9hCg== =3YOT -----END PGP MESSAGE----- ``` </details> <details> <summary>Message:</summary> > Salut, tu sais comment utiliser Playwright en mode interactif Python (de sorte à ce que je puisse exécuter des instructions Playwright que j'écris durant l'exécution en fonction de comment réagit le site Internet) ? > > ```python > from playwright.async_api import async_playwright > > p = await async_playwright().start() > browser = await p.firefox.launch(headless=False) > ``` > > `async` est nécessaire dans Pyzo et Jupyter Notebook. > `await p.firefox.launch(headless=False)` n'ouvre pas de fenêtre graphique contrairement à: > > Notes personnelles: [Benjamin_Loison/overleaf/issues/3#issuecomment-4631](https://gitea.lemnoslife.com/Benjamin_Loison/overleaf/issues/3#issuecomment-4631). </details> <details> <summary>Python script:</summary> ```python from playwright.async_api import async_playwright p = await async_playwright().start() browser = await p.firefox.launch(headless = False) page = await browser.new_page() await page.goto("https://example.com") ``` </details> works as wanted in JupyterLab. There is no need to `await page.wait_for_timeout` or similar for the cell output to show ongoing WebSocket traffic, while the cell execution is shown as finished as wanted.
Author
Owner

DuckDuckGo search Playwright click.

https://playwright.dev/docs/input#mouse-click

Error:
AttributeError                            Traceback (most recent call last)
Cell In[3], line 1
----> 1 print(await page.getByText('Item'))

AttributeError: 'Page' object has no attribute 'getByText'

https://playwright.dev/python/docs/input#mouse-click

.click(position={ "x": 0, "y": 0}) looks interesting.

DuckDuckGo search Playwright click at coordinate.

https://playwright.dev/python/docs/api/class-mouse

print(page.get_by_text('Item'))
2025-12-09 00:27:24.691695 <Locator frame=<Frame name= url='https://plmlatex.math.cnrs.fr/project/XXXXXXXXXXXXXXXXXXXXXXXX'> selector='internal:text="Item"i'>
Error:
Error                                     Traceback (most recent call last)
Cell In[7], line 1
----> 1 await page.get_by_text('Item').click(position = {'x': 0, 'y': 0})

File ~/venv/lib/python3.13/site-packages/playwright/async_api/_generated.py:15527, in Locator.click(self, modifiers, position, delay, button, click_count, timeout, force, no_wait_after, trial)
  15446 async def click(
  15447     self,
  15448     *,
   (...)  15459     trial: typing.Optional[bool] = None,
  15460 ) -> None:
  15461     """Locator.click
  15462 
  15463     Click an element.
   (...)  15523         are pressed.
  15524     """
  15526     return mapping.from_maybe_impl(
> 15527         await self._impl_obj.click(
  15528             modifiers=mapping.to_impl(modifiers),
  15529             position=position,
  15530             delay=delay,
  15531             button=button,
  15532             clickCount=click_count,
  15533             timeout=timeout,
  15534             force=force,
  15535             noWaitAfter=no_wait_after,
  15536             trial=trial,
  15537         )
  15538     )

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_locator.py:160, in Locator.click(self, modifiers, position, delay, button, clickCount, timeout, force, noWaitAfter, trial)
    147 async def click(
    148     self,
    149     modifiers: Sequence[KeyboardModifier] = None,
   (...)    157     trial: bool = None,
    158 ) -> None:
    159     params = locals_to_params(locals())
--> 160     return await self._frame.click(self._selector, strict=True, **params)

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_frame.py:549, in Frame.click(self, selector, modifiers, position, delay, button, clickCount, timeout, force, noWaitAfter, strict, trial)
    535 async def click(
    536     self,
    537     selector: str,
   (...)    547     trial: bool = None,
    548 ) -> None:
--> 549     await self._channel.send("click", self._timeout, locals_to_params(locals()))

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:69, in Channel.send(self, method, timeout_calculator, params, is_internal, title)
     61 async def send(
     62     self,
     63     method: str,
   (...)     67     title: str = None,
     68 ) -> Any:
---> 69     return await self._connection.wrap_api_call(
     70         lambda: self._inner_send(method, timeout_calculator, params, False),
     71         is_internal,
     72         title,
     73     )

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:559, in Connection.wrap_api_call(self, cb, is_internal, title)
    557     return await cb()
    558 except Exception as error:
--> 559     raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None
    560 finally:
    561     self._api_zone.set(None)

Error: Locator.click: Error: strict mode violation: get_by_text("Item") resolved to 5 elements:
    1) <p class="card-hint-text ng-binding" ng-show="entry.humanReadableHint" ng-bind-html="entry.humanReadableHint">You have used the same label more than once. Chec…</p> aka get_by_text("You have used the same label").first
    2) <p class="card-hint-text ng-binding" ng-show="entry.humanReadableHint" ng-bind-html="entry.humanReadableHint">You have used the same label more than once. Chec…</p> aka get_by_text("You have used the same label").nth(1)
    ...

Call log:
  - waiting for get_by_text("Item")
await page.mouse.click(0, 0)

hides:

L'éditeur a été déconnecté. Cliquez n'importe où pour vous reconnecter

by opening Menu.

DuckDuckGo and Google search Playwright get page resolution.

Related to Benjamin_Loison/playwright/issues/6#issuecomment-8808858.

await page.mouse.click(page.viewport_size['width'] // 2, page.viewport_size['height'] // 2)

to test next time, but when there is not the prompt it seems to click at the wanted location.

DuckDuckGo search *Playwright click*. https://playwright.dev/docs/input#mouse-click <details> <summary>Error:</summary> ``` AttributeError Traceback (most recent call last) Cell In[3], line 1 ----> 1 print(await page.getByText('Item')) AttributeError: 'Page' object has no attribute 'getByText' ``` </details> https://playwright.dev/python/docs/input#mouse-click `.click(position={ "x": 0, "y": 0})` looks interesting. DuckDuckGo search *Playwright click at coordinate*. https://playwright.dev/python/docs/api/class-mouse ```python print(page.get_by_text('Item')) ``` ``` 2025-12-09 00:27:24.691695 <Locator frame=<Frame name= url='https://plmlatex.math.cnrs.fr/project/XXXXXXXXXXXXXXXXXXXXXXXX'> selector='internal:text="Item"i'> ``` <details> <summary>Error:</summary> ``` Error Traceback (most recent call last) Cell In[7], line 1 ----> 1 await page.get_by_text('Item').click(position = {'x': 0, 'y': 0}) File ~/venv/lib/python3.13/site-packages/playwright/async_api/_generated.py:15527, in Locator.click(self, modifiers, position, delay, button, click_count, timeout, force, no_wait_after, trial) 15446 async def click( 15447 self, 15448 *, (...) 15459 trial: typing.Optional[bool] = None, 15460 ) -> None: 15461 """Locator.click 15462 15463 Click an element. (...) 15523 are pressed. 15524 """ 15526 return mapping.from_maybe_impl( > 15527 await self._impl_obj.click( 15528 modifiers=mapping.to_impl(modifiers), 15529 position=position, 15530 delay=delay, 15531 button=button, 15532 clickCount=click_count, 15533 timeout=timeout, 15534 force=force, 15535 noWaitAfter=no_wait_after, 15536 trial=trial, 15537 ) 15538 ) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_locator.py:160, in Locator.click(self, modifiers, position, delay, button, clickCount, timeout, force, noWaitAfter, trial) 147 async def click( 148 self, 149 modifiers: Sequence[KeyboardModifier] = None, (...) 157 trial: bool = None, 158 ) -> None: 159 params = locals_to_params(locals()) --> 160 return await self._frame.click(self._selector, strict=True, **params) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_frame.py:549, in Frame.click(self, selector, modifiers, position, delay, button, clickCount, timeout, force, noWaitAfter, strict, trial) 535 async def click( 536 self, 537 selector: str, (...) 547 trial: bool = None, 548 ) -> None: --> 549 await self._channel.send("click", self._timeout, locals_to_params(locals())) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:69, in Channel.send(self, method, timeout_calculator, params, is_internal, title) 61 async def send( 62 self, 63 method: str, (...) 67 title: str = None, 68 ) -> Any: ---> 69 return await self._connection.wrap_api_call( 70 lambda: self._inner_send(method, timeout_calculator, params, False), 71 is_internal, 72 title, 73 ) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:559, in Connection.wrap_api_call(self, cb, is_internal, title) 557 return await cb() 558 except Exception as error: --> 559 raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None 560 finally: 561 self._api_zone.set(None) Error: Locator.click: Error: strict mode violation: get_by_text("Item") resolved to 5 elements: 1) <p class="card-hint-text ng-binding" ng-show="entry.humanReadableHint" ng-bind-html="entry.humanReadableHint">You have used the same label more than once. Chec…</p> aka get_by_text("You have used the same label").first 2) <p class="card-hint-text ng-binding" ng-show="entry.humanReadableHint" ng-bind-html="entry.humanReadableHint">You have used the same label more than once. Chec…</p> aka get_by_text("You have used the same label").nth(1) ... Call log: - waiting for get_by_text("Item") ``` </details> ```python await page.mouse.click(0, 0) ``` hides: > L'éditeur a été déconnecté. Cliquez n'importe où pour vous reconnecter by opening *Menu*. DuckDuckGo and Google search *Playwright get page resolution*. Related to [Benjamin_Loison/playwright/issues/6#issuecomment-8808858](https://codeberg.org/Benjamin_Loison/playwright/issues/6#issuecomment-8808858). ```python await page.mouse.click(page.viewport_size['width'] // 2, page.viewport_size['height'] // 2) ``` to test next time, but when there is not the prompt it seems to click at the wanted location.
Author
Owner
Related to [Benjamin_Loison/firefox/issues/262](https://codeberg.org/Benjamin_Loison/firefox/issues/262).
Author
Owner
[Benjamin_Loison/jupyterlab/issues/19](https://codeberg.org/Benjamin_Loison/jupyterlab/issues/19) would help.
Author
Owner

Despite having received No Overleaf ping, source: https://matrix.to/#/!sNARMdEsFZERaQAJzl:matrix.org/$a2FPiyr6QjiFlRdkm4RL1g8orkTlzJ4Y3pf7tko2Nno, the loop was still going on it seems, so I interrupted it, but it closed the Playwright window.

Error:
---------------------------------------------------------------------------
CancelledError                            Traceback (most recent call last)
Cell In[1], line 109
    107     print('Waiting...')
    108     #await page.wait_for_timeout(2 ** 31 - 1)
--> 109     await page.wait_for_timeout(1)
    110     time.sleep(1)
    112 # Does this make sense?
    113 #thread.join()
    114 # It makes `verifyLastTime` works fine back, but Overleaf does not print anything.
    115 # So *Waiting forever* seems necessary.

File ~/venv/lib/python3.13/site-packages/playwright/async_api/_generated.py:11467, in Page.wait_for_timeout(self, timeout)
  11445 async def wait_for_timeout(self, timeout: float) -> None:
  11446     """Page.wait_for_timeout
  11447 
  11448     Waits for the given `timeout` in milliseconds.
   (...)  11463         A timeout to wait for
  11464     """
  11466     return mapping.from_maybe_impl(
> 11467         await self._impl_obj.wait_for_timeout(timeout=timeout)
  11468     )

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_page.py:1101, in Page.wait_for_timeout(self, timeout)
   1100 async def wait_for_timeout(self, timeout: float) -> None:
-> 1101     await self._main_frame.wait_for_timeout(timeout)

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_frame.py:862, in Frame.wait_for_timeout(self, timeout)
    861 async def wait_for_timeout(self, timeout: float) -> None:
--> 862     await self._channel.send("waitForTimeout", None, {"waitTimeout": timeout})

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:69, in Channel.send(self, method, timeout_calculator, params, is_internal, title)
     61 async def send(
     62     self,
     63     method: str,
   (...)     67     title: str = None,
     68 ) -> Any:
---> 69     return await self._connection.wrap_api_call(
     70         lambda: self._inner_send(method, timeout_calculator, params, False),
     71         is_internal,
     72         title,
     73     )

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:557, in Connection.wrap_api_call(self, cb, is_internal, title)
    555 self._api_zone.set(parsed_st)
    556 try:
--> 557     return await cb()
    558 except Exception as error:
    559     raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None

File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:123, in Channel._inner_send(self, method, timeout_calculator, params, return_as_dict)
    119     raise error
    120 callback = self._connection._send_message_to_server(
    121     self._object, method, _augment_params(params, timeout_calculator)
    122 )
--> 123 done, _ = await asyncio.wait(
    124     {
    125         self._connection._transport.on_error_future,
    126         callback.future,
    127     },
    128     return_when=asyncio.FIRST_COMPLETED,
    129 )
    130 if not callback.future.done():
    131     callback.future.cancel()

File /usr/lib/python3.13/asyncio/tasks.py:451, in wait(fs, timeout, return_when)
    448     raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
    450 loop = events.get_running_loop()
--> 451 return await _wait(fs, timeout, return_when, loop)

File /usr/lib/python3.13/asyncio/tasks.py:537, in _wait(fs, timeout, return_when, loop)
    534     f.add_done_callback(_on_completion)
    536 try:
--> 537     await waiter
    538 finally:
    539     if timeout_handle is not None:

CancelledError: 
Despite having received *No Overleaf ping*, source: https://matrix.to/#/!sNARMdEsFZERaQAJzl:matrix.org/$a2FPiyr6QjiFlRdkm4RL1g8orkTlzJ4Y3pf7tko2Nno, the loop was still going on it seems, so I interrupted it, but it closed the Playwright window. <details> <summary>Error:</summary> ``` --------------------------------------------------------------------------- CancelledError Traceback (most recent call last) Cell In[1], line 109 107 print('Waiting...') 108 #await page.wait_for_timeout(2 ** 31 - 1) --> 109 await page.wait_for_timeout(1) 110 time.sleep(1) 112 # Does this make sense? 113 #thread.join() 114 # It makes `verifyLastTime` works fine back, but Overleaf does not print anything. 115 # So *Waiting forever* seems necessary. File ~/venv/lib/python3.13/site-packages/playwright/async_api/_generated.py:11467, in Page.wait_for_timeout(self, timeout) 11445 async def wait_for_timeout(self, timeout: float) -> None: 11446 """Page.wait_for_timeout 11447 11448 Waits for the given `timeout` in milliseconds. (...) 11463 A timeout to wait for 11464 """ 11466 return mapping.from_maybe_impl( > 11467 await self._impl_obj.wait_for_timeout(timeout=timeout) 11468 ) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_page.py:1101, in Page.wait_for_timeout(self, timeout) 1100 async def wait_for_timeout(self, timeout: float) -> None: -> 1101 await self._main_frame.wait_for_timeout(timeout) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_frame.py:862, in Frame.wait_for_timeout(self, timeout) 861 async def wait_for_timeout(self, timeout: float) -> None: --> 862 await self._channel.send("waitForTimeout", None, {"waitTimeout": timeout}) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:69, in Channel.send(self, method, timeout_calculator, params, is_internal, title) 61 async def send( 62 self, 63 method: str, (...) 67 title: str = None, 68 ) -> Any: ---> 69 return await self._connection.wrap_api_call( 70 lambda: self._inner_send(method, timeout_calculator, params, False), 71 is_internal, 72 title, 73 ) File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:557, in Connection.wrap_api_call(self, cb, is_internal, title) 555 self._api_zone.set(parsed_st) 556 try: --> 557 return await cb() 558 except Exception as error: 559 raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None File ~/venv/lib/python3.13/site-packages/playwright/_impl/_connection.py:123, in Channel._inner_send(self, method, timeout_calculator, params, return_as_dict) 119 raise error 120 callback = self._connection._send_message_to_server( 121 self._object, method, _augment_params(params, timeout_calculator) 122 ) --> 123 done, _ = await asyncio.wait( 124 { 125 self._connection._transport.on_error_future, 126 callback.future, 127 }, 128 return_when=asyncio.FIRST_COMPLETED, 129 ) 130 if not callback.future.done(): 131 callback.future.cancel() File /usr/lib/python3.13/asyncio/tasks.py:451, in wait(fs, timeout, return_when) 448 raise TypeError("Passing coroutines is forbidden, use tasks explicitly.") 450 loop = events.get_running_loop() --> 451 return await _wait(fs, timeout, return_when, loop) File /usr/lib/python3.13/asyncio/tasks.py:537, in _wait(fs, timeout, return_when, loop) 534 f.add_done_callback(_on_completion) 536 try: --> 537 await waiter 538 finally: 539 if timeout_handle is not None: CancelledError: ``` </details>
Author
Owner

To not crash due to print(f'Checking at {currentTime} ({lastTime=}, {asyncWait=})') global asyncWait is necessary in verifyLastTime.

To not crash due to `print(f'Checking at {currentTime} ({lastTime=}, {asyncWait=})')` `global asyncWait` is necessary in `verifyLastTime`.
Author
Owner

DuckDuckGo search Playwright get element by text.

https://playwright.dev/docs/locators#locate-by-text

DuckDuckGo search Playwright Python click.

The Stack Overflow question 76632423 thread seems to show that I proceed correctly.

DuckDuckGo search playwright refresh webpage.

https://playwright.dev/docs/api/class-page#page-reload

DuckDuckGo search *Playwright get element by text*. https://playwright.dev/docs/locators#locate-by-text DuckDuckGo search *Playwright Python click*. [The Stack Overflow question 76632423](https://stackoverflow.com/q/76632423) thread seems to show that I proceed correctly. DuckDuckGo search *playwright refresh webpage*. https://playwright.dev/docs/api/class-page#page-reload
Author
Owner
await page.get_by_text("Actualiser l'éditeur").click()

results in out of sync prompt if I remember correctly.

```python await page.get_by_text("Actualiser l'éditeur").click() ``` results in out of sync prompt if I remember correctly.
Author
Owner

I confirm that await page.reload() solves the issue, now the point is keep listening.

An alternative is just to restart the Python script on exit, but it is unsatisfying and it may be inefficient from a script initialization point of view.

I confirm that `await page.reload()` solves the issue, now the point is keep listening. An alternative is just to restart the Python script on exit, but it is unsatisfying and it may be inefficient from a script initialization point of view.
Author
Owner

Maybe can't in 2 threads proceed to Playwright async calls, even if await page.wait_for_timeout(1) is quite inactive.

Maybe can't in 2 threads proceed to Playwright `async` calls, even if `await page.wait_for_timeout(1)` is quite inactive.
Author
Owner

async def verifyLastTime(): does not seem executed in thread.

DuckDuckGo search Python thread async.

The Stack Overflow answer 61778654 helps.

`async def verifyLastTime():` does not seem executed in thread. DuckDuckGo search *Python thread async*. [The Stack Overflow answer 61778654](https://stackoverflow.com/a/61778654) helps.
Author
Owner
jq -r '.[0].outputs[0].text' cell.json > output.txt

does not return anything and works as wanted.

```bash jq -r '.[0].outputs[0].text' cell.json > output.txt ``` does not return anything and works as wanted.
Author
Owner

await page.reload() blocks forever.

Let us try to not use another thread, as it does not seem necessary anymore, as while asyncWait: was commented and the behavior except expiration was as wanted it seems.

`await page.reload()` blocks forever. Let us try to not use another thread, as it does not seem necessary anymore, as `while asyncWait:` was commented and the behavior except expiration was as wanted it seems.
Author
Owner

In such case only see Checking output which is not satisfying.

In such case only see *Checking* output which is not satisfying.
Author
Owner

DuckDuckGo and Google search Jupyter disable Python output buffer does not seem helpful.

Let us uncomment while asyncWait: and see if it helps, otherwise will set up a shared boolean and proceed to webpage reload from the main thread.

DuckDuckGo and Google search *Jupyter disable Python output buffer* does not seem helpful. Let us uncomment `while asyncWait:` and see if it helps, otherwise will set up a shared boolean and proceed to webpage reload from the main thread.
Author
Owner

At least now APT and pip packages are up-to-date.

At least now APT and pip packages are up-to-date.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Benjamin_Loison/overleaf#3