diff --git a/components/spotify/patches/cspot/0002-diag-log-recv-errors.patch b/components/spotify/patches/cspot/0002-diag-log-recv-errors.patch new file mode 100644 index 0000000..8e873a9 --- /dev/null +++ b/components/spotify/patches/cspot/0002-diag-log-recv-errors.patch @@ -0,0 +1,15 @@ +diff --git a/cspot/src/PlainConnection.cpp b/cspot/src/PlainConnection.cpp +index 1bff6cf..b4ee6ff 100644 +--- a/cspot/src/PlainConnection.cpp ++++ b/cspot/src/PlainConnection.cpp +@@ -150,6 +150,10 @@ void PlainConnection::readBlock(const uint8_t* dst, size_t size) { + case EINTR: + break; + default: ++ // diagnostic (project patch): n == 0 means the peer closed (FIN); n < 0 with errno ++ // e.g. ECONNRESET (RST). Note errno is not updated when n == 0. ++ CSPOT_LOG(error, "recv returned %d, errno %d, %u of %u bytes, retry %d", (int)n, ++ getErrno(), idx, (unsigned)size, retries); + if (retries++ > 4) + throw std::runtime_error("Error in read"); + goto READ; diff --git a/components/spotify/patches/cspot/0003-delayed-pong.patch b/components/spotify/patches/cspot/0003-delayed-pong.patch new file mode 100644 index 0000000..45d2c4c --- /dev/null +++ b/components/spotify/patches/cspot/0003-delayed-pong.patch @@ -0,0 +1,74 @@ +Keep-alive like librespot (core/src/session.rs): the server sends a Ping every ~2 min; send +the Pong 60 s later, not at once. With an immediate Pong the server reset the AP connection +(ECONNRESET) on the third Ping, i.e. every 6 minutes, and the Spotify app lost the device. + +--- a/cspot/include/MercurySession.h ++++ b/cspot/include/MercurySession.h +@@ -120,6 +120,14 @@ + + unsigned long long timestampDiff; + unsigned long long lastPingTimestamp = -1; ++ ++ // Keep-alive as librespot does it: Ping -> wait 60 s -> Pong -> PongAck. Answering at once made ++ // the server reset the connection on the third Ping (every 6 minutes). ++ const int PONG_DELAY_MS = 60 * 1000; ++ bool pongPending = false; ++ unsigned long long pongDueTimestamp = 0; ++ std::vector pongData; ++ void sendPendingPong(); + std::string countryCode = ""; + + std::mutex isRunningMutex; +--- a/cspot/src/MercurySession.cpp ++++ b/cspot/src/MercurySession.cpp +@@ -45,7 +45,10 @@ + timeProvider->syncWithPingPacket(packet.data); + + this->lastPingTimestamp = timeProvider->getSyncedTimestamp(); +- this->shanConn->sendPacket(0x49, packet.data); ++ // Pong 60 s later (sent from triggerTimeout(), called on every 3 s receive timeout) ++ this->pongData = packet.data; ++ this->pongDueTimestamp = this->lastPingTimestamp + PONG_DELAY_MS; ++ this->pongPending = true; + } else { + this->packetQueue.push(packet); + } +@@ -80,6 +83,7 @@ + BELL_SLEEP_MS(100); + + lastPingTimestamp = timeProvider->getSyncedTimestamp(); ++ pongPending = false; // a Ping on the old connection needs no Pong on the new one + isReconnecting = false; + + this->executeEstabilishedCallback = true; +@@ -98,9 +102,30 @@ + this->connectionReadyCallback = callback; + } + ++void MercurySession::sendPendingPong() { ++ if (!pongPending || ++ timeProvider->getSyncedTimestamp() < pongDueTimestamp) ++ return; ++ pongPending = false; ++ std::shared_ptr shanConn; ++ { ++ std::scoped_lock lock(connMutex); ++ shanConn = this->shanConn; ++ } ++ if (!shanConn) ++ return; ++ try { ++ shanConn->sendPacket(0x49, pongData); ++ CSPOT_LOG(info, "Sent delayed Pong"); ++ } catch (...) { ++ CSPOT_LOG(error, "Failed to send Pong"); ++ } ++} ++ + bool MercurySession::triggerTimeout() { + if (!isRunning) + return true; ++ sendPendingPong(); + auto currentTimestamp = timeProvider->getSyncedTimestamp(); + + if (currentTimestamp - this->lastPingTimestamp > PING_TIMEOUT_MS) {