From 62f1686dce3112c7c312b2b4f07376f9cadda600 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Thu, 22 Feb 2024 08:09:53 +0100 Subject: [PATCH] Allow to fetch streams by date to access content more than 7 days into the past --- src/main/java/org/dstadler/audio/fm4/FM4.java | 75 +++++++++++++++---- .../java/org/dstadler/audio/fm4/FM4Test.java | 22 ++++++ 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/dstadler/audio/fm4/FM4.java b/src/main/java/org/dstadler/audio/fm4/FM4.java index 193c6ec..53995c7 100644 --- a/src/main/java/org/dstadler/audio/fm4/FM4.java +++ b/src/main/java/org/dstadler/audio/fm4/FM4.java @@ -32,28 +32,46 @@ public class FM4 { private static final ObjectMapper objectMapper = new ObjectMapper(); + /** + * Fetch default list of previous streams, usually returns data for the last 7 days + * @return A list of {@link FM4Stream} + * @throws IOException If fetching data via the FM4 REST API fails + */ public List fetchStreams() throws IOException { return fetchStreams(FM4_API_URL, FM4_STREAM_URL_BASE); } - public List fetchStreams(String apiUrl, String streamUrlBase) throws IOException { - final String json; - try { - // fetch stream - json = HttpClientWrapper.retrieveData(apiUrl); - } catch (IOException e) { - throw new IOException("While reading from: " + apiUrl, e); - } + /** + * Fetch a list of previous streams for a given day. + * + * @param date A date in the form of yyyyMMdd, usually data is available for the last 30 days + * @return A list of {@link FM4Stream} + * @throws IOException If fetching data via the FM4 REST API fails + */ + public List fetchStreams(String date) throws IOException { + // https://audioapi.orf.at/fm4/api/json/5.0/broadcasts/20240131?_o=sound.orf.at + String apiUrl = FM4_API_URL.replace("/broadcasts?", "/broadcasts/" + date + "?"); - JsonNode jsonNode = objectMapper.readTree(json).get("payload"); + JsonNode jsonNode = parsAPI(apiUrl); - /*{ - "dateOffset" : -3600000, - "broadcasts" : [ - { - ... + List streams = new ArrayList<>(); + for (JsonNode node : jsonNode) { + streams.add(new FM4Stream(node, FM4_STREAM_URL_BASE)); + } + return streams; + } - */ + /** + * Fetch default list of previous streams via the given REST API URLs. + * This usually returns data for the last 7 days + * + * @param apiUrl The REST API URL to use + * @param streamUrlBase The URL to use for downloading streams + * @return A list of {@link FM4Stream} + * @throws IOException If fetching data via the FM4 REST API fails + */ + public List fetchStreams(String apiUrl, String streamUrlBase) throws IOException { + JsonNode jsonNode = parsAPI(apiUrl); List streams = new ArrayList<>(); for (JsonNode node : jsonNode) { @@ -64,6 +82,26 @@ public List fetchStreams(String apiUrl, String streamUrlBase) throws return streams; } + private static JsonNode parsAPI(String apiUrl) throws IOException { + final String json; + try { + // fetch stream + json = HttpClientWrapper.retrieveData(apiUrl); + } catch (IOException e) { + throw new IOException("While reading from: " + apiUrl, e); + } + + return objectMapper.readTree(json).get("payload"); + } + + /** + * Fetch streams and filter them by the given programKey and also remove + * future instances of streams. + * + * @param programKey The FM4 program-key, e.g. "4MG" + * @return A list of {@link FM4Stream} + * @throws IOException If fetching data via the FM4 REST API fails + */ public List filterStreams(String programKey) throws IOException { List streams = fetchStreams(); @@ -87,6 +125,13 @@ public List filterStreams(String programKey) throws IOException { collect(Collectors.toList()); } + /** + * Download the given stream to a MP3 file. + * + * @param stream The FM4 stream to download + * @param downloadDir Where to store the resulting file. It will be named after the title of the stream. + * @throws IOException If fetching data via the FM4 REST API fails + */ public void downloadStream(FM4Stream stream, File downloadDir) throws IOException { int count = 0; for (String url : stream.getStreams()) { diff --git a/src/test/java/org/dstadler/audio/fm4/FM4Test.java b/src/test/java/org/dstadler/audio/fm4/FM4Test.java index e951ce6..094ef97 100644 --- a/src/test/java/org/dstadler/audio/fm4/FM4Test.java +++ b/src/test/java/org/dstadler/audio/fm4/FM4Test.java @@ -6,6 +6,8 @@ import org.apache.commons.io.HexDump; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.commons.lang3.time.FastDateFormat; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -25,6 +27,7 @@ import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.text.ParseException; +import java.util.Date; import java.util.List; import java.util.logging.Logger; @@ -60,6 +63,25 @@ public void testFetch() throws IOException, ParseException { } } + @Test + public void testFetchForDate() throws IOException, ParseException { + String date = FastDateFormat.getInstance("yyyyMMdd").format(DateUtils.addDays(new Date(), -3)); + List fm4Streams = fm4.fetchStreams(date); + + assertNotNull(fm4Streams); + assertFalse(fm4Streams.isEmpty()); + + // check resulting streams are sane + for (FM4Stream stream : fm4Streams) { + assertTrue(stream.getDuration() > 60_000); + assertTrue(stream.getStart() > System.currentTimeMillis() - (35L*24*60*60*1000)); + assertNotNull(stream.getProgramKey()); + assertNotNull(stream.getTitle()); + assertNotNull(stream.getShortTime()); + assertNotNull(DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse(stream.getShortTime())); + } + } + @Ignore("Fetches streams for all programs, this runs for some time, so disable for CI") @Test public void testFetchAllStreams() throws IOException {