Skip to content

Commit b350509

Browse files
committed
Fix queue activation: use Seek instead of SetAVTransportURI to prevent UPnP errors
- Replace SetAVTransportURI (error 714) with Seek to track 1 - Add fallback to next() if Seek fails - Improve queue verification with retries - Add better logging for queue operations
1 parent 00a30e3 commit b350509

6 files changed

Lines changed: 89 additions & 4 deletions

File tree

app.manifest.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,5 @@
5858

5959

6060

61+
62+

docs/DISCORD_DISCOVERY_CONTENT.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,5 @@ Join our Discord server for:
111111

112112

113113

114+
115+

index.js

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4463,9 +4463,18 @@ async function _add(input, channel, userName) {
44634463
if (state === 'stopped') {
44644464
logger.info('Player stopped - ensuring queue is active source and flushing');
44654465
try {
4466-
// Parallel stop and flush (flush is safe even if not stopped)
4466+
// Stop any active playback to force Sonos to use queue
4467+
try {
4468+
await sonos.stop();
4469+
await new Promise(resolve => setTimeout(resolve, 300));
4470+
} catch (stopErr) {
4471+
// Ignore stop errors (might already be stopped)
4472+
logger.debug('Stop command result (may already be stopped): ' + stopErr.message);
4473+
}
4474+
4475+
// Flush queue to start fresh
44674476
await sonos.flush();
4468-
await new Promise(resolve => setTimeout(resolve, 200)); // Reduced from 300ms
4477+
await new Promise(resolve => setTimeout(resolve, 300));
44694478
logger.info('Queue flushed and ready');
44704479
} catch (flushErr) {
44714480
logger.warn('Could not flush queue: ' + flushErr.message);
@@ -4493,8 +4502,9 @@ async function _add(input, channel, userName) {
44934502
// Try to queue the first valid candidate (most relevant result)
44944503
let result = null;
44954504
try {
4505+
logger.info(`Attempting to queue: ${firstCandidate.name} by ${firstCandidate.artist} (URI: ${firstCandidate.uri})`);
44964506
await sonos.queue(firstCandidate.uri);
4497-
logger.info('Added track: ' + firstCandidate.name);
4507+
logger.info('Successfully queued track: ' + firstCandidate.name);
44984508
result = firstCandidate;
44994509
} catch (e) {
45004510
const errorDetails = e.message || String(e);
@@ -4540,7 +4550,72 @@ async function _add(input, channel, userName) {
45404550
// Start playback in background (don't await)
45414551
(async () => {
45424552
try {
4543-
await new Promise(resolve => setTimeout(resolve, 300)); // Brief delay for queue to settle
4553+
// Ensure queue is the active source before starting playback
4554+
// Stop any active playback to force Sonos to use queue
4555+
try {
4556+
await sonos.stop();
4557+
await new Promise(resolve => setTimeout(resolve, 500));
4558+
} catch (stopErr) {
4559+
// Ignore stop errors (might already be stopped)
4560+
logger.debug('Stop before play (may already be stopped): ' + stopErr.message);
4561+
}
4562+
4563+
// Verify queue has items before trying to play (prevents UPnP error 701)
4564+
let queueReady = false;
4565+
let retries = 0;
4566+
while (!queueReady && retries < 5) {
4567+
try {
4568+
const queue = await sonos.getQueue();
4569+
if (queue && queue.items && queue.items.length > 0) {
4570+
queueReady = true;
4571+
logger.debug(`Queue verified: ${queue.items.length} items ready`);
4572+
} else {
4573+
logger.debug(`Queue not ready yet (attempt ${retries + 1}/5), waiting...`);
4574+
await new Promise(resolve => setTimeout(resolve, 300));
4575+
retries++;
4576+
}
4577+
} catch (queueErr) {
4578+
logger.debug(`Queue check failed (attempt ${retries + 1}/5): ${queueErr.message}`);
4579+
await new Promise(resolve => setTimeout(resolve, 300));
4580+
retries++;
4581+
}
4582+
}
4583+
4584+
if (!queueReady) {
4585+
logger.warn('Queue not ready after 5 attempts, attempting playback anyway');
4586+
}
4587+
4588+
// Try to activate queue by seeking to position 1 (alternative to SetAVTransportURI)
4589+
// This should activate the queue as the transport source
4590+
try {
4591+
logger.debug('Attempting to seek to queue position 1 to activate queue');
4592+
// Seek to track 1 in the queue to activate it
4593+
await sonos.avTransportService().Seek({
4594+
InstanceID: 0,
4595+
Unit: 'TRACK_NR',
4596+
Target: '1'
4597+
});
4598+
logger.debug('Successfully sought to track 1, queue should be active');
4599+
// Wait for seek to complete
4600+
await new Promise(resolve => setTimeout(resolve, 500));
4601+
} catch (seekErr) {
4602+
// If seek fails, try alternative: use next() to jump to first track
4603+
logger.debug('Seek failed, trying next() to activate queue: ' + seekErr.message);
4604+
try {
4605+
// Jump to first track in queue (this should activate the queue)
4606+
await sonos.next();
4607+
logger.debug('Used next() to activate queue');
4608+
await new Promise(resolve => setTimeout(resolve, 300));
4609+
} catch (nextErr) {
4610+
logger.debug('next() also failed: ' + nextErr.message);
4611+
// Continue anyway - play() might still work
4612+
}
4613+
}
4614+
4615+
// Wait a moment to ensure queue is ready
4616+
await new Promise(resolve => setTimeout(resolve, 500));
4617+
4618+
// Start playback from queue
45444619
await sonos.play();
45454620
logger.info('Started playback from queue');
45464621
} catch (playErr) {

lib/slack-validator.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,5 @@ module.exports = {
101101

102102

103103

104+
105+

lib/sonos-discovery.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,5 @@ module.exports = {
149149

150150

151151

152+
153+

lib/spotify-validator.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ module.exports = {
6565

6666

6767

68+
69+

0 commit comments

Comments
 (0)