package moe.kyokobot.music.source; import com.sedmelluq.discord.lavaplayer.container.mpeg.MpegAudioTrack; import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; import com.sedmelluq.discord.lavaplayer.tools.io.PersistentHttpStream; import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; import com.sedmelluq.discord.lavaplayer.track.DelegatedAudioTrack; import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Map; import static com.sedmelluq.discord.lavaplayer.tools.DataFormatTools.convertToMapLayout; /** * Audio track that handles processing NicoNico tracks. */ @SuppressWarnings("WeakerAccess") public class NicoAudioTrack extends DelegatedAudioTrack { private static final Logger log = LoggerFactory.getLogger(NicoAudioTrack.class); private final NicoAudioSourceManager sourceManager; /** * @param trackInfo Track info * @param sourceManager Source manager which was used to find this track */ public NicoAudioTrack(AudioTrackInfo trackInfo, NicoAudioSourceManager sourceManager) { super(trackInfo); this.sourceManager = sourceManager; } @Override public void process(LocalAudioTrackExecutor localExecutor) throws Exception { sourceManager.checkLoggedIn(); try (HttpInterface httpInterface = sourceManager.getHttpInterface()) { loadVideoMainPage(httpInterface); String playbackUrl = loadPlaybackUrl(httpInterface); log.debug("Starting NicoNico track from URL: {}", playbackUrl); try (PersistentHttpStream stream = new PersistentHttpStream(httpInterface, new URI(playbackUrl), null)) { processDelegate(new MpegAudioTrack(trackInfo, stream), localExecutor); } } } private void loadVideoMainPage(HttpInterface httpInterface) throws IOException { HttpGet request = new HttpGet("http://www.nicovideo.jp/watch/" + trackInfo.identifier); try (CloseableHttpResponse response = httpInterface.execute(request)) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) { throw new IOException("Unexpected status code from video main page: " + statusCode); } EntityUtils.consume(response.getEntity()); } } private String loadPlaybackUrl(HttpInterface httpInterface) throws IOException { HttpGet request = new HttpGet("http://flapi.nicovideo.jp/api/getflv/" + trackInfo.identifier); try (CloseableHttpResponse response = httpInterface.execute(request)) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) { throw new IOException("Unexpected status code from playback parameters page: " + statusCode); } String text = EntityUtils.toString(response.getEntity()); Map<String, String> format = convertToMapLayout(URLEncodedUtils.parse(text, StandardCharsets.UTF_8)); String url = format.get("url"); try (CloseableHttpResponse testResponse = httpInterface.execute(new HttpGet(url))) { int testStatusCode = testResponse.getStatusLine().getStatusCode(); if (testStatusCode != 200) { url = url.replace("/smile?v=", "/smile?m="); url += "low"; } } return url; } } @Override public AudioTrack makeClone() { return new NicoAudioTrack(trackInfo, sourceManager); } @Override public AudioSourceManager getSourceManager() { return sourceManager; } }