public static void createArtistPlayQueue(
      Connection connection, PlexDirectory artist, final PlexPlayQueueHandler responseHandler) {
    Map qs = new HashMap<>();
    qs.put("type", "audio");
    qs.put("shuffle", "1");
    String uri =
        String.format(
            "library://%s/item/%%2flibrary%%2fmetadata%%2f%s",
            artist.server.machineIdentifier, artist.ratingKey);
    Logger.d("URI: %s", uri);
    qs.put("uri", uri);
    if (artist.server.accessToken != null)
      qs.put(PlexHeaders.XPlexToken, artist.server.accessToken);
    qs.put("continuous", "0");
    qs.put("includeRelated", "1");
    PlexHttpService service =
        getService(String.format("http://%s:%s", connection.address, connection.port));
    Call<MediaContainer> call =
        service.createPlayQueue(qs, VoiceControlForPlexApplication.getUUID());
    call.enqueue(
        new Callback<MediaContainer>() {
          @Override
          public void onResponse(Response<MediaContainer> response) {
            if (responseHandler != null) responseHandler.onSuccess(response.body());
          }

          @Override
          public void onFailure(Throwable t) {
            Logger.d("createPlayQueue failure.");
            t.printStackTrace();
          }
        });
  }
  public static void signin(
      String username,
      String password,
      String authToken,
      final PlexHttpUserHandler responseHandler) {
    PlexHttpService service = getService("https://plex.tv", username, password, false);
    Call<PlexUser> call =
        service.signin("Android", VoiceControlForPlexApplication.getUUID(), authToken);
    call.enqueue(
        new Callback<PlexUser>() {
          @Override
          public void onResponse(Response<PlexUser> response) {
            if (response.code() == 200) responseHandler.onSuccess(response.body());
            else if (response.code() == 401) {
              responseHandler.onFailure(response.code());
            }
          }

          @Override
          public void onFailure(Throwable t) {
            t.printStackTrace();
            responseHandler.onFailure(0);
          }
        });
  }
  public static void subscribe(
      PlexClient client,
      int subscriptionPort,
      int commandId,
      String uuid,
      String deviceName,
      final PlexHttpResponseHandler responseHandler) {
    String url = String.format("http://%s:%s", client.address, client.port);
    Logger.d("Subscribing at url %s", url);
    PlexHttpService service = getService(url, true);

    Call<PlexResponse> call = service.subscribe(uuid, deviceName, subscriptionPort, commandId);
    call.enqueue(
        new Callback<PlexResponse>() {
          @Override
          public void onResponse(Response<PlexResponse> response) {
            Logger.d("Subscribe code: %d", response.code());
            if (responseHandler != null) {
              if (response.code() == 200) responseHandler.onSuccess(response.body());
              else responseHandler.onFailure(new Throwable());
            }
          }

          @Override
          public void onFailure(Throwable t) {
            Logger.d("subscribe onFailure:");
            PlexResponse response = new PlexResponse();
            response.status = "ok";
            if (responseHandler != null) responseHandler.onSuccess(response);
            t.printStackTrace();
            if (responseHandler != null) responseHandler.onFailure(t);
          }
        });
  }
  public static void createPlayQueue(
      Connection connection,
      final PlexMedia media,
      final String key,
      String transientToken,
      final PlexPlayQueueHandler responseHandler) {
    Map qs = new HashMap<>();
    qs.put("type", media.getType());
    qs.put("next", "0");

    boolean hasOffset = media.viewOffset != null && Integer.parseInt(media.viewOffset) > 0;
    if (media.isMovie() && !hasOffset) {
      qs.put(
          "extrasPrefixCount",
          Integer.toString(
              VoiceControlForPlexApplication.getInstance()
                  .prefs
                  .get(Preferences.NUM_CINEMA_TRAILERS, 0)));
    }

    String uri =
        String.format(
            "library://%s/item/%%2flibrary%%2fmetadata%%2f%s", media.server.machineIdentifier, key);
    qs.put("uri", uri);
    qs.put("window", "50"); // no idea what this is for
    if (transientToken != null) qs.put("token", transientToken);
    if (media.server.accessToken != null) qs.put(PlexHeaders.XPlexToken, media.server.accessToken);

    Logger.d("Qs: ", qs);
    PlexHttpService service =
        getService(String.format("http://%s:%s", connection.address, connection.port));
    Call<MediaContainer> call =
        service.createPlayQueue(qs, VoiceControlForPlexApplication.getUUID());
    call.enqueue(
        new Callback<MediaContainer>() {
          @Override
          public void onResponse(Response<MediaContainer> response) {
            if (responseHandler != null) {
              MediaContainer mc = response.body();
              for (int i = 0; i < mc.tracks.size(); i++) {
                mc.tracks.get(i).server = media.server;
              }
              for (int i = 0; i < mc.videos.size(); i++) {
                mc.videos.get(i).server = media.server;
                if (mc.videos.get(i).isClip()) mc.videos.get(i).setClipDuration();
              }
              responseHandler.onSuccess(mc);
            }
          }

          @Override
          public void onFailure(Throwable t) {
            Logger.d("createPlayQueue failure.");
            t.printStackTrace();
          }
        });
  }
  public static void getPinCode(final PlexPinResponseHandler responseHandler) {
    PlexHttpService service = getService("https://plex.tv:443");
    Call<Pin> call = service.getPinCode(VoiceControlForPlexApplication.getUUID());
    call.enqueue(
        new Callback<Pin>() {
          @Override
          public void onResponse(Response<Pin> response) {
            responseHandler.onSuccess(response.body());
          }

          @Override
          public void onFailure(Throwable t) {
            responseHandler.onFailure(t);
          }
        });
  }
  public static void fetchPin(int pinID, final PlexPinResponseHandler responseHandler) {
    String url = "https://plex.tv:443";
    PlexHttpService service = getService(url);
    Call<Pin> call =
        service.fetchPin(pinID, VoiceControlForPlexApplication.getInstance().prefs.getUUID());
    call.enqueue(
        new Callback<Pin>() {
          @Override
          public void onResponse(Response<Pin> response) {
            responseHandler.onSuccess(response.body());
          }

          @Override
          public void onFailure(Throwable t) {
            responseHandler.onFailure(t);
          }
        });
  }
  public static void get(
      String baseHostname, String path, final PlexHttpResponseHandler responseHandler) {
    PlexHttpService service = getService(baseHostname, true);
    Call<PlexResponse> call =
        service.getPlexResponse(
            VoiceControlForPlexApplication.getInstance().prefs.getUUID(),
            path.replaceFirst("^/", ""));
    call.enqueue(
        new Callback<PlexResponse>() {
          @Override
          public void onResponse(Response<PlexResponse> response) {
            if (responseHandler != null) responseHandler.onSuccess(response.body());
          }

          @Override
          public void onFailure(Throwable t) {
            if (responseHandler != null) responseHandler.onFailure(t);
          }
        });
  }
  public static void unsubscribe(
      PlexClient client,
      int commandId,
      String uuid,
      String deviceName,
      final PlexHttpResponseHandler responseHandler) {
    String url = String.format("http://%s:%s", client.address, client.port);
    PlexHttpService service = getService(url, true);
    Call<PlexResponse> call = service.unsubscribe(uuid, deviceName, client.machineIdentifier);
    call.enqueue(
        new Callback<PlexResponse>() {
          @Override
          public void onResponse(Response<PlexResponse> response) {
            responseHandler.onSuccess(response.body());
          }

          @Override
          public void onFailure(Throwable t) {
            responseHandler.onFailure(t);
          }
        });
  }
  public static void getClientTimeline(
      PlexClient client, final int commandId, final PlexHttpMediaContainerHandler responseHandler) {
    String url = String.format("http://%s:%s", client.address, client.port);
    PlexHttpService service = getService(url, true);
    Logger.d(
        "Polling timeline with uuid %s",
        VoiceControlForPlexApplication.getInstance().prefs.getUUID());
    Call<MediaContainer> call =
        service.pollTimeline(
            VoiceControlForPlexApplication.getInstance().prefs.getUUID(), commandId);
    call.enqueue(
        new Callback<MediaContainer>() {
          @Override
          public void onResponse(Response<MediaContainer> response) {
            responseHandler.onSuccess(response.body());
          }

          @Override
          public void onFailure(Throwable t) {
            responseHandler.onFailure(t);
          }
        });
  }