postlund / pyatv

A client library for Apple TV and AirPlay devices
https://pyatv.dev
MIT License
827 stars 87 forks source link

Playing Radio Station URLs and authentication #2374

Closed peterfealey closed 2 months ago

peterfealey commented 2 months ago

What do you need help with?

v0.10.3

I'm trying to build a Flask app that'll discover my Airplay compatible devices, and on clicking 'play' will send a local radio stream (hosted on the same device as the Flask server) and am coming across an authentication error.

pyatv.exceptions.AuthenticationError: not authenticated

That's the gist of what I'm trying to do:

@app.route('/atv_play', methods=['POST'])
def play_airplay():
    data = request.get_json()
    airplay_ip = data.get('ip')
    device_ip = get_ip()
    print('AirPlay started ')
    print(f'airplay device: { airplay_ip }')
    print(f'local device: { device_ip }')

    try:
        result = asyncio.run(play_atv_async(airplay_ip, device_ip))
        return jsonify(result)
    except Exception as e:
        print(f'Error: { e }')
        return jsonify(e)

async def play_atv_async(airplay_ip, device_ip):

    loop = asyncio.get_event_loop()
    atvs = await pyatv.scan(loop=loop, hosts=[airplay_ip])
    atv = await pyatv.connect(atvs[0], loop=loop)

    # Start playing the audio stream
    print(f'Found player: { atv }')
    await atv.stream.stream_file("http://" + device_ip +":8001/listen")

    print('Starting playback on AirPlay listener ')
    return "Playing audio on Apple TV device"

I'm pretty sure that stream_file didn't require authentication but might require transient pairing ( #1259 ). I only have Airplay 2 devices on my network if that helps narrow things down, some Sonos, some Symphonisk, some Denon and a couple of Macs.

Would be really grateful for some guidance, I'm sure I'm missing something obvious!

postlund commented 2 months ago

Can you enable some additional logging and provide logs from pyatv? Transient authentication should work automatically. Also, what does a scan say?

peterfealey commented 2 months ago

Here's a scan:

1970-01-01 00:01:24 DEBUG [asyncio]: Using selector: EpollSelector
1970-01-01 00:01:24 DEBUG [pyatv.support.net]: Binding on *:5353
1970-01-01 00:01:24 DEBUG [pyatv.support.net]: Binding on 127.0.0.1:0
1970-01-01 00:01:24 DEBUG [pyatv.support.net]: Binding on 192.168.1.123:0
1970-01-01 00:01:24 DEBUG [pyatv.support.net]: Binding on 192.168.1.128:0
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered Living Room at 192.168.1.120:7000 via Protocol.AirPlay ({'acl': '0', 'deviceid': '38:42:0B:F8:0C:A0', 'features': '0x445F8A00,0x1C340', 'rsf': '0x0', 'fv': 'p20.77.4-49290', 'flags': '0x4', 'model': 'Beam', 'manufacturer': 'Sonos', 'serialnumber': '38-42-0B-F8-0C-A0:6', 'protovers': '1.1', 'srcvers': '366.0', 'pi': '38:42:0B:F8:0C:A0', 'gid': '38:42:0B:F8:0C:A0', 'gcgl': '0', 'pk': '1b7837ca6c184b8c4cc97537a1820a25925dde67e253833c22b3347159d5c108'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered 38420BF80CA0@Living Room at 192.168.1.120:7000 via Protocol.RAOP ({'cn': '0,1', 'da': 'true', 'et': '0,4', 'ft': '0x445F8A00,0x1C340', 'fv': 'p20.77.4-49290', 'md': '0,1,2', 'am': 'Beam', 'sf': '0x4', 'tp': 'UDP', 'vn': '65537', 'vs': '366.0', 'pk': '1b7837ca6c184b8c4cc97537a1820a25925dde67e253833c22b3347159d5c108'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered Kitchen at 192.168.1.119:7000 via Protocol.AirPlay ({'acl': '0', 'deviceid': '78:28:CA:EE:BC:EE', 'features': '0x445F8A00,0x1C340', 'rsf': '0x0', 'fv': 'p20.77.4-49290', 'flags': '0x4', 'model': 'One', 'manufacturer': 'Sonos', 'serialnumber': '78-28-CA-EE-BC-EE:G', 'protovers': '1.1', 'srcvers': '366.0', 'pi': '78:28:CA:EE:BC:EE', 'gid': '78:28:CA:EE:BC:EE', 'gcgl': '0', 'pk': 'd45e45fdbc9dba14c12d2b297cd50c2f4aada33d079b5d51cfe638bc2102693c'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered 7828CAEEBCEE@Kitchen at 192.168.1.119:7000 via Protocol.RAOP ({'cn': '0,1', 'da': 'true', 'et': '0,4', 'ft': '0x445F8A00,0x1C340', 'fv': 'p20.77.4-49290', 'md': '0,1,2', 'am': 'One', 'sf': '0x4', 'tp': 'UDP', 'vn': '65537', 'vs': '366.0', 'pk': 'd45e45fdbc9dba14c12d2b297cd50c2f4aada33d079b5d51cfe638bc2102693c'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered Office at 192.168.1.121:7000 via Protocol.AirPlay ({'acl': '0', 'deviceid': '38:42:0B:14:9F:E2', 'features': '0x445F8A00,0x1C340', 'rsf': '0x0', 'fv': 'p20.77.4-49290', 'flags': '0x4', 'model': 'One SL', 'manufacturer': 'Sonos', 'serialnumber': '38-42-0B-14-9F-E2:8', 'protovers': '1.1', 'srcvers': '366.0', 'pi': '38:42:0B:14:9F:E2', 'gid': '38:42:0B:14:9F:E2', 'gcgl': '0', 'pk': '5f0d7cf4cbe493ec3e79d75fca260e7cc0cee6149c4edac2062310c9bfdaeb01'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered 38420B149FE2@Office at 192.168.1.121:7000 via Protocol.RAOP ({'cn': '0,1', 'da': 'true', 'et': '0,4', 'ft': '0x445F8A00,0x1C340', 'fv': 'p20.77.4-49290', 'md': '0,1,2', 'am': 'One SL', 'sf': '0x4', 'tp': 'UDP', 'vn': '65537', 'vs': '366.0', 'pk': '5f0d7cf4cbe493ec3e79d75fca260e7cc0cee6149c4edac2062310c9bfdaeb01'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered Pete F's MacBook Pro (2) at 192.168.1.169:7000 via Protocol.AirPlay ({'act': '2', 'acl': '0', 'deviceid': 'F0:2F:4B:11:FB:0D', 'fex': '1c9/St5PF7g2IQQ', 'features': '0x4A7FCFD5,0xB8174FDE', 'rsf': '0x8', 'flags': '0x204', 'gid': '5A0076BF-1FE4-401C-BA10-1D7D67C51062', 'igl': '0', 'gcgl': '0', 'model': 'MacBookPro18,2', 'at': '4', 'protovers': '1.1', 'pi': '491632eb-8940-4f3f-b09c-bedceef03b05', 'psi': 'DA3373A3-4159-4E6A-94AC-2F60B417D4EF', 'pk': '856a812ad427983d9c897380600b8bd50956b970ad11e6e7ed13f220306547e1', 'srcvers': '710.79.1'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered Pete F's MacBook Pro (2) at 192.168.1.169:49697 via Protocol.Companion ({'rpmac': '0', 'rphn': '5752e1e49697', 'rpfl': '0x20000', 'rpha': 'e34b0d3587ff', 'rpvr': '500.59.4', 'rpad': '6d831f7286d0', 'rphi': '3fe0d8700059', 'rpba': '45:8F:E1:2E:3C:85'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered F02F4B11FB0D@Pete F's MacBook Pro (2) at 192.168.1.169:7000 via Protocol.RAOP ({'cn': '0,1,2,3', 'da': 'true', 'et': '0,3,5', 'ft': '0x4A7FCFD5,0xB8174FDE', 'sf': '0x204', 'md': '0,1,2', 'am': 'MacBookPro18,2', 'pk': '856a812ad427983d9c897380600b8bd50956b970ad11e6e7ed13f220306547e1', 'tp': 'UDP', 'vn': '65537', 'vs': '710.79.1', 'vv': '0'})
1970-01-01 00:01:29 DEBUG [pyatv.core.scan]: Auto-discovered Pete F's MacBook Pro (2) at 192.168.1.118:7000 via Protocol.AirPlay ({'act': '2', 'acl': '0', 'deviceid': 'F0:2F:4B:11:FB:0D', 'fex': '1c9/St5PF7g2IQQ', 'features': '0x4A7FCFD5,0xB8174FDE', 'rsf': '0x8', 'flags': '0x204', 'gid': '5A0076BF-1FE4-401C-BA10-1D7D67C51062', 'igl': '0', 'gcgl': '0', 'model': 'MacBookPro18,2', 'at': '4', 'protovers': '1.1', 'pi': '491632eb-8940-4f3f-b09c-bedceef03b05', 'psi': 'DA3373A3-4159-4E6A-94AC-2F60B417D4EF', 'pk': '856a812ad427983d9c897380600b8bd50956b970ad11e6e7ed13f220306547e1', 'srcvers': '710.79.1'})
1970-01-01 00:01:30 DEBUG [pyatv.core.scan]: Auto-discovered Pete F's MacBook Pro (2) at 192.168.1.118:49697 via Protocol.Companion ({'rpmac': '0', 'rphn': '5752e1e49697', 'rpfl': '0x20000', 'rpha': 'e34b0d3587ff', 'rpvr': '500.59.4', 'rpad': '6d831f7286d0', 'rphi': '3fe0d8700059', 'rpba': '45:8F:E1:2E:3C:85'})
1970-01-01 00:01:30 DEBUG [pyatv.core.scan]: Auto-discovered F02F4B11FB0D@Pete F's MacBook Pro (2) at 192.168.1.118:7000 via Protocol.RAOP ({'cn': '0,1,2,3', 'da': 'true', 'et': '0,3,5', 'ft': '0x4A7FCFD5,0xB8174FDE', 'sf': '0x204', 'md': '0,1,2', 'am': 'MacBookPro18,2', 'pk': '856a812ad427983d9c897380600b8bd50956b970ad11e6e7ed13f220306547e1', 'tp': 'UDP', 'vn': '65537', 'vs': '710.79.1', 'vv': '0'})

And if my play function above is called. Note I've changed the url to include a .mp3 extension in case some weirdness was happening there

AirPlay started 
airplay device: 192.168.1.121
local device: 192.168.1.128
1970-01-01 00:04:16 DEBUG [asyncio]: Using selector: EpollSelector
async airplay device: 192.168.1.121
async local device: 192.168.1.128
1970-01-01 00:04:16 DEBUG [pyatv.support.knock]: Knocking at port 3689 on 192.168.1.121
1970-01-01 00:04:16 DEBUG [pyatv.support.knock]: Knocking at port 7000 on 192.168.1.121
1970-01-01 00:04:16 DEBUG [pyatv.support.knock]: Knocking at port 49152 on 192.168.1.121
1970-01-01 00:04:16 DEBUG [pyatv.support.knock]: Knocking at port 32498 on 192.168.1.121
1970-01-01 00:04:17 DEBUG [pyatv.core.scan]: Auto-discovered Office at 192.168.1.121:7000 via Protocol.AirPlay ({'acl': '0', 'deviceid': '38:42:0B:14:9F:E2', 'features': '0x445F8A00,0x1C340', 'rsf': '0x0', 'fv': 'p20.77.4-49290', 'flags': '0x4', 'model': 'One SL', 'manufacturer': 'Sonos', 'serialnumber': '38-42-0B-14-9F-E2:8', 'protovers': '1.1', 'srcvers': '366.0', 'pi': '38:42:0B:14:9F:E2', 'gid': '38:42:0B:14:9F:E2', 'gcgl': '0', 'pk': '5f0d7cf4cbe493ec3e79d75fca260e7cc0cee6149c4edac2062310c9bfdaeb01'})
1970-01-01 00:04:17 DEBUG [pyatv.core.scan]: Auto-discovered 38420B149FE2@Office at 192.168.1.121:7000 via Protocol.RAOP ({'cn': '0,1', 'da': 'true', 'et': '0,4', 'ft': '0x445F8A00,0x1C340', 'fv': 'p20.77.4-49290', 'md': '0,1,2', 'am': 'One SL', 'sf': '0x4', 'tp': 'UDP', 'vn': '65537', 'vs': '366.0', 'pk': '5f0d7cf4cbe493ec3e79d75fca260e7cc0cee6149c4edac2062310c9bfdaeb01'})
ATVs: [<pyatv.conf.AppleTV object at 0xffffb014b9a0>]
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Adding handler for protocol Protocol.AirPlay
1970-01-01 00:04:17 DEBUG [pyatv.protocols.airplay]: Remote control not supported by device
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Adding handler for protocol Protocol.RAOP
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Connecting to protocol: Protocol.AirPlay
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Connected to protocol: Protocol.AirPlay
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Connecting to protocol: Protocol.RAOP
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Connected to protocol: Protocol.RAOP
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Power management not supported by any protocols
Found player: <pyatv.core.facade.FacadeAppleTV object at 0xffffb014aec0>
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Takeover (<class 'pyatv.interface.Audio'>, <class 'pyatv.interface.Metadata'>, <class 'pyatv.interface.PushUpdater'>, <class 'pyatv.interface.RemoteControl'>) by Protocol.RAOP
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Connected to 192.168.1.121
1970-01-01 00:04:17 DEBUG [pyatv.protocols.raop.raop]: Initializing RTSP with encryption=EncryptionType.MFiSAP|Unencrypted, metadata=MetadataType.Progress|Artwork|Text
1970-01-01 00:04:17 DEBUG [pyatv.protocols.raop.raop]: Update play settings to 44100/2/16bit
1970-01-01 00:04:17 DEBUG [pyatv.protocols.raop.raop]: Local ports: control=57964, timing=46675
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Sending HTTP/1.1 message: b'GET /info HTTP/1.1\r\nUser-Agent: AirPlay/540.31\r\nCSeq: 0\r\nDACP-ID: 4B5A2F2B53CD9A38\r\nActive-Remote: 657448825\r\nClient-Instance: 4B5A2F2B53CD9A38\r\n\r\n'
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Received: b'HTTP/1.1 200 OK\r\nContent-Length: 526\r\nContent-Type: application/x-apple-binary-plist\r\nServer: AirTunes/366.0\r\nCSeq: 0\r\n\r\nbplist00\xdf\x10\x13\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x19\x1e\x15\x1f\x14 !"#SsdkRpi_\x10\x10firmwareRevisionVOSInfo\\manufacturer_\x10\x11keepAliveLowPower_\x10\x11firmwareBuildDateUmodel_\x10\x14nameIsFactoryDefault_\x10\x10hardwareRevision_\x10\x18keepAliveSendStatsAsBody[statusFlagsXdeviceIDUbuildWPTPInfo_\x10\x0fprotocolVersion]sourceVersionXfeaturesTname]AirPlay;2.0.8_\x10\x1138:42:0B:14:9F:E2Z77.4-49290\\Linux 4.9.99USonos\t[Nov 10 2023VOne SL\x08\\1.28.2.6-2.1\x10\x04T49.0S1.1U366.0\x13\x00\x01\xc3@D_\x8a\x00VOffice\x00\x08\x001\x005\x008\x00K\x00R\x00_\x00s\x00\x87\x00\x8d\x00\xa4\x00\xb7\x00\xd2\x00\xde\x00\xe7\x00\xed\x00\xf5\x01\x07\x01\x15\x01\x1e\x01#\x011\x01E\x01P\x01]\x01c\x01d\x01p\x01w\x01x\x01\x85\x01\x87\x01\x8c\x01\x90\x01\x96\x01\x9f\x00\x00\x00\x00\x00\x00\x02\x01\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xa6'
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Got HTTP response: HttpResponse(protocol='HTTP', version='1.1', code=200, message='OK', headers={'content-length': '526', 'content-type': 'application/x-apple-binary-plist', 'server': 'AirTunes/366.0', 'cseq': '0'}, body=b'bplist00\xdf\x10\x13\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x19\x1e\x15\x1f\x14 !"#SsdkRpi_\x10\x10firmwareRevisionVOSInfo\\manufacturer_\x10\x11keepAliveLowPower_\x10\x11firmwareBuildDateUmodel_\x10\x14nameIsFactoryDefault_\x10\x10hardwareRevision_\x10\x18keepAliveSendStatsAsBody[statusFlagsXdeviceIDUbuildWPTPInfo_\x10\x0fprotocolVersion]sourceVersionXfeaturesTname]AirPlay;2.0.8_\x10\x1138:42:0B:14:9F:E2Z77.4-49290\\Linux 4.9.99USonos\t[Nov 10 2023VOne SL\x08\\1.28.2.6-2.1\x10\x04T49.0S1.1U366.0\x13\x00\x01\xc3@D_\x8a\x00VOffice\x00\x08\x001\x005\x008\x00K\x00R\x00_\x00s\x00\x87\x00\x8d\x00\xa4\x00\xb7\x00\xd2\x00\xde\x00\xe7\x00\xed\x00\xf5\x01\x07\x01\x15\x01\x1e\x01#\x011\x01E\x01P\x01]\x01c\x01d\x01p\x01w\x01x\x01\x85\x01\x87\x01\x8c\x01\x90\x01\x96\x01\x9f\x00\x00\x00\x00\x00\x00\x02\x01\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xa6'):
1970-01-01 00:04:17 DEBUG [pyatv.protocols.raop.raop]: Updated info parameters to: {'sdk': 'AirPlay;2.0.8', 'pi': '38:42:0B:14:9F:E2', 'firmwareRevision': '77.4-49290', 'OSInfo': 'Linux 4.9.99', 'manufacturer': 'Sonos', 'keepAliveLowPower': True, 'firmwareBuildDate': 'Nov 10 2023', 'model': 'One SL', 'nameIsFactoryDefault': False, 'hardwareRevision': '1.28.2.6-2.1', 'keepAliveSendStatsAsBody': True, 'statusFlags': 4, 'deviceID': '38:42:0B:14:9F:E2', 'build': '49.0', 'PTPInfo': 'AirPlay;2.0.8', 'protocolVersion': '1.1', 'sourceVersion': '366.0', 'features': 496155769145856, 'name': 'Office'}
1970-01-01 00:04:17 DEBUG [pyatv.protocols.airplay.auth]: Setting up new AirPlay Pair-Verify procedure with type AuthenticationType.Null
1970-01-01 00:04:17 DEBUG [pyatv.protocols.airplay.auth]: Performing null Pair-Verify
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Sending RTSP/1.0 message: b'ANNOUNCE rtsp://192.168.1.123/3388833804 RTSP/1.0\r\nUser-Agent: AirPlay/540.31\r\nContent-Type: application/sdp\r\nContent-Length: 184\r\nCSeq: 1\r\nDACP-ID: 4B5A2F2B53CD9A38\r\nActive-Remote: 657448825\r\nClient-Instance: 4B5A2F2B53CD9A38\r\n\r\nv=0\r\no=iTunes 3388833804 0 IN IP4 192.168.1.123\r\ns=iTunes\r\nc=IN IP4 192.168.1.121\r\nt=0 0\r\nm=audio 0 RTP/AVP 96\r\na=rtpmap:96 AppleLossless\r\na=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100\r\n'
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Received: b'RTSP/1.0 200 OK\r\nServer: AirTunes/366.0\r\nCSeq: 1\r\n\r\n'
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Got RTSP response: HttpResponse(protocol='RTSP', version='1.0', code=200, message='OK', headers={'server': 'AirTunes/366.0', 'cseq': '1'}, body=''):
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Sending RTSP/1.0 message: b'SETUP rtsp://192.168.1.123/3388833804 RTSP/1.0\r\nUser-Agent: AirPlay/540.31\r\nCSeq: 2\r\nDACP-ID: 4B5A2F2B53CD9A38\r\nActive-Remote: 657448825\r\nClient-Instance: 4B5A2F2B53CD9A38\r\nTransport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=57964;timing_port=46675\r\n\r\n'
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Received: b'RTSP/1.0 403 Forbidden\r\nContent-Length: 0\r\nServer: AirTunes/366.0\r\nCSeq: 2\r\n\r\n'
1970-01-01 00:04:17 DEBUG [pyatv.support.http]: Got RTSP response: HttpResponse(protocol='RTSP', version='1.0', code=403, message='Forbidden', headers={'content-length': '0', 'server': 'AirTunes/366.0', 'cseq': '2'}, body=''):
1970-01-01 00:04:17 DEBUG [pyatv.core.facade]: Release (<class 'pyatv.interface.Audio'>, <class 'pyatv.interface.Metadata'>, <class 'pyatv.interface.PushUpdater'>, <class 'pyatv.interface.RemoteControl'>) by Protocol.RAOP
1970-01-01 00:04:17 DEBUG [pyatv.protocols.raop.raop]: Control connection lost (None)
Error in play_airplay: not authenticated
peterfealey commented 2 months ago

@postlund I realised I was on an old version, despite me pasting it in the issue - my bad.

It works now, but I'm getting ~30 secs of audio before the process crashes out - whilst playing I'm getting these types of error

DEBUG [pyatv.protocols.raop.stream_client]: Too slow to keep up for seqno 17825 (21.614875 vs 21.615497 => -0.000622)

Progress at least

peterfealey commented 2 months ago

The version bump 'fixed' my inability to check features v versions