All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xyz.gianlu.librespot.api.handlers.PlayerHandler Maven / Gradle / Ivy

package xyz.gianlu.librespot.api.handlers;

import com.google.gson.JsonObject;
import io.undertow.server.HttpServerExchange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.gianlu.librespot.api.PlayerWrapper;
import xyz.gianlu.librespot.api.Utils;
import xyz.gianlu.librespot.common.ProtobufToJson;
import xyz.gianlu.librespot.core.Session;
import xyz.gianlu.librespot.metadata.EpisodeId;
import xyz.gianlu.librespot.metadata.PlayableId;
import xyz.gianlu.librespot.metadata.TrackId;
import xyz.gianlu.librespot.player.Player;
import xyz.gianlu.librespot.player.TrackOrEpisode;

import java.util.Deque;
import java.util.Map;
import java.util.Objects;

public final class PlayerHandler extends AbsPlayerHandler {

    public PlayerHandler(@NotNull PlayerWrapper wrapper) {
        super(wrapper);
    }

    private static void setVolume(HttpServerExchange exchange, @NotNull Player player, @Nullable String valStr, @Nullable String stepStr) {
        if (valStr != null && stepStr != null) {
            // Reject requests with both parameters
            Utils.invalidParameter(exchange, "step", "Cannot be passed alongside volume");
        } else if (valStr != null) {
            // Absolute volume change
            int val;
            try {
                val = Integer.parseInt(valStr);
            } catch (Exception ex) {
                Utils.invalidParameter(exchange, "volume", "Not an integer");
                return;
            }

            if (val < 0 || val > Player.VOLUME_MAX) {
                Utils.invalidParameter(exchange, "volume", "Must be >= 0 and <= " + Player.VOLUME_MAX);
                return;
            }

            player.setVolume(val);
        } else if (stepStr != null) {
            // Relative volume change in number of steps
            int val;
            try {
                val = Integer.parseInt(stepStr);
            } catch (Exception ex) {
                Utils.invalidParameter(exchange, "step", "Not an integer");
                return;
            }

            if (val > 0) player.volumeUp(val);
            else if (val < 0) player.volumeDown(Math.abs(val));
            else Utils.invalidParameter(exchange, "step", "Must be non zero");
        } else {
            Utils.invalidParameter(exchange, "volume");
        }
    }

    private static void load(HttpServerExchange exchange, @NotNull Player player, @Nullable String uri, boolean play) {
        if (uri == null) {
            Utils.invalidParameter(exchange, "uri");
            return;
        }

        player.load(uri, play);
    }

    private static void current(HttpServerExchange exchange, @NotNull Player player) {
        PlayableId id;
        try {
            id = player.currentPlayable();
        } catch (IllegalStateException ex) {
            id = null;
        }

        JsonObject obj = new JsonObject();
        if (id != null) obj.addProperty("current", id.toSpotifyUri());

        long time = player.time();
        obj.addProperty("trackTime", time);

        TrackOrEpisode metadata = player.currentMetadata();
        if (id instanceof TrackId) {
            if (metadata == null || metadata.track == null) {
                Utils.internalError(exchange, "Missing track metadata. Try again.");
                return;
            }

            obj.add("track", ProtobufToJson.convert(metadata.track));
        } else if (id instanceof EpisodeId) {
            if (metadata == null || metadata.episode == null) {
                Utils.internalError(exchange, "Missing episode metadata. Try again.");
                return;
            }

            obj.add("episode", ProtobufToJson.convert(metadata.episode));
        } else {
            Utils.internalError(exchange, "Invalid PlayableId: " + id);
            return;
        }

        exchange.getResponseSender().send(obj.toString());
    }

    private static void tracks(@NotNull HttpServerExchange exchange, @NotNull Player player, boolean withQueue) {
        Player.Tracks tracks = player.tracks(withQueue);

        JsonObject obj = new JsonObject();
        obj.add("current", tracks.current == null ? null : ProtobufToJson.convert(tracks.current));
        obj.add("next", ProtobufToJson.convertList(tracks.next));
        obj.add("prev", ProtobufToJson.convertList(tracks.previous));
        exchange.getResponseSender().send(obj.toString());
    }

    private static void addToQueue(HttpServerExchange exchange, @NotNull Player player, String uri) {
        if (uri == null) {
            Utils.invalidParameter(exchange, "uri");
            return;
        }

        player.addToQueue(uri);
    }

    private static void removeFromQueue(HttpServerExchange exchange, @NotNull Player player, String uri) {
        if (uri == null) {
            Utils.invalidParameter(exchange, "uri");
            return;
        }

        player.removeFromQueue(uri);
    }

    private static void seek(HttpServerExchange exchange, @NotNull Player player, @Nullable String valStr) {
        if (valStr == null) {
            Utils.invalidParameter(exchange, "pos");
            return;
        }

        int pos;
        try {
            pos = Integer.parseInt(valStr);
        } catch (Exception ex) {
            Utils.invalidParameter(exchange, "pos", "Not an integer");
            return;
        }

        if (pos < 0) {
            Utils.invalidParameter(exchange, "pos", "Cannot be negative");
            return;
        }

        player.seek(pos);
    }

    @Override
    protected void handleRequest(@NotNull HttpServerExchange exchange, @NotNull Session session, @NotNull Player player) throws Exception {
        exchange.startBlocking();
        if (exchange.isInIoThread()) {
            exchange.dispatch(this);
            return;
        }

        Map> params = Utils.readParameters(exchange);
        String cmdStr = Utils.getFirstString(params, "cmd");
        if (cmdStr == null) {
            Utils.invalidParameter(exchange, "cmd");
            return;
        }

        Command cmd = Command.parse(cmdStr);
        if (cmd == null) {
            Utils.invalidParameter(exchange, "cmd");
            return;
        }

        switch (cmd) {
            case CURRENT:
                current(exchange, player);
                return;
            case SET_VOLUME:
                setVolume(exchange, player, Utils.getFirstString(params, "volume"), Utils.getFirstString(params, "step"));
                return;
            case VOLUME_UP:
                player.volumeUp();
                return;
            case VOLUME_DOWN:
                player.volumeDown();
                return;
            case LOAD:
                load(exchange, player, Utils.getFirstString(params, "uri"), Utils.getFirstBoolean(params, "play"));
                return;
            case PLAY_PAUSE:
                player.playPause();
                return;
            case PAUSE:
                player.pause();
                return;
            case RESUME:
                player.play();
                return;
            case PREV:
                player.previous();
                return;
            case NEXT:
                player.next();
                return;
            case SEEK:
                seek(exchange, player, Utils.getFirstString(params, "pos"));
                return;
            case TRACKS:
                tracks(exchange, player, Utils.getFirstBoolean(params, "withQueue"));
                return;
            case ADD_TO_QUEUE:
                addToQueue(exchange, player, Utils.getFirstString(params, "uri"));
                break;
            case REMOVE_FROM_QUEUE:
                removeFromQueue(exchange, player, Utils.getFirstString(params, "uri"));
                break;
            default:
                throw new IllegalArgumentException(cmd.name());
        }
    }

    private enum Command {
        LOAD("load"), PLAY_PAUSE("play-pause"), PAUSE("pause"), RESUME("resume"), TRACKS("tracks"),
        NEXT("next"), PREV("prev"), SET_VOLUME("set-volume"), SEEK("seek"),
        VOLUME_UP("volume-up"), VOLUME_DOWN("volume-down"), CURRENT("current"),
        ADD_TO_QUEUE("addToQueue"), REMOVE_FROM_QUEUE("removeFromQueue");

        private final String name;

        Command(String name) {
            this.name = name;
        }

        @Nullable
        private static Command parse(@NotNull String val) {
            for (Command cmd : values())
                if (Objects.equals(cmd.name, val))
                    return cmd;

            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy