com.sedmelluq.discord.lavaplayer.tools.JsonBrowser Java Examples

The following examples show how to use com.sedmelluq.discord.lavaplayer.tools.JsonBrowser. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DefaultSoundCloudPlaylistLoader.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
protected AudioPlaylist loadFromSet(
    HttpInterfaceManager httpInterfaceManager,
    String playlistWebUrl,
    Function<AudioTrackInfo, AudioTrack> trackFactory
) {
  try (HttpInterface httpInterface = httpInterfaceManager.getInterface()) {
    JsonBrowser rootData = htmlDataLoader.load(httpInterface, playlistWebUrl);
    JsonBrowser playlistData = dataReader.findPlaylistData(rootData);

    return new BasicAudioPlaylist(
        dataReader.readPlaylistName(playlistData),
        loadPlaylistTracks(httpInterface, playlistData, trackFactory),
        null,
        false
    );
  } catch (IOException e) {
    throw new FriendlyException("Loading playlist from SoundCloud failed.", SUSPICIOUS, e);
  }
}
 
Example #2
Source File: YoutubeAudioSourceManager.java    From kyoko with MIT License 6 votes vote down vote up
private JsonBrowser loadTrackBaseInfoFromEmbedPage(HttpInterface httpInterface, String videoId) throws IOException {
    try (CloseableHttpResponse response = httpInterface.execute(new HttpGet("https://www.youtube.com/embed/" + videoId))) {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != 200) {
            throw new IOException("Invalid status code for embed video page response: " + statusCode);
        }

        String html = IOUtils.toString(response.getEntity().getContent(), Charset.forName(CHARSET));
        String configJson = DataFormatTools.extractBetween(html, "'PLAYER_CONFIG': ", "});writeEmbed();");

        if (configJson != null) {
            return JsonBrowser.parse(configJson);
        }
    }

    throw new FriendlyException("Track information is unavailable.", SUSPICIOUS,
            new IllegalStateException("Expected player config is not present in embed page."));
}
 
Example #3
Source File: TwitchStreamAudioSourceManager.java    From kyoko with MIT License 6 votes vote down vote up
@Override
public AudioItem loadItem(DefaultAudioPlayerManager manager, AudioReference reference) {
    String streamName = getChannelIdentifierFromUrl(reference.identifier);
    if (streamName == null) {
        return null;
    }

    JsonBrowser channelInfo = fetchChannelInfo(streamName);

    if (channelInfo == null) {
        return AudioReference.NO_TRACK;
    } else {
        final String displayName = channelInfo.get("display_name").text();
        final String status = channelInfo.get("status").text();

        return new TwitchStreamAudioTrack(new AudioTrackInfo(
                status,
                displayName,
                Long.MAX_VALUE,
                reference.identifier,
                true,
                reference.identifier
        ), this);
    }
}
 
Example #4
Source File: YoutubeMixProvider.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
private void extractPlaylistTracks(
    JsonBrowser browser,
    List<AudioTrack> tracks,
    Function<AudioTrackInfo, AudioTrack> trackFactory
) {
  for (JsonBrowser video : browser.values()) {
    JsonBrowser renderer = video.get("playlistPanelVideoRenderer");
    String title = renderer.get("title").get("simpleText").text();
    String author = renderer.get("longBylineText").get("runs").index(0).get("text").text();
    String durationStr = renderer.get("lengthText").get("simpleText").text();
    long duration = parseDuration(durationStr);
    String identifier = renderer.get("videoId").text();
    String uri = "https://youtube.com/watch?v=" + identifier;

    AudioTrackInfo trackInfo = new AudioTrackInfo(title, author, duration, identifier, false, uri);
    tracks.add(trackFactory.apply(trackInfo));
  }
}
 
Example #5
Source File: VimeoAudioSourceManager.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
private AudioTrack loadTrackFromPageContent(String trackUrl, String content) throws IOException {
  JsonBrowser config = loadConfigJsonFromPageContent(content);

  if (config == null) {
    throw new FriendlyException("Track information not found on the page.", SUSPICIOUS, null);
  }

  return new VimeoAudioTrack(new AudioTrackInfo(
      config.get("clip").get("title").text(),
      config.get("owner").get("display_name").text(),
      (long) (config.get("clip").get("duration").get("raw").as(Double.class) * 1000.0),
      trackUrl,
      false,
      trackUrl
  ), this);
}
 
Example #6
Source File: TwitchStreamSegmentUrlProvider.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
@Override
protected String fetchSegmentPlaylistUrl(HttpInterface httpInterface) throws IOException {
  if (System.currentTimeMillis() < tokenExpirationTime) {
    return streamSegmentPlaylistUrl;
  }

  JsonBrowser token = loadAccessToken(httpInterface);
  HttpUriRequest request = new HttpGet(getChannelStreamsUrl(token).toString());
  ChannelStreams streams = loadChannelStreamsInfo(HttpClientTools.fetchResponseLines(httpInterface, request, "channel streams list"));

  if (streams.entries.isEmpty()) {
    throw new IllegalStateException("No streams available on channel.");
  }

  ChannelStreamInfo stream = streams.entries.get(0);

  log.debug("Chose stream with quality {} from url {}", stream.quality, stream.url);
  streamSegmentPlaylistUrl = stream.url;

  long tokenServerExpirationTime = JsonBrowser.parse(token.get(TOKEN_PARAMETER).text()).get("expires").as(Long.class) * 1000L;
  tokenExpirationTime = System.currentTimeMillis() + (tokenServerExpirationTime - streams.serverTime) - 5000;

  return streamSegmentPlaylistUrl;
}
 
Example #7
Source File: DefaultYoutubePlaylistLoader.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
@Override
public AudioPlaylist load(HttpInterface httpInterface, String playlistId, String selectedVideoId,
                          Function<AudioTrackInfo, AudioTrack> trackFactory) {

  HttpGet request = new HttpGet(getPlaylistUrl(playlistId) + "&pbj=1&hl=en");

  try (CloseableHttpResponse response = httpInterface.execute(request)) {
    int statusCode = response.getStatusLine().getStatusCode();
    if (!HttpClientTools.isSuccessWithContent(statusCode)) {
      throw new IOException("Invalid status code for playlist response: " + statusCode);
    }

    JsonBrowser json = JsonBrowser.parse(response.getEntity().getContent());

    return buildPlaylist(httpInterface, json, selectedVideoId, trackFactory);
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
}
 
Example #8
Source File: DefaultYoutubeTrackDetailsLoader.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
protected String getUnplayableReason(JsonBrowser statusBlock) {
  JsonBrowser playerErrorMessage = statusBlock.get("errorScreen").get("playerErrorMessageRenderer");
  String unplayableReason = statusBlock.get("reason").text();

  if (!playerErrorMessage.get("subreason").isNull()) {
    JsonBrowser subreason = playerErrorMessage.get("subreason");

    if (!subreason.get("simpleText").isNull()) {
      unplayableReason = subreason.get("simpleText").text();
    } else if (!subreason.get("runs").isNull() && subreason.get("runs").isList()) {
      StringBuilder reasonBuilder = new StringBuilder();
      subreason.get("runs").values().forEach(
          item -> reasonBuilder.append(item.get("text").text()).append('\n')
      );
      unplayableReason = reasonBuilder.toString();
    }
  }

  return unplayableReason;
}
 
Example #9
Source File: DefaultSoundCloudHtmlDataLoader.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
@Override
public JsonBrowser load(HttpInterface httpInterface, String url) throws IOException {
  try (CloseableHttpResponse response = httpInterface.execute(new HttpGet(url))) {
    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
      return JsonBrowser.NULL_BROWSER;
    }

    HttpClientTools.assertSuccessWithContent(response, "video page response");

    String html = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
    String rootData = extractJsonFromHtml(html);

    if (rootData == null) {
      throw new FriendlyException("This url does not appear to be a playable track.", SUSPICIOUS, null);
    }

    return JsonBrowser.parse(rootData);
  }
}
 
Example #10
Source File: YoutubeSearchProvider.java    From lavaplayer with Apache License 2.0 6 votes vote down vote up
private List<AudioTrack> polymerExtractTracks(Document document, Function<AudioTrackInfo, AudioTrack> trackFactory) throws IOException {
  // Match the JSON from the HTML. It should be within a script tag
  Matcher matcher = polymerInitialDataRegex.matcher(document.outerHtml());
  if (!matcher.find()) {
    log.warn("Failed to match ytInitialData JSON object");
    return Collections.emptyList();
  }

  JsonBrowser jsonBrowser = JsonBrowser.parse(matcher.group(1));
  ArrayList<AudioTrack> list = new ArrayList<>();
  jsonBrowser.get("contents")
      .get("twoColumnSearchResultsRenderer")
      .get("primaryContents")
      .get("sectionListRenderer")
      .get("contents")
      .index(0)
      .get("itemSectionRenderer")
      .get("contents")
      .values()
      .forEach(json -> {
        AudioTrack track = extractPolymerData(json, trackFactory);
        if (track != null) list.add(track);
      });
  return list;
}
 
Example #11
Source File: YoutubeVersionData.java    From SkyBot with GNU Affero General Public License v3.0 5 votes vote down vote up
public static YoutubeVersionData fromBrowser(JsonBrowser json) {
    return new YoutubeVersionData(
        json.get("INNERTUBE_CONTEXT_CLIENT_VERSION").safeText(),
        json.get("VARIANTS_CHECKSUM").safeText(),
        json.get("PAGE_BUILD_LABEL").safeText(),
        json.get("PAGE_CL").safeText()
    );
}
 
Example #12
Source File: SoundCloudAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private AudioItem loadSearchResultsFromResponse(HttpResponse response, String query) throws IOException {
  try {
    JsonBrowser searchResults = JsonBrowser.parse(response.getEntity().getContent());
    return extractTracksFromSearchResults(query, searchResults);
  } finally {
    EntityUtils.consumeQuietly(response.getEntity());
  }
}
 
Example #13
Source File: SoundCloudAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private AudioItem extractTracksFromLikedList(JsonBrowser likedTracks, UserInfo userInfo) {
  List<AudioTrack> tracks = new ArrayList<>();

  for (JsonBrowser item : likedTracks.get("collection").values()) {
    JsonBrowser trackItem = item.get("track");

    if (!trackItem.isNull() && !dataReader.isTrackBlocked(trackItem)) {
      tracks.add(loadFromTrackData(trackItem));
    }
  }

  return new BasicAudioPlaylist("Liked by " + userInfo.name, tracks, null, false);
}
 
Example #14
Source File: SoundCloudAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private JsonBrowser loadLikedListForUserId(HttpInterface httpInterface, UserInfo userInfo) throws IOException {
  URI uri = URI.create("https://api-v2.soundcloud.com/users/" + userInfo.id + "/likes?limit=200&offset=0");

  try (CloseableHttpResponse response = httpInterface.execute(new HttpGet(uri))) {
    HttpClientTools.assertSuccessWithContent(response, "liked tracks response");
    return JsonBrowser.parse(response.getEntity().getContent());
  }
}
 
Example #15
Source File: SoundCloudAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
public AudioTrack loadFromTrackPage(String trackWebUrl) {
  try (HttpInterface httpInterface = getHttpInterface()) {
    JsonBrowser rootData = htmlDataLoader.load(httpInterface, trackWebUrl);
    JsonBrowser trackData = dataReader.findTrackData(rootData);

    if (trackData == null) {
      throw new FriendlyException("This track is not available", COMMON, null);
    }

    return loadFromTrackData(trackData);
  } catch (IOException e) {
    throw new FriendlyException("Loading track from SoundCloud failed.", SUSPICIOUS, e);
  }
}
 
Example #16
Source File: DefaultSoundCloudDataReader.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
protected JsonBrowser findEntryOfKind(JsonBrowser data, String kind) {
  for (JsonBrowser value : data.values()) {
    for (JsonBrowser entry : value.get("data").values()) {
      if (entry.isMap() && kind.equals(entry.get("kind").text())) {
        return entry;
      }
    }
  }

  return null;
}
 
Example #17
Source File: BandcampAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private JsonBrowser readAlbumInformation(String text) throws IOException {
  String albumInfoJson = DataFormatTools.extractBetween(text, "var EmbedData = ", "};");

  if (albumInfoJson == null) {
    throw new FriendlyException("Album information not found on the Bandcamp page.", SUSPICIOUS, null);
  }

  albumInfoJson = albumInfoJson.replace("\" + \"", "") + "};";
  return JsonBrowser.parse(albumInfoJson);
}
 
Example #18
Source File: TwitchStreamSegmentUrlProvider.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private JsonBrowser loadAccessToken(HttpInterface httpInterface) throws IOException {
  HttpUriRequest request = createSegmentGetRequest("https://api.twitch.tv/api/channels/" + channelName +
      "/access_token?adblock=false&need_https=true&platform=web&player_type=site");

  try (CloseableHttpResponse response = httpInterface.execute(request)) {
    int statusCode = response.getStatusLine().getStatusCode();
    if (!HttpClientTools.isSuccessWithContent(statusCode)) {
      throw new IOException("Unexpected response code from access token request: " + statusCode);
    }

    return JsonBrowser.parse(response.getEntity().getContent());
  }
}
 
Example #19
Source File: BandcampAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private AudioTrack extractTrack(JsonBrowser trackInfo, String bandUrl, String artist) {
  String trackPageUrl = bandUrl + trackInfo.get("title_link").text();

  return new BandcampAudioTrack(new AudioTrackInfo(
      trackInfo.get("title").text(),
      artist,
      (long) (trackInfo.get("duration").as(Double.class) * 1000.0),
      bandUrl + trackInfo.get("title_link").text(),
      false,
      trackPageUrl
  ), this);
}
 
Example #20
Source File: SoundCloudAudioSourceManager.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private AudioItem extractTracksFromSearchResults(String query, JsonBrowser searchResults) {
  List<AudioTrack> tracks = new ArrayList<>();

  for (JsonBrowser item : searchResults.get("collection").values()) {
    if (!item.isNull()) {
      tracks.add(loadFromTrackData(item));
    }
  }

  return new BasicAudioPlaylist("Search results for: " + query, tracks, null, true);
}
 
Example #21
Source File: YoutubeAudioSourceManager.java    From kyoko with MIT License 5 votes vote down vote up
/**
 * @param httpInterface HTTP interface to use for performing any necessary request.
 * @param videoId       ID of the video.
 * @param mustExist     If <code>true</code>, throws an exception instead of returning <code>null</code> if the track does
 *                      not exist.
 * @return JSON information about the track if it exists. <code>null</code> if it does not and mustExist is
 * <code>false</code>.
 * @throws IOException On network error.
 */
public JsonBrowser getTrackInfoFromMainPage(HttpInterface httpInterface, String videoId, boolean mustExist) throws IOException {
    try (CloseableHttpResponse response = httpInterface.execute(new HttpGet(getWatchUrl(videoId)))) {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != 200) {
            throw new IOException("Invalid status code for video page response: " + statusCode);
        }

        String html = IOUtils.toString(response.getEntity().getContent(), Charset.forName(CHARSET));
        String configJson = DataFormatTools.extractBetween(html, "ytplayer.config = ", ";ytplayer.load");

        if (configJson != null) {
            return JsonBrowser.parse(configJson);
        } else {
            if (html.contains("player-age-gate-content\">")) {
                // In case main page does not give player configuration, but info page indicates an OK result, it is probably an
                // age-restricted video for which the complete track info can be combined from the embed page and the info page.
                return getTrackInfoFromEmbedPage(httpInterface, videoId);
            }
        }
    }

    if (determineFailureReason(httpInterface, videoId, mustExist)) {
        return null;
    }

    log.debug("Falling back to embed...");
    return getTrackInfoFromEmbedPage(httpInterface, videoId);
}
 
Example #22
Source File: DefaultSoundCloudDataReader.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
@Override
public AudioTrackInfo readTrackInfo(JsonBrowser trackData, String identifier) {
  return new AudioTrackInfo(
      trackData.get("title").safeText(),
      trackData.get("user").get("username").safeText(),
      trackData.get("duration").as(Integer.class),
      identifier,
      false,
      trackData.get("permalink_url").text()
  );
}
 
Example #23
Source File: DefaultSoundCloudPlaylistLoader.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
protected void sortPlaylistTracks(List<JsonBrowser> trackDataList, List<String> trackIds) {
  Map<String, Integer> positions = new HashMap<>();

  for (int i = 0; i < trackIds.size(); i++) {
    positions.put(trackIds.get(i), i);
  }

  trackDataList.sort(Comparator.comparingInt(trackData ->
      positions.getOrDefault(dataReader.readTrackId(trackData), Integer.MAX_VALUE)
  ));
}
 
Example #24
Source File: SoundCloudHelper.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
public static String loadPlaybackUrl(HttpInterface httpInterface, String jsonUrl) throws IOException {
  try (PersistentHttpStream stream = new PersistentHttpStream(httpInterface, URI.create(jsonUrl), null)) {
    if (!HttpClientTools.isSuccessWithContent(stream.checkStatusCode())) {
      throw new IOException("Invalid status code for soundcloud stream: " + stream.checkStatusCode());
    }

    JsonBrowser json = JsonBrowser.parse(stream);
    return json.get("url").text();
  }
}
 
Example #25
Source File: YoutubeSearchProvider.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private AudioTrack extractPolymerData(JsonBrowser json, Function<AudioTrackInfo, AudioTrack> trackFactory) {
  json = json.get("videoRenderer");
  if (json.isNull()) return null; // Ignore everything which is not a track

  String title = json.get("title").get("runs").index(0).get("text").text();
  String author = json.get("ownerText").get("runs").index(0).get("text").text();
  long duration = DataFormatTools.durationTextToMillis(json.get("lengthText").get("simpleText").text());
  String videoId = json.get("videoId").text();

  AudioTrackInfo info = new AudioTrackInfo(title, author, duration, videoId, false,
      WATCH_URL_PREFIX + videoId);

  return trackFactory.apply(info);
}
 
Example #26
Source File: DefaultYoutubeTrackDetailsLoader.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
protected InfoStatus checkStatusBlock(JsonBrowser statusBlock) {
  if (statusBlock.isNull()) {
    throw new RuntimeException("No playability status block.");
  }

  String status = statusBlock.get("status").text();

  if (status == null) {
    throw new RuntimeException("No playability status field.");
  } else if ("OK".equals(status)) {
    return InfoStatus.INFO_PRESENT;
  } else if ("ERROR".equals(status)) {
    String reason = statusBlock.get("reason").text();

    if ("Video unavailable".equals(reason)) {
      return InfoStatus.DOES_NOT_EXIST;
    } else {
      throw new FriendlyException(reason, COMMON, null);
    }
  } else if ("UNPLAYABLE".equals(status)) {
    String unplayableReason = getUnplayableReason(statusBlock);
    throw new FriendlyException(unplayableReason, COMMON, null);
  } else if ("LOGIN_REQUIRED".equals(status)) {
    String errorReason = statusBlock.get("errorScreen")
            .get("playerErrorMessageRenderer")
            .get("reason")
            .get("simpleText")
            .text();

    if ("Private video".equals(errorReason)) {
      throw new FriendlyException("This is a private video.", COMMON, null);
    }

    return InfoStatus.REQUIRES_LOGIN;
  } else {
    throw new FriendlyException("This video cannot be viewed anonymously.", COMMON, null);
  }
}
 
Example #27
Source File: DefaultYoutubeTrackDetailsLoader.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
protected JsonBrowser loadTrackBaseInfoFromEmbedPage(HttpInterface httpInterface, String videoId) throws IOException {
  try (CloseableHttpResponse response = httpInterface.execute(new HttpGet("https://www.youtube.com/embed/" + videoId))) {
    HttpClientTools.assertSuccessWithContent(response, "embed video page response");

    String html = EntityUtils.toString(response.getEntity(), UTF_8);
    String configJson = DataFormatTools.extractBetween(html, "'PLAYER_CONFIG': ", "});writeEmbed();");

    if (configJson != null) {
      return JsonBrowser.parse(configJson);
    }
  }

  throw new FriendlyException("Track information is unavailable.", SUSPICIOUS,
          new IllegalStateException("Expected player config is not present in embed page."));
}
 
Example #28
Source File: DefaultYoutubePlaylistLoader.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private String extractPlaylistTracks(JsonBrowser playlistVideoList, List<AudioTrack> tracks,
                                     Function<AudioTrackInfo, AudioTrack> trackFactory) {

  JsonBrowser trackArray = playlistVideoList.get("contents");

  if (trackArray.isNull()) return null;

  for (JsonBrowser track : trackArray.values()) {
    JsonBrowser item = track.get("playlistVideoRenderer");

    JsonBrowser shortBylineText = item.get("shortBylineText");

    // If the isPlayable property does not exist, it means the video is removed or private
    // If the shortBylineText property does not exist, it means the Track is Region blocked
    if (!item.get("isPlayable").isNull() && !shortBylineText.isNull()) {
      String videoId = item.get("videoId").text();
      String title = item.get("title").get("simpleText").text();
      String author = shortBylineText.get("runs").index(0).get("text").text();
      JsonBrowser lengthSeconds = item.get("lengthSeconds");
      long duration = Units.secondsToMillis(lengthSeconds.asLong(Units.DURATION_SEC_UNKNOWN));

      AudioTrackInfo info = new AudioTrackInfo(title, author, duration, videoId, false,
          "https://www.youtube.com/watch?v=" + videoId);

      tracks.add(trackFactory.apply(info));
    }
  }

  JsonBrowser continuations = playlistVideoList.get("continuations");

  if (!continuations.isNull()) {
    String continuationsToken = continuations.index(0).get("nextContinuationData").get("continuation").text();
    return "/browse_ajax?continuation=" + continuationsToken + "&ctoken=" + continuationsToken + "&hl=en";
  }

  return null;
}
 
Example #29
Source File: DefaultYoutubeTrackDetails.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
private long extractDurationSeconds(boolean isStream, JsonBrowser object, String field) {
  if (isStream) {
    return DURATION_MS_UNKNOWN;
  }

  return Units.secondsToMillis(object.get(field).asLong(DURATION_SEC_UNKNOWN));
}
 
Example #30
Source File: HttpClientTools.java    From lavaplayer with Apache License 2.0 5 votes vote down vote up
/**
 * Executes an HTTP request and returns the response as a JsonBrowser instance.
 *
 * @param httpInterface HTTP interface to use for the request.
 * @param request Request to perform.
 * @return Response as a JsonBrowser instance. null in case of 404.
 * @throws IOException On network error or for non-200 response code.
 */
public static JsonBrowser fetchResponseAsJson(HttpInterface httpInterface, HttpUriRequest request) throws IOException {
  try (CloseableHttpResponse response = httpInterface.execute(request)) {
    int statusCode = response.getStatusLine().getStatusCode();

    if (statusCode == HttpStatus.SC_NOT_FOUND) {
      return null;
    } else if (!isSuccessWithContent(statusCode)) {
      throw new FriendlyException("Server responded with an error.", SUSPICIOUS,
          new IllegalStateException("Response code from channel info is " + statusCode));
    }

    return JsonBrowser.parse(response.getEntity().getContent());
  }
}