@@ -110,6 +110,16 @@ func New(cfg Config) (*Daemon, error) {
110110 if err != nil {
111111 return nil , fmt .Errorf ("load credentials: %w" , err )
112112 }
113+
114+ // If the credential store is empty, try importing from the legacy file
115+ if cs .Len () == 0 && cfg .CredFile != "" {
116+ legacyCreds , lerr := airplay .LoadCredentials (cfg .CredFile )
117+ if lerr == nil && legacyCreds != nil {
118+ cs .Import ("" , legacyCreds )
119+ log .Printf ("[daemon] imported legacy credentials from %s" , cfg .CredFile )
120+ }
121+ }
122+
113123 return & Daemon {
114124 cfg : cfg ,
115125 state : StateIdle ,
@@ -404,43 +414,72 @@ func (d *Daemon) connectAndStream(ctx context.Context, target string, port int,
404414
405415 log .Printf ("[daemon] connected to %s (model: %s, deviceID: %s)" , info .Name , info .Model , deviceID )
406416
407- // Try saved credentials by DeviceID
417+ // If a PIN was provided, skip credential lookup and go straight to PIN pairing.
418+ // The PIN was displayed during the previous connection attempt; trying pair-verify
419+ // or transient pairing first would reset the device's pairing state and invalidate it.
408420 paired := false
409- savedCreds := d .credStore .Lookup (deviceID )
410- if savedCreds != nil {
411- pub , priv := savedCreds .Ed25519Keys ()
412- client .PairingID = savedCreds .PairingID
413- client .PairKeys = & airplay.PairKeys {
414- Ed25519Public : pub ,
415- Ed25519Private : priv ,
421+ if pin != "" {
422+ if err := client .Pair (ctx , pin ); err != nil {
423+ setErr (fmt .Sprintf ("pairing failed: %v" , err ))
424+ return
416425 }
417- if err := client .PairVerify (ctx ); err != nil {
418- log .Printf ("[daemon] pair-verify with saved creds failed: %v" , err )
419- // Reconnect for fresh pairing attempt
420- client .Close ()
421- client = airplay .NewAirPlayClient (target , port )
422- if err := client .Connect (ctx ); err != nil {
423- setErr (fmt .Sprintf ("reconnect failed: %v" , err ))
424- return
426+ paired = true
427+ // Save the new credentials
428+ if client .PairKeys != nil {
429+ if err := d .credStore .Save (deviceID , client .PairingID ,
430+ client .PairKeys .Ed25519Public , client .PairKeys .Ed25519Private ); err != nil {
431+ log .Printf ("[daemon] warning: failed to save credentials: %v" , err )
432+ } else {
433+ log .Printf ("[daemon] credentials saved for %s (deviceID: %s)" , info .Name , deviceID )
434+ }
435+ }
436+ }
437+
438+ // Try saved credentials by DeviceID
439+ if ! paired {
440+ savedCreds := d .credStore .Lookup (deviceID )
441+ if savedCreds != nil {
442+ pub , priv := savedCreds .Ed25519Keys ()
443+ client .PairingID = savedCreds .PairingID
444+ client .PairKeys = & airplay.PairKeys {
445+ Ed25519Public : pub ,
446+ Ed25519Private : priv ,
425447 }
426- if _ , err := client .GetInfo (); err != nil {
427- setErr (fmt .Sprintf ("get info after reconnect failed: %v" , err ))
428- return
448+ if err := client .PairVerify (ctx ); err != nil {
449+ log .Printf ("[daemon] pair-verify with saved creds failed: %v, trying transient pairing" , err )
450+ // Reconnect for fresh pairing attempt
451+ client .Close ()
452+ client = airplay .NewAirPlayClient (target , port )
453+ if err := client .Connect (ctx ); err != nil {
454+ setErr (fmt .Sprintf ("reconnect failed: %v" , err ))
455+ return
456+ }
457+ if _ , err := client .GetInfo (); err != nil {
458+ setErr (fmt .Sprintf ("get info after reconnect failed: %v" , err ))
459+ return
460+ }
461+ // Try transient (no-PIN) pairing as fallback
462+ if err := client .Pair (ctx , "" ); err != nil {
463+ log .Printf ("[daemon] transient pairing also failed: %v" , err )
464+ } else {
465+ paired = true
466+ log .Printf ("[daemon] transient pairing succeeded for %s" , info .Name )
467+ }
468+ } else {
469+ paired = true
470+ log .Printf ("[daemon] pair-verify succeeded for %s" , info .Name )
429471 }
430- } else {
431- paired = true
432- log .Printf ("[daemon] pair-verify succeeded for %s" , info .Name )
433472 }
434473 }
435474
436- // If not paired, we need a PIN
475+ // If still not paired, try transient pairing (no saved creds)
437476 if ! paired {
438- if pin == "" {
439- // Trigger PIN display on the device and ask the user
477+ if err := client .Pair (ctx , "" ); err != nil {
478+ log .Printf ("[daemon] transient pairing failed: %v" , err )
479+ // Last resort: ask for PIN
440480 if err := client .StartPINDisplay (); err != nil {
441481 log .Printf ("[daemon] start PIN display failed: %v" , err )
442482 }
443- // Pause connection — wait for PIN via a subsequent connect command
444483 client .Close ()
445484 d .mu .Lock ()
446485 d .state = StatePINRequired
@@ -451,21 +490,8 @@ func (d *Daemon) connectAndStream(ctx context.Context, target string, port int,
451490 log .Printf ("[daemon] PIN required for %s — waiting for user input" , info .Name )
452491 return
453492 }
454-
455- // PIN was provided — do full pair-setup
456- if err := client .Pair (ctx , pin ); err != nil {
457- setErr (fmt .Sprintf ("pairing failed: %v" , err ))
458- return
459- }
460- // Save the new credentials
461- if client .PairKeys != nil {
462- if err := d .credStore .Save (deviceID , client .PairingID ,
463- client .PairKeys .Ed25519Public , client .PairKeys .Ed25519Private ); err != nil {
464- log .Printf ("[daemon] warning: failed to save credentials: %v" , err )
465- } else {
466- log .Printf ("[daemon] credentials saved for %s (deviceID: %s)" , info .Name , deviceID )
467- }
468- }
493+ paired = true
494+ log .Printf ("[daemon] transient pairing succeeded for %s" , info .Name )
469495 }
470496
471497 // FairPlay setup
0 commit comments