balta2ar / brotab

Control your browser's tabs from the command line
MIT License
389 stars 27 forks source link

Using brotab with python directly #38

Open Fr33Fun opened 4 years ago

Fr33Fun commented 4 years ago

Hi, -Noob here, Is there a way to use brotab directly in my python scripts? something like:

import brotab myList = brotab.list() -returning a list of "Tab" objects brotab.close(myList[x])
or is this already possible(since it is written in python?) if his is already possible -how is the syntax?

thank you in advance

balta2ar commented 4 years ago

if your scripts are short-lived fire and forget, then yes. here is an example:

from brotab.api import MultipleMediatorsAPI
from brotab.main import create_clients
api = MultipleMediatorsAPI(create_clients())
result = api.list_tabs([])[10:13]
for line in result:
    print(line)
$ python3 ./test.py
a.1.430 Using brotab with python directly · Issue #38 · balta2ar/brotab https://github.com/balta2ar/brotab/issues/38
a.1.436 brotab/brotab at master · balta2ar/brotab       https://github.com/balta2ar/brotab/tree/master/brotab
a.1.439 brotab/parallel.py at master · balta2ar/brotab  https://github.com/balta2ar/brotab/blob/master/brotab/parallel.py

However, when I tried to call that method twice, I found a problem here: https://github.com/balta2ar/brotab/blob/master/brotab/parallel.py#L23. Default event loop is closed.

Even though brotab was not quite designed for such use cases (there could be rough edges), it is still possible (because that's what brotab does internally). You can check the main.py for more examples, it should be pretty intuitive: https://github.com/balta2ar/brotab/blob/master/brotab/main.py#L148

CRImier commented 4 years ago

I've just written some examples in Python - you can see them here https://github.com/CRImier/dudetab

drocta commented 2 years ago

With regards to the error with the "Event loop is closed", I believe I have a workaround. If you run asyncio.set_event_loop(asyncio.new_event_loop()) before calling api.list_tabs again, it works.

I haven't gone through the process of setting up docker in order to contribute properly to this, and at least for the time being I think I'll just use that workaround, but I think that if, in parallel.py (linking the version at the particular commit, so it is clear to any future readers what version I'm quoting in case the file is changed) if you replace

loop = get_event_loop()

with

loop = get_event_loop()
if(loop.is_closed()):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

(and, of course, replace the from asyncio import get_event_loop, gather with from asyncio import get_event_loop, new_event_loop, set_event_loop, gather ), then I think that should fix the issue. But I both haven't tested this, and have roughly no experience using the asyncio library, so take that with a decently sized serving of salt.

Also, while I think it would fix the issue, I suspect that it isn't "the right way" to fix the issue (though, I don't know at all what "the right way" to fix the issue would be.)

Also, thanks for making this nice program/library!

drocta commented 1 year ago

Now coming back to this over a year later, I realize something that I don't understand. It isn't clear to me what purpose the use of call_parallel is serving. It looks to me as though the only place that call_parallel is used, is in order to query the different browsers simultaneously, rather than in sequence. But, as a typical user will likely only have at most 2 or 3 distinct browsers open at once (if they are running firefox, chrome, and chromium?), and typically only one, I don't see why having the different browsers be queried in parallel, would be important.

I've tested the following, (though only when only one browser set up with brotab is running), and it seems to work fine, and seems less hacky then having to make a new event loop every time your script needs to query some tabs as in my above work-around.

import brotab.api as btapi
from brotab.main import create_clients

def call_all(someList):
    outList = []
    try:
        for f in someList:
            outList.append(f())
    except:
        print("couldn't call it...")
    return outList

btapi.call_parallel = call_all

api = btapi.MultipleMediatorsAPI(create_clients())
#and now you are free to call things like api.list_tabs multiple times without the error with asyncio
#and, as a bonus, if you want to use this in a larger (say, interactive) program that also uses asyncio,
# then I think this will make it so it doesn't step on the toes of the rest of the program?

#following is demonstration that this works

allTabs1 = api.list_tabs([])
import time
print("sleeping for 7 seconds. Open another tab to confirm that it really is checking it two separate times.")
time.sleep(7)
allTabs2 = api.list_tabs([])
print(f"tabs in 1st list: {len(allTabs1)}")
print(f"tabs in 2nd list: {len(allTabs2)}")

This seems to work great for me. Hoping it will also help anyone else looking to do similarly.