At the moment, it waits for the timeout because the code is structured so that it retrieves all the devices within the timeout period first, and then filters them according to the identifier, etc.
This improvement would facilitate a faster connect when you know the ID of the device as you'd only need to wait for the device to report its IP address and port before proceeding to the connect.
My workaround is to initially call scan() with a low timeout (0.125s) and then keep doubling the timeout if I don't get back the device. In almost all cases I get a response within 0.125s.
Ah, yes, I can explain the reason for this. In the early ages of pyatv, there was a flag (called abort_on_found or something similar) which did something like that. For release 0.4.0 I decided to remove it to simplify the API (and implementation) but also because it didn't really work.
When using the zeroconf library, it's only possible to subscribe to individual services. So I had subscribe to a bunch of services individually, like DMAP and AirPlay, and wait for the results to come in asynchronously. I couldn't really know if I had received all of them or not, and abort in the right time. That was a problem and the timeout was my lifeline.
So, at some point I added support for unicast scanning by implementing it myself from scratch. When doing so, I made sure to support asking for multiple services at once in the same request (instead of one per request). This way I would get a response containing all services at once, which is quite convenient.
Moving on to implementing multicast support, I used the same approach as I did with unicast scanning. Simply asking for all services of interest at once. This way I no longer have the problem of having to wait for lots of independent service responses. Much better.
This leaves us at where we are today and the only reason why there's no early exit is because I haven't added that behavior. As you say, the code is structured in such way that scanning is done first and filtering after that since I want to keep responsibilities separate. Shouldn't be that hard to fix, I can see what I can do when I have some spare time.
Thank you for the explanation, it all makes sense. Very nice library, by the way.
No problems and thanks! 馃槉
A simple approach I came up with would be to accept a filter function to pyatv.mdns.multicast, which would be called every time a service is discovered. If it returns None the scan continues. If it returns a list of services, the scan is aborted and that list is returned. I would imagine the passed filter to receive both the discovered service and all services discovered so far. Should be easy enough to integrate with the rest of the scanning code. Sounds good?
Just so I understand - what would be the list of services returned by the filter? Would it not just be return a flag indicating whether to continue or not?
Edit: the suggestion seems fine I guess it gives me the hook I need. I would just check for the returned service having the identifier I鈥檓 looking for and just return that service.
I should have been more clear about this being more internal implementation 馃槉 The intention is that if you pass an identifier to pyatv.scan, it will use the mentioned filter to abort when the device is found and return it. So you won't have to do anything else. Sorry about the confusion!
Got it, sounds perfect.
I'm far from finished, but I have a working prototype now. Here is with master:
$ time atvremote --id <id> playing
...
real 0m3.600s
user 0m0.572s
sys 0m0.032s
With my changes:
$ time atvremote --id <id> playing
...
real 0m0.596s
user 0m0.524s
sys 0m0.036s
Not very scientific, but it's something.
Looks promising, happy to test any changes (I couldn't see any commits in master relating to it though?)
I haven't pushed anything yet, it was just a teaser 馃槈 Lets see if I can finish it tonight. Code is done, just need to finish tests and update some documentation.
@oviano Not done with tests yet, too tired for that right now. But I pushed the code so far, it's in PR #811. It should work as expected hopefully. Feel free to give it a test spin!
Wonderful, I will test this out tomorrow and let you know how it goes.
Works perfectly, thank you.
@oviano Merged to master now!