Ctrl K
ComunidadGuías…

queue_playlist.h

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
#pragma once
#include "lms_artwork.h"
#include "queue_lms_cli.h"
#include <map>
#include <cmath>
#include <sstream>
#include <stdexcept>
namespace queue_audio {
struct PlaylistEntry {
    std::string id, title, artist, album, url;
    std::string artworkUrl, remote;
    double duration = 0, liveEdge = -1;
    unsigned startOffsetMs = 0;
    std::string plainTitle() const {
        if (url.compare(0, 10, "podcast://")) return title;
        // Native Podcast scanUrl can store getCurrentTitle's player formatting
        // back into TITLE. Remove only the exact suffix made from these tags.
        const auto suffix = (!artist.empty() ? " by " + artist : std::string{}) +
                            (!album.empty() ? " from " + album : std::string{});
        if (!suffix.empty() && title.size() >= suffix.size() &&
            title.compare(title.size() - suffix.size(), suffix.size(), suffix) == 0)
            return title.substr(0, title.size() - suffix.size());
        return title;
    }
    bool finiteRemote() const { return std::isfinite(duration) && duration > 0 && liveEdge < 0; }
    std::string artwork(const std::string &host) const {
        return bridge::lmsArtwork(host, id, artworkUrl, bridge::remoteArtworkTrack(id, remote, url));
    }
};
struct PlaylistSnapshot {
    unsigned index = 0, timeMs = 0;
    std::string mode;
    double duration = 0;
    std::vector<PlaylistEntry> entries;
    std::string revision;
    unsigned shuffle = 0, repeat = 0;
};
// Read-only metadata snapshot. LMS alone determines stream order; the relay
// has no occurrence matching, playlist edit inference or mirroring policy.
class PlaylistReader {
public:
    PlaylistSnapshot parse(const std::string &wire) {
        PlaylistSnapshot out;
        std::istringstream input(wire);
        std::string field;
        PlaylistEntry *entry = nullptr;
        unsigned count = 0;
        while (input >> field) {
            field = decodeLms(field);
            auto colon = field.find(':');
            if (colon == std::string::npos)
                continue;
            auto key = field.substr(0, colon), value = field.substr(colon + 1);
            if (key == "mode" && !entry)
                out.mode = value;
            else if (key == "playlist_cur_index")
                out.index = value == "-1" ? 0 : std::stoul(value);
            else if (key == "playlist_tracks")
                count = std::stoul(value);
            else if (key == "playlist_timestamp")
                out.revision = value;
            else if (key == "playlist shuffle" || key == "playlist repeat") {
                const auto policy = std::stoul(value);
                if (policy > 2) throw std::runtime_error("playlist-policy-invalid");
                (key == "playlist shuffle" ? out.shuffle : out.repeat) = unsigned(policy);
            }
            else if (key == "duration" && !entry)
                out.duration = std::stod(value);
            else if (key == "time")
                out.timeMs = unsigned(std::stod(value) * 1000);
            else if (key == "playlist index" || key == "index") {
                if (std::stoul(value) != out.entries.size())
                    throw std::runtime_error("playlist-incomplete");
                out.entries.emplace_back();
                entry = &out.entries.back();
            } else if (entry) {
                if (key == "id")
                    entry->id = value;
                else if (key == "title")
                    entry->title = value;
                else if (key == "artist")
                    entry->artist = value;
                else if (key == "album")
                    entry->album = value;
                else if (key == "url")
                    entry->url = value;
                else if (key == "artwork_url")
                    entry->artworkUrl = value;
                else if (key == "duration")
                    entry->duration = std::stod(value);
                else if (key == "live_edge")
                    entry->liveEdge = std::stod(value);
                else if (key == "remote")
                    entry->remote = value;
            }
        }
        if (count > 60000 || out.entries.size() != count || (!count && out.index) ||
            (count && out.index >= count))
            throw std::runtime_error("playlist-incomplete");
        return out;
    }
    PlaylistSnapshot read(const std::string &host, const std::string &mac, unsigned port = 9090) {
        std::string reply;
        if (!controlLms(host, mac, "status 0 60000 tags:alKudV", port, &reply))
            throw std::runtime_error("playlist-cli-unavailable");
        return parse(reply);
    }
};
} // namespace queue_audio