Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connection eventually gets detached from screen #9

Open
FabioGNR opened this issue May 1, 2024 · 3 comments
Open

Connection eventually gets detached from screen #9

FabioGNR opened this issue May 1, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@FabioGNR
Copy link
Owner

FabioGNR commented May 1, 2024

Using code that continually reconnects and refreshes authorization, eventually the real connection to the lounge session is lost.
Instead a sort of phantom session becomes active, where no events are being sent, but there's no errors from the API.
Calling refresh_auth usually fixes this situation, however it should automatically be detected and prevented.

The following sample code can be used to observe this:

import asyncio
from pyytlounge import YtLoungeApi, PlaybackState, State
from ast import literal_eval
import os

AUTH_STATE_FILE = "auth_state"
CONNECT_RETRY_INTERVAL = 10
ERROR_RETRY_INTERVAL = 30
SUBSCRIBE_RETRY_INTERVAL = 1

async def go():
    api = YtLoungeApi("Test")
    if os.path.exists(AUTH_STATE_FILE):
        with open(AUTH_STATE_FILE, "r") as f:
            content = f.read()
            api.load_auth_state(literal_eval(content))
            print("Loaded from file")
    else:
        pairing_code = input("Enter pairing code: ")
        print(f"Pairing with code {pairing_code}...")
        paired = await api.pair(pairing_code)
        print(paired and "success" or "failed")
        if not paired:
            exit()
        auth_state = api.auth.serialize()
        with open(AUTH_STATE_FILE, "w") as f:
            f.write(str(auth_state))
    print("Connecting...")
    connected = await api.connect()
    print(connected and "success" or "failed")
    if not connected:
        exit()

    async def receive_state(state: PlaybackState):
        print(f"New state: {state}")
        if state.videoId:
            print(
                f"Image should be at: https://img.youtube.com/vi/{state.videoId}/0.jpg"
            )

    async def subscribe_and_keep_alive():
        if not api.connected():
            await api.connect()

        while True:
            while not api.connected():
                print("subscribe_and_keep_alive: reconnecting")
                await asyncio.sleep(CONNECT_RETRY_INTERVAL)
                if not api.linked():
                    await api.refresh_auth()
                await api.connect()
            print("subscribe_and_keep_alive: subscribing")
            await api.subscribe(receive_state)
            await asyncio.sleep(SUBSCRIBE_RETRY_INTERVAL)

    while True:
        try:
            print("Starting subscribe and keep alive")
            await subscribe_and_keep_alive()
        except asyncio.CancelledError:
            break
        except:
            print("Subscribe and keep alive encountered error, waiting seconds: ", ERROR_RETRY_INTERVAL)
            await asyncio.sleep(ERROR_RETRY_INTERVAL)


asyncio.run(go())

In my experience it can take a few days or up to a week before this problem occurs, in this time the screen (and thus youtube app) is turned on/off a few times.

@FabioGNR FabioGNR added the bug Something isn't working label May 1, 2024
@dmunozv04
Copy link
Contributor

I faced this issue and sort of sorted it by calling refresh_auth every 24 hours. Implementation can be seen here:
https://github.com/dmunozv04/iSponsorBlockTV/blob/80196b19aade6041ddf75c46f97ff885715b55fc/src/iSponsorBlockTV/main.py#L35

@bertybuttface
Copy link

bertybuttface commented May 19, 2024

Is this a race condition with command_offset?

  • Ensure safe updates to self._command_offset by using an asyncio.Lock.
  • This prevents race conditions if _command method is called concurrently by multiple async tasks.
  • Without the lock, concurrent updates could lead to inconsistent states.
  • The lock guarantees that only one coroutine can update self._command_offset at a time.

@FabioGNR
Copy link
Owner Author

FabioGNR commented May 20, 2024

Thank you for your comment and helping!

Sadly I believe the reproduction described in this issue should not be related to this; it occurs even when no commands are issued and there's only one active coroutine.

I think the self._command_offset variable is safely updated and read; there's no await in between.
But since the self.session.post call does await when entering the context manager it's possible that the commands are issued out of order. I will experiment with this and see if the lock effectively solves it.

As a general note: with multiple threads this will also cause issues. At this time it's not something I consider as supported by the library though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants