package ctbrec.sites.mfc; import static ctbrec.io.HttpConstants.*; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.iheartradio.m3u8.Encoding; import com.iheartradio.m3u8.Format; import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.ParsingMode; import com.iheartradio.m3u8.PlaylistException; import com.iheartradio.m3u8.PlaylistParser; import com.iheartradio.m3u8.data.MasterPlaylist; import com.iheartradio.m3u8.data.Playlist; import com.iheartradio.m3u8.data.PlaylistData; import ctbrec.Config; import ctbrec.io.HttpClient; import ctbrec.io.HttpException; import ctbrec.recorder.download.StreamSource; import okhttp3.Request; import okhttp3.Response; public record HlsStreamSourceProvider(HttpClient httpClient) implements StreamSourceProvider { private static final Logger LOG = LoggerFactory.getLogger(HlsStreamSourceProvider.class); @Override public List getStreamSources(String streamUrl) throws IOException, ExecutionException, ParseException, PlaylistException { MasterPlaylist masterPlaylist = getMasterPlaylist(streamUrl); List sources = new ArrayList<>(); for (PlaylistData playlist : masterPlaylist.getPlaylists()) { if (playlist.hasStreamInfo()) { StreamSource src = new StreamSource(); src.bandwidth = playlist.getStreamInfo().getBandwidth(); if (playlist.getStreamInfo().getResolution() != null) { src.width = playlist.getStreamInfo().getResolution().width; src.height = playlist.getStreamInfo().getResolution().height; } else { src.width = StreamSource.UNKNOWN; src.height = StreamSource.UNKNOWN; } String masterUrl = streamUrl; String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1); String segmentUri = baseUrl + playlist.getUri(); src.mediaPlaylistUrl = segmentUri; LOG.trace("Media playlist {}", src.mediaPlaylistUrl); sources.add(src); } } return sources; } private MasterPlaylist getMasterPlaylist(String streamUrl) throws IOException, ParseException, PlaylistException { if (streamUrl == null) { throw new IllegalStateException("Stream url unknown"); } LOG.trace("Loading master playlist {}", streamUrl); Request req = new Request.Builder() .url(streamUrl) .header(ACCEPT, "*/*") .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .header(CONNECTION, KEEP_ALIVE) .header(ORIGIN, MyFreeCams.baseUrl) .header(REFERER, MyFreeCams.baseUrl + '/') .build(); try (Response response = httpClient.execute(req)) { if (response.isSuccessful()) { InputStream inputStream = Objects.requireNonNull(response.body(), "HTTP response is null").byteStream(); PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT); Playlist playlist = parser.parse(); MasterPlaylist master = playlist.getMasterPlaylist(); return master; } else { throw new HttpException(response.code(), response.message() + " - " + streamUrl); } } } }