isaackogan / TikTokLive

Python library to receive live stream events (comments, gifts, etc.) in realtime from TikTok LIVE.
https://isaackogan.github.io/TikTokLive/
MIT License
857 stars 173 forks source link

error when running on a new thread #63

Closed tmokmss closed 2 years ago

tmokmss commented 2 years ago

Hi I'm a newbie who just started exploring TikTok world and thank you for developing this great toolkit! I found a weird behavior as below. Could you confirm it as expected or not? Thanks!

Describe the bug When I execute TikTokLiveClient.run on a newly created thread, it crashes.

To Reproduce Run the code below:

import threading
import time
import asyncio
from TikTokLive import TikTokLiveClient

def start_tiktok():
    # loop = asyncio.new_event_loop()
    # asyncio.set_event_loop(loop)
    client = TikTokLiveClient(unique_id='@isaackogz')
    client.run()

thread = threading.Thread(target=start_tiktok)
thread.daemon = True
thread.start()

time.sleep(10)

and we get this error:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/home/ec2-user/.local/lib/python3.7/site-packages/TikTokLive/client/base.py", line 73, in __init__
    self.loop: AbstractEventLoop = asyncio.get_event_loop()
  File "/usr/lib64/python3.7/asyncio/events.py", line 644, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.7/threading.py", line 926, in _bootstrap_inner
    self.run()
  File "/usr/lib64/python3.7/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "repro.py", line 9, in start_tiktok
    subscription = TikTokLiveClient(unique_id='@tv_asahi_news')
  File "/home/ec2-user/.local/lib/python3.7/site-packages/TikTokLive/client/client.py", line 31, in __init__
    BaseClient.__init__(self, unique_id, **options)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/TikTokLive/client/base.py", line 75, in __init__
    self.loop: AbstractEventLoop = asyncio.get_running_loop()
RuntimeError: no running event loop

If I uncomment the two lines, it works without problem.

     loop = asyncio.new_event_loop()
     asyncio.set_event_loop(loop)

Shouldn't this be handled inside the library? I saw the following description in README.md

Optionally supply your own asyncio event loop for usage by the client. When set to None, the client pulls the current active loop or creates a new one. This option is mostly useful for people trying to nest asyncio.

Expected behavior Code runs without problem.

Additional context Add any other context about the problem here.

isaackogan commented 2 years ago

Current Behaviour

It's interesting because it works in the main thread, but not in new threads. I do not know why this is. More than likely I misunderstood the use of get_event_loop

tmokmss commented 2 years ago

If we want to achieve the below behavior, how about this code?

the client pulls the current active loop or creates a new one.

            try:
                self.loop: AbstractEventLoop = asyncio.get_running_loop()
            except RuntimeError:
                # if there is no running loop, create a new event loop
                self.loop: AbstractEventLoop = asyncio.new_event_loop()

I guess no event loop is set by default for a new thread.

Also note that starting from python 3.10, get_event_loop becomes deprecated and it will be identical with get_running_loop (doc)

isaackogan commented 2 years ago

If we want to achieve the below behavior, how about this code?

the client pulls the current active loop or creates a new one.

            try:
                self.loop: AbstractEventLoop = asyncio.get_running_loop()
            except RuntimeError:
                # if there is no running loop, create a new event loop
                self.loop: AbstractEventLoop = asyncio.new_event_loop()

I guess no event loop is set by default for a new thread.

Also note that starting from python 3.10, get_event_loop becomes deprecated and it will be identical with get_running_loop (doc)

Looks good. Do you want to make a pull request? If not, I will when I have a bit of free time next.

My job can betime consuming. Plus, 'twas your find :)

tmokmss commented 2 years ago

@isaackogan Hi thanks for the offer. I submitted a PR for this. Hope you can check it when you get a chance 🙏 .

isaackogan commented 2 years ago

Tested locally in 3.8, looks good. Merged into master and will be released in today's update

isaackogan commented 2 years ago

One comment, @tmokmss

If your intention is to run multiple clients, there's an IP rate limit for signing connections to respect fair use of TikTok services.

There are a million different reasons to thread, so I won't presume that's why you want to, but, just in-case you wanted to set up a bunch of clients at once, you will need an API key.

Thanks for the contribution, releasing the update now!

tmokmss commented 2 years ago

New API key feature? Sounds cool, thanks! I'm currently using it in my small hobby project (hoping it is not harming your server :pray: ) but I'll definitely contact you when I need larger-scale access.

isaackogan commented 2 years ago

For sure! The server can handle a few hundred requests a second before I'd need to up the capacity, which will never happen. Just don't want TikTok to tighten security again because the library is used for spam!

Thanks for the help, enjoy the new version! Isaac