isaackogan / TikTokLive

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

TypeError: issubclass() arg 1 must be a class #183

Closed diego-fmachado closed 2 months ago

diego-fmachado commented 3 months ago

Traceback (most recent call last): File "...\test.py", line 24, in client.run() File "...\test.py", line 21, in run return self._client.run() ^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\TikTokLive\client\client.py", line 170, in run return self._asyncio_loop.run_until_complete(self.connect(**kwargs)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "...\Lib\site-packages\TikTokLive\client\client.py", line 155, in connect await task File "...\Lib\site-packages\TikTokLive\client\client.py", line 203, in _client_loop async for event in self._ws_loop(initial_response): File "...\Lib\site-packages\TikTokLive\client\client.py", line 240, in _ws_loop for event in self._parse_webcast_response(response_message): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\TikTokLive\client\client.py", line 322, in _parse_webcast_response proto_event: ProtoEvent = event_type().parse(response.payload) ^^^^^^^^^^^^ File "", line 16, in init File "...\Lib\site-packages\betterproto__init.py", line 631, in post_init for field_name, meta in self._betterproto.meta_by_field_name.items(): ^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\betterproto__init.py", line 698, in getattribute value = super().getattribute(name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\betterproto\init__.py", line 754, in _betterproto meta = ProtoClassMetadata(self.class) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\betterproto__init.py", line 568, in init self.default_gen = self._get_default_gen(cls, fields) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\betterproto\init__.py", line 575, in _get_default_gen return {field.name: cls._get_field_default_gen(field) for field in fields} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\betterproto\init.py", line 575, in return {field.name: cls._get_field_default_gen(field) for field in fields} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "...\Lib\site-packages\betterproto\init__.py", line 910, in _get_field_default_gen elif issubclass(t, Enum): ^^^^^^^^^^^^^^^^^^^ TypeError: issubclass() arg 1 must be a class

Unchangeable commented 3 months ago

I'm getting the exactly same error. Any fix for this ?

OS: Win 11 Python version: 3.10.10 Last version of TikTokLive

isaackogan commented 3 months ago

I need debug...or code...or anything...

diego-fmachado commented 3 months ago

This one might be occurring in Windows only, as I'm also using it. It is pretty straight forward, after you start the client, after some seconds, it crashes with this error.

diego-fmachado commented 3 months ago

It might happen with any example code on this platform, I was using the client just to print the comments, while testing

Opybot commented 3 months ago

This happened to me too. Windows. With Example codes.

Unchangeable commented 3 months ago

Even exemples codes trigger this error, it might be due to some gifts I think... but not sure. If i'm starting this on a Live Stream with 1-2 people that doens't have activity it doesn't throw this error, but if i start it on a stream with a higher amount of people into the Live Stream it throws very quickly.

And also along that error is one more that is quickly throwed and randomly:

Traceback (most recent call last): File "D:\BibFullTikTok\main.py", line 172, in asyncio.run(check_loop()) File "C:\Program Files\Python310\lib\asyncio\runners.py", line 44, in run return loop.run_until_complete(main) File "C:\Program Files\Python310\lib\asyncio\base_events.py", line 649, in run_until_complete return future.result() File "D:\BibFullTikTok\main.py", line 166, in check_loop await client.connect() File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 155, in connect await task File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 203, in _client_loop async for event in self._ws_loop(initial_response): File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 240, in _ws_loop for event in self._parse_webcast_response(response_message): File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 322, in _parse_webcast_response proto_event: ProtoEvent = event_type().parse(response.payload) File "D:\BibFullTikTok\venv\lib\site-packages\betterproto__init.py", line 1015, in parse value = self._postprocess_single( File "D:\BibFullTikTok\venv\lib\site-packages\betterproto__init.py", line 955, in _postprocess_single value = cls().parse(value) File "D:\BibFullTikTok\venv\lib\site-packages\betterproto\init.py", line 1015, in parse value = self._postprocess_single( File "D:\BibFullTikTok\venv\lib\site-packages\betterproto\init.py", line 955, in _postprocess_single value = cls().parse(value) File "D:\BibFullTikTok\venv\lib\site-packages\betterproto\init__.py", line 1008, in parse decoded, pos = decode_varint(parsed.value, pos) File "D:\BibFullTikTok\venv\lib\site-packages\betterproto\init__.py", line 487, in decode_varint raise ValueError("Too many bytes when decoding varint.") ValueError: Too many bytes when decoding varint.

Process finished with exit code 1

isaackogan commented 3 months ago

someone give me some code i can reproduce this with please

Unchangeable commented 3 months ago
import aiohttp
import re
import datetime

from TikTokLive import TikTokLiveClient
from TikTokLive.client.logger import LogLevel
from TikTokLive.events import ConnectEvent, CommentEvent, GiftEvent, JoinEvent, LikeEvent

client: TikTokLiveClient = TikTokLiveClient(
    unique_id="@biancabiiiiib"
)

async def reg_db(user, nickname, avatar, level, following, followers, last_seen):
    url = 'http://localhost:5000/add_user'
    data = {
        'user': user,
        'nickname': nickname,
        'avatar': avatar,
        'level': level,
        'following_count': following,
        'follower_count': followers,
        'song_credits': 0,
        'last_seen': last_seen,
        'creation_date': datetime.datetime.now().isoformat()
    }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=data) as response:
            return await response.text()

async def taptap(user, nickname, count):
    url = 'http://localhost:5000/add_taptap'
    data = {
        'user': user,
        'nickname': nickname,
        'count': count
    }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=data) as response:
            return await response.text()

async def giftz(user, nickname, gift_name, count):
    url = 'http://localhost:5000/add_gift'
    data = {
        'user': user,
        'nickname': nickname,
        'gift_name': gift_name,
        'count': count
    }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=data) as response:
            return await response.text()

async def play(user, video):
    url = f'http://localhost:5000/play/{user}/{video}'
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

@client.on(ConnectEvent)
async def on_connect(event: ConnectEvent):
    client.logger.info(f"Connected to @{event.unique_id}!")

async def on_comment(event: CommentEvent) -> None:
    video_id_pattern = r"(?:v=|\/)([0-9A-Za-z_-]{10,12})(?:&|\/|$)"
    match = re.search(video_id_pattern, event.comment)
    if match:
        video_id = match.group(1)
        await play(event.user.unique_id, video_id)

@client.on(JoinEvent)
async def on_join(event: JoinEvent):
    #client.logger.info(f"Joined the live @{event.user.unique_id}!")
    badge_list = event.user.badge_list
    str_value = ""
    if badge_list and len(badge_list) > 0:
        str_value = badge_list[0].combine.str

    await reg_db(event.user.unique_id, event.user.nickname, event.user.avatar_thumb.url_list[2], str_value,
           event.user.follow_info.following_count, event.user.follow_info.follower_count,
           datetime.datetime.now().isoformat())

@client.on(LikeEvent)
async def on_like(event: LikeEvent):
    #client.logger.info(f"TapTap @{event.user.unique_id} -> {event.count}!")
    await taptap(event.user.unique_id, event.user.nickname, event.count)

@client.on(GiftEvent)
async def on_gift(event: GiftEvent):
    if event.gift.streakable and not event.streaking:
        await giftz(event.user.unique_id, event.user.nickname, event.gift.name, event.gift.count)

    # Non-streakable gift
    elif not event.gift.streakable:
        await giftz(event.user.unique_id, event.user.nickname, event.gift.name, event.gift.diamond_count)

        badge_list = event.user.badge_list
        str_value = ""
        if badge_list and len(badge_list) > 0:
            str_value = badge_list[0].combine.str

        await reg_db(event.user.unique_id, event.user.nickname, event.user.avatar_thumb.url_list[2], str_value,
                     event.user.follow_info.following_count, event.user.follow_info.follower_count,
                     datetime.datetime.now().isoformat())

async def check_loop():
    # Run 24/7
    while True:

        # Check if they're live
        while not await client.is_live():
            client.logger.info("Client is currently not live. Checking again in 60 seconds.")
            await asyncio.sleep(120)  # Spamming the endpoint will get you blocked

        # Connect once they become live
        client.logger.info("Requested client is live!")
        await client.connect()

client.add_listener(CommentEvent, on_comment)

if __name__ == '__main__':
    client.logger.setLevel(LogLevel.INFO.value)
    asyncio.run(check_loop())

And also the API :

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
import subprocess

app = Flask(__name__)
ytcast_path = r"C:\Users\twiff\Downloads\ytcast-v1.4.0-windows-amd64\ytcast.exe"

# Configure your MySQL database; replace USERNAME, PASSWORD, SERVER, and DBNAME with your actual database credentials
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:@127.0.0.1/bib_live?charset=utf8mb4'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
    'connect_args': {
        'init_command': "SET time_zone='+03:00';",  # Bucharest timezone offset
    }
}

db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(80), nullable=False)
    nickname = db.Column(db.String(80), nullable=True)
    avatar = db.Column(db.String(200), nullable=True)
    level = db.Column(db.String(200), nullable=True, default=0)
    following_count = db.Column(db.Integer, default=0, nullable=True)
    follower_count = db.Column(db.Integer, default=0, nullable=True)
    song_credits = db.Column(db.Integer, default=0)
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    creation_date = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return '<User %r>' % self.nickname

class TapTap(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(80), nullable=False)
    nickname = db.Column(db.String(80), nullable=True)
    count = db.Column(db.Integer, nullable=True)
    creation_date = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return '<User %r>' % self.nickname

class Gifts(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(80), nullable=False)
    nickname = db.Column(db.String(80), nullable=True)
    gift_name = db.Column(db.String(200), nullable=True)
    count = db.Column(db.Integer, nullable=True)
    creation_date = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return '<User %r>' % self.nickname

class Play(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(80), nullable=False)
    nickname = db.Column(db.String(80), nullable=True)
    creation_date = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return '<User %r>' % self.nickname

#with app.app_context():
#    db.create_all()

@app.route('/add_user', methods=['POST'])
def add_or_update_user():
    data = request.json
    user_identifier = data.get('user')  # Assuming 'user' is a unique identifier

    # Query the database for the user
    existing_user = User.query.filter_by(user=user_identifier).first()

    if existing_user:
        # If the user exists, update the last_seen time
        existing_user.last_seen = datetime.utcnow()
        db.session.commit()
        return jsonify({'message': 'User last_seen updated successfully!'}), 200
    else:
        # If the user does not exist, add a new user
        new_user = User(
            user=data.get('user'),
            nickname=data.get('nickname'),
            avatar=data.get('avatar'),
            level=data.get('level', 1),
            following_count=data.get('following_count', 0),
            follower_count=data.get('follower_count', 0),
            song_credits=data.get('song_credits', 0),
            last_seen=data.get('last_seen', datetime.utcnow()),
            creation_date=data.get('creation_date', datetime.utcnow())
        )
        db.session.add(new_user)
        db.session.commit()
        return jsonify({'message': 'User added successfully!'}), 201

@app.route('/add_taptap', methods=['POST'])
def add_taptapz():
    data = request.json
    new_taptap = TapTap(
        user=data.get('user'),
        nickname=data.get('nickname'),
        count=data.get('count'),
        creation_date=data.get('creation_date', datetime.utcnow())
    )
    db.session.add(new_taptap)
    db.session.commit()
    return jsonify({'message': 'User added successfully!'}), 201

@app.route('/add_gift', methods=['POST'])
def add_giftz():
    data = request.json
    new_gift = Gifts(
        user=data.get('user'),
        nickname=data.get('nickname'),
        gift_name=data.get('gift_name'),
        count=data.get('count'),
        creation_date=data.get('creation_date', datetime.utcnow())
    )
    db.session.add(new_gift)
    db.session.commit()
    return jsonify({'message': 'User added successfully!'}), 201

@app.route('/update_play/<int:play_id>', methods=['POST'])
def update_play(play_id):
    play_to_update = Play.query.get(play_id)

    if play_to_update:
        # Update the creation_date field to the current date and time
        play_to_update.creation_date = datetime.utcnow()

        db.session.commit()
        return jsonify({'message': 'Play creation date updated successfully!'}), 200
    else:
        return jsonify({'message': 'Play record not found.'}), 404

@app.route('/play/<user_identifier>/<video>', methods=['GET'])
def check_song_credits_and_play(user_identifier, video):
    # Query the database for the user by the unique identifier
    user = User.query.filter_by(user=user_identifier).first()
    print(user)
    if user:
        # Check if the user has song credits
        if user.song_credits <= 0:
            return jsonify({
                'message': 'User has no song credits.',
                'song_credits': user.song_credits,
                'allow_play': False
            }), 200

        # Get the most recent item from the Play table for the user
        last_play = Play.query.filter_by(id=1).order_by(Play.creation_date.desc()).first()
        print(last_play)
        if last_play:
            da = datetime.utcnow() + timedelta(hours=2)
            time_diff = da - last_play.creation_date
            # Check if the time difference is less than 60 seconds
            print(time_diff)
            print(time_diff.total_seconds())
            if time_diff.total_seconds() < 60:
                return jsonify({
                    'message': 'Cannot play the song. Wait until 60 seconds have passed since the last play.',
                    'allow_play': False
                }), 200

        # Deduct one song credit because the user is allowed to play the song
        user.song_credits -= 1
        command = [ytcast_path, "-d", "e4e54d33", "https://youtu.be/"+ video]
        try:
            result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            print("Output:", result.stdout)
        except subprocess.CalledProcessError as e:
            print("Error:", e.stderr)
        last_play.creation_date = datetime.utcnow() + timedelta(hours=2)
        db.session.commit()
        return jsonify({
            'message': 'Song played successfully. One credit deducted.',
            'song_credits': user.song_credits,
            'allow_play': True
        }), 200
    else:
        return jsonify({'message': 'User not found.'}), 404

if __name__ == '__main__':
    app.run(debug=True)
Opybot commented 3 months ago

I commented out:

client.add_listener(GiftEvent, on_gift)

and still got the error. so its not something with gifts..

Opybot commented 3 months ago

Python code: https://pastebin.com/xY3SNN6S Error logged: https://pastebin.com/WtWguwaU

SeanStar commented 3 months ago

With how much stuff was added to proto in this latest update, I wouldn't be surprised if there's data types returned by tiktok that are being improperly handled from the webcast response.

The following code will crash within seconds if you insert a unique id of a live user that has a lot of events being passed:


from TikTokLive.client.client import TikTokLiveClient
from TikTokLive.client.logger import LogLevel
from TikTokLive.events import ConnectEvent

client: TikTokLiveClient = TikTokLiveClient(
    unique_id="@" # Insert a unique id of a live user that has a lot of events being passed
)

@client.on(ConnectEvent)
async def on_connect(event: ConnectEvent):
    client.logger.info(f"Connected to @{event.unique_id}!")

if __name__ == '__main__':
    # Enable debug info
    client.logger.setLevel(LogLevel.INFO.value)

    # Connect
    client.run()
isaackogan commented 3 months ago

The interim solution is there should be a proper error handler that doesn't crash the client LOL...then we can fix this specific proto error

SeanStar commented 3 months ago

The interim solution is there should be a proper error handler that doesn't crash the client LOL...then we can fix this specific proto error

You should probably add exception handling that captures the payload so it's easier to find which ones the schema fails to parse properly at the very least.

isaackogan commented 3 months ago

Unfortunately I will internally combust it if I have to add another thing onto my plate right now. Waaaay too stressed for this. PRs welcome or I'll get to it when I have a sec.

Unchangeable commented 3 months ago

Any update on this error ? :<

isaackogan commented 3 months ago

Yes,

I am embarassed. This was a stupid issue.

The internal Type I created "MessageType" shadowed a TikTok proto field also called "MessageType". This caused an instance of the TypeVar to be passed to an is_subclass in the proto type-checker, throwing the familiar and horrifying error.

Release: https://github.com/isaackogan/TikTokLive/releases/tag/v6.0.1-post1 Commit: https://github.com/isaackogan/TikTokLive/commit/5faa492f7988b5fc0956594e71ccf6a37f1180cd Install: pip install TikTokLive==6.0.1.post1

Will close this in 2 days if the issue is resolved.

Unchangeable commented 3 months ago

Thank you for the fix. I'm running it now, it looks like it was fixed and it does work. I'll keep an eye on it and if theres any other bug i'll report it.

Also check the error with "AttributeError: 'GiftEvent' object has no attribute 'streakable'"

Exception in callback AsyncIOEventEmitter._emit_run.<locals>.callback(<Task finishe...streakable'")>) at D:\BibFullTikTok\venv\lib\site-packages\pyee\asyncio.py:69
handle: <Handle AsyncIOEventEmitter._emit_run.<locals>.callback(<Task finishe...streakable'")>) at D:\BibFullTikTok\venv\lib\site-packages\pyee\asyncio.py:69>
Traceback (most recent call last):
  File "C:\Program Files\Python310\lib\asyncio\events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "D:\BibFullTikTok\venv\lib\site-packages\pyee\asyncio.py", line 77, in callback
    self.emit("error", exc)
  File "D:\BibFullTikTok\venv\lib\site-packages\pyee\base.py", line 211, in emit
    self._emit_handle_potential_error(event, args[0] if args else None)
  File "D:\BibFullTikTok\venv\lib\site-packages\pyee\base.py", line 169, in _emit_handle_potential_error
    raise error
  File "D:\BibFullTikTok\main.py", line 111, in on_gift
    if event.gift.streakable and not event.streaking:
  File "D:\BibFullTikTok\venv\lib\site-packages\betterproto\__init__.py", line 698, in __getattribute__
    value = super().__getattribute__(name)
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\events\proto_events.py", line 99, in streaking
    if not self.streakable:
  File "D:\BibFullTikTok\venv\lib\site-packages\betterproto\__init__.py", line 698, in __getattribute__
    value = super().__getattribute__(name)
AttributeError: 'GiftEvent' object has no attribute 'streakable'
isaackogan commented 3 months ago

Also check the error with "AttributeError: 'GiftEvent' object has no attribute 'streakable'"

Way ahead of you.

pip install TikTokLive==6.0.1.post2

Unchangeable commented 3 months ago

Found another one after 13 minutes of running it

Traceback (most recent call last):
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 963, in transfer_data
    message = await self.read_message()
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 1033, in read_message
    frame = await self.read_data_frame(max_size=self.max_size)
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 1108, in read_data_frame
    frame = await self.read_frame(max_size)
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 1165, in read_frame
    frame = await Frame.read(
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\framing.py", line 68, in read
    data = await reader(2)
  File "C:\Program Files\Python310\lib\asyncio\streams.py", line 705, in readexactly
    raise exceptions.IncompleteReadError(incomplete, n)
asyncio.exceptions.IncompleteReadError: 0 bytes read on a total of 2 expected bytes

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\BibFullTikTok\main.py", line 147, in <module>
    asyncio.run(check_loop())
  File "C:\Program Files\Python310\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Program Files\Python310\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
  File "D:\BibFullTikTok\main.py", line 141, in check_loop
    await client.connect()
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 156, in connect
    await task
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 204, in _client_loop
    async for event in self._ws_loop(initial_response):
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 228, in _ws_loop
    async for response_message in self._ws.connect(*self._build_connect_info(initial_response)):
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\ws\ws_client.py", line 145, in connect
    async for webcast_message in self.connect_loop(uri, headers):
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\ws\ws_client.py", line 181, in connect_loop
    async for message in websocket:
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 498, in __aiter__
    yield await self.recv()
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 568, in recv
    await self.ensure_open()
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 939, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: no close frame received or sent

Process finished with exit code 1
Unchangeable commented 3 months ago
(venv) D:\BibFullTikTok>pip install TikTokLive==6.0.1.post2
ERROR: Ignored the following yanked versions: 4.3.2
ERROR: Could not find a version that satisfies the requirement TikTokLive==6.0.1.post2 (from versions: 0.0.1, 0.6.9, 0.7.0, 0.7.5, 0.8.0, 0.8.2, 0.8.5, 0.8.6, 0.8.9, 4.2.0, 4.2.5, 4.2.6, 4.3.0, 4.3.3, 4.3.5, 4.3.6, 4.3.7, 4.3.8, 4.5.0, 4.5.1, 4.5.2, 5.0.0, 5.0.1, 5.0.5, 5.0.6, 5.0.7, 5.0.8, 6.0.0rc1, 6.0.0, 6.0.0.post1, 6.0.1, 6.0.1.post1, 6.0.2.post1)
ERROR: No matching distribution found for TikTokLive==6.0.1.post2
isaackogan commented 3 months ago

My dumb-ass self named it 6.0.2.post1...

isaackogan commented 3 months ago

Found another one after 13 minutes of running it

Traceback (most recent call last):
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 963, in transfer_data
    message = await self.read_message()
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 1033, in read_message
    frame = await self.read_data_frame(max_size=self.max_size)
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 1108, in read_data_frame
    frame = await self.read_frame(max_size)
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 1165, in read_frame
    frame = await Frame.read(
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\framing.py", line 68, in read
    data = await reader(2)
  File "C:\Program Files\Python310\lib\asyncio\streams.py", line 705, in readexactly
    raise exceptions.IncompleteReadError(incomplete, n)
asyncio.exceptions.IncompleteReadError: 0 bytes read on a total of 2 expected bytes

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\BibFullTikTok\main.py", line 147, in <module>
    asyncio.run(check_loop())
  File "C:\Program Files\Python310\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Program Files\Python310\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
  File "D:\BibFullTikTok\main.py", line 141, in check_loop
    await client.connect()
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 156, in connect
    await task
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 204, in _client_loop
    async for event in self._ws_loop(initial_response):
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\client.py", line 228, in _ws_loop
    async for response_message in self._ws.connect(*self._build_connect_info(initial_response)):
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\ws\ws_client.py", line 145, in connect
    async for webcast_message in self.connect_loop(uri, headers):
  File "D:\BibFullTikTok\venv\lib\site-packages\TikTokLive\client\ws\ws_client.py", line 181, in connect_loop
    async for message in websocket:
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 498, in __aiter__
    yield await self.recv()
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 568, in recv
    await self.ensure_open()
  File "D:\BibFullTikTok\venv\lib\site-packages\websockets\legacy\protocol.py", line 939, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: no close frame received or sent

Process finished with exit code 1

Did the stream stop perhaps? This error suggests the connection was closed by TikTok "suddenly".

Unchangeable commented 3 months ago

Nope the stream was still active, watching it.

isaackogan commented 3 months ago

Welp, sorry to say I have put in all the time I have today. I will float this by the folks at TikTokLiveJava and see if they've seen this...that's all I can do.

Reading up on it, what you are describing seems like a client issue, not a TikTokLive issue.

See here: https://websockets.readthedocs.io/en/stable/faq/common.html#what-does-connectionclosederror-no-close-frame-received-or-sent-mean

isaackogan commented 2 months ago

I'm going to close this as resolved. Please open another issue @Unchangeable if you continue to face your separate issue, so I can fix it.