// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 #include "queue_room_internal.h" namespace bridge { void roomLmsCommand(const std::string &host, const std::string &mac, const std::string &command, const std::string &reason, const std::function &log) { const auto volume = command.rfind("mixer volume ", 0) == 0 ? command.substr(13) : std::string{}; const bool validVolume = !volume.empty() && volume.size() <= 3 && volume.find_first_not_of("0123456789") == std::string::npos && std::stoi(volume) <= 100; const auto index = command.rfind("playlist index ", 0) == 0 ? command.substr(15) : std::string{}; const bool validIndex = !index.empty() && index.size() <= 5 && index.find_first_not_of("0123456789") == std::string::npos; const auto time = command.rfind("time ", 0) == 0 ? command.substr(5) : std::string{}; const bool validTime = !time.empty() && time.size() <= 10 && time.find_first_not_of("0123456789") == std::string::npos; const bool allowed = (reason == "speaker-rewind" && validTime) || ((reason == "lazy-pcm-get" || reason == "speaker-nav") && validIndex) || (reason == "speaker-volume" && (validVolume || command == "mixer muting 0" || command == "mixer muting 1")) || (command == "stop" && (reason == "speaker-stop" || reason == "speaker-source-change" || reason == "room-fault" || reason == "group-member" || reason == "track-fault-limit")) || (command == "pause 1" && reason == "speaker-pause") || (command == "pause 0" && reason == "speaker-resume"); if (!allowed) throw std::logic_error("invalid speaker command"); log("LMS mutation reason=" + reason + " command=" + command); if (!queue_audio::controlLms(host, mac, command)) { if (reason == "lazy-pcm-get") throw std::runtime_error("lazy selection unconfirmed"); std::string reply; const bool verified = queue_audio::controlLms(host, mac, "mode ?", 9090, &reply, 3000); log("LMS mutation uncertain command=" + command + " verification=" + (verified ? queue_audio::decodeLms(reply) : "unavailable")); } } void QueueRoom::Impl::speakerCommand(const std::string &command, const std::string &reason) { const auto entry = std::find_if(items.begin(), items.end(), [&](const auto &i) { return i.stream == current; }); const auto source = entry != items.end() && entry->owner ? entry->owner : current; if (command == "pause 1" && pathReason == "remote-finite") finitePause = true; if (command == "pause 0") finitePause = false; if (command == "pause 0" && source == queue.latestOccurrence() && queue.queueItemProduced(source)) { try { resumeEchoUntil = 0; resumeSnapshot = reader.read(host, mac); resumeEchoUntil = milliseconds() + 3000; } catch (const std::exception &e) { logQueue("resume echo snapshot unavailable: " + std::string(e.what())); } } roomLmsCommand(host, mac, command, reason, logQueue); } } // namespace bridge