summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Lehmann <ByteHamster@users.noreply.github.com>2019-03-03 23:26:32 +0100
committerGitHub <noreply@github.com>2019-03-03 23:26:32 +0100
commit1593a0607774a157dffa6c95e068b6ab3b077cf7 (patch)
treefa7a40d41164c3d2bd6df65c7ccdeae53181e604
parent335d287d5ef01f56a50ad44e9476ece98fe42622 (diff)
parent1d0e701525a33c638822a59241d99b96e026649a (diff)
downloadAntennaPod-1593a0607774a157dffa6c95e068b6ab3b077cf7.zip
Merge pull request #3032 from shortspider/3031-TimecodeRegex
Change Timecode Regex
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/ConverterTest.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java89
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Converter.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java94
4 files changed, 173 insertions, 47 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/util/ConverterTest.java b/app/src/androidTest/java/de/test/antennapod/util/ConverterTest.java
index 47fca41ba..cd1a489d8 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/ConverterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/ConverterTest.java
@@ -17,8 +17,8 @@ public class ConverterTest extends AndroidTestCase {
public void testGetDurationStringShort() throws Exception {
String expected = "13:05";
- int input = 47110000;
- assertEquals(expected, Converter.getDurationStringShort(input));
+ assertEquals(expected, Converter.getDurationStringShort(47110000, true));
+ assertEquals(expected, Converter.getDurationStringShort(785000, false));
}
public void testDurationStringLongToMs() throws Exception {
@@ -29,7 +29,7 @@ public class ConverterTest extends AndroidTestCase {
public void testDurationStringShortToMs() throws Exception {
String input = "8:30";
- long expected = 30600000;
- assertEquals(expected, Converter.durationStringShortToMs(input));
+ assertEquals(30600000, Converter.durationStringShortToMs(input, true));
+ assertEquals(510000, Converter.durationStringShortToMs(input, false));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
index 7e535e12c..4bef14cd9 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
@@ -30,12 +30,12 @@ public class TimelineTest extends InstrumentationTestCase {
context = getInstrumentation().getTargetContext();
}
- private Playable newTestPlayable(List<Chapter> chapters, String shownotes) {
+ private Playable newTestPlayable(List<Chapter> chapters, String shownotes, int duration) {
FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null);
item.setChapters(chapters);
item.setContentEncoded(shownotes);
FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
- media.setDuration(Integer.MAX_VALUE);
+ media.setDuration(duration);
item.setMedia(media);
return media;
}
@@ -44,7 +44,17 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11:12";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>");
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeHHMMSSMoreThen24HoursNoChapters() throws Exception {
+ final String timeStr = "25:00:00";
+ final long time = 25 * 60 * 60 * 1000;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@@ -54,17 +64,67 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>");
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeMMSSNoChapters() throws Exception {
+ final String timeStr = "10:11";
+ final long time = 10 * 60 * 1000 + 11 * 1000;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", 11 * 60 * 1000);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ public void testProcessShownotesAddTimecodeHMMSSNoChapters() throws Exception {
+ final String timeStr = "2:11:12";
+ final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeMSSNoChapters() throws Exception {
+ final String timeStr = "1:12";
+ final long time = 60 * 1000 + 12 * 1000;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", 2 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeMultipleFormatsNoChapters() throws Exception {
+ final String[] timeStrings = new String[]{ "10:12", "1:10:12" };
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 2 * 60 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000 }, timeStrings);
+ }
+
+ public void testProcessShownotesAddTimecodeMultipleShortFormatNoChapters() throws Exception {
+
+ // One of these timecodes fits as HH:MM and one does not so both should be parsed as MM:SS.
+ final String[] timeStrings = new String[]{ "10:12", "2:12" };
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 3 * 60 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000 }, timeStrings);
+ }
+
public void testProcessShownotesAddTimecodeParentheses() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode (" + timeStr + ") here.</p>");
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode (" + timeStr + ") here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@@ -74,7 +134,7 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode [" + timeStr + "] here.</p>");
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode [" + timeStr + "] here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@@ -84,12 +144,27 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode <" + timeStr + "> here.</p>");
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode <" + timeStr + "> here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ public void testProcessShownotesAndInvalidTimecode() throws Exception {
+ final String[] timeStrs = new String[] {"2:1", "0:0", "000", "00", "00:000"};
+
+ StringBuilder shownotes = new StringBuilder("<p> Some test text with timecodes ");
+ for (String timeStr : timeStrs) {
+ shownotes.append(timeStr).append(" ");
+ }
+ shownotes.append("here.</p>");
+
+ Playable p = newTestPlayable(null, shownotes.toString(), Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[0], new String[0]);
+ }
+
private void checkLinkCorrect(String res, long[] timecodes, String[] timecodeStr) {
assertNotNull(res);
Document d = Jsoup.parse(res);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
index 6966667bf..6ecca941a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
@@ -74,13 +74,14 @@ public final class Converter {
return String.format(Locale.getDefault(), "%02d:%02d:%02d", h, m, s);
}
- /** Converts milliseconds to a string containing hours and minutes */
- public static String getDurationStringShort(int duration) {
- int h = duration / HOURS_MIL;
- int rest = duration - h * HOURS_MIL;
- int m = rest / MINUTES_MIL;
-
- return String.format(Locale.getDefault(), "%02d:%02d", h, m);
+ /** Converts milliseconds to a string containing hours and minutes or minutes and seconds*/
+ public static String getDurationStringShort(int duration, boolean durationIsInHours) {
+ int firstPartBase = durationIsInHours ? HOURS_MIL : MINUTES_MIL;
+ int firstPart = duration / firstPartBase;
+ int leftoverFromFirstPart = duration - firstPart * firstPartBase;
+ int secondPart = leftoverFromFirstPart / (durationIsInHours ? MINUTES_MIL : SECONDS_MIL);
+
+ return String.format(Locale.getDefault(), "%02d:%02d", firstPart, secondPart);
}
/** Converts long duration string (HH:MM:SS) to milliseconds. */
@@ -94,14 +95,20 @@ public final class Converter {
Integer.parseInt(parts[2]) * 1000;
}
- /** Converts short duration string (HH:MM) to milliseconds. */
- public static int durationStringShortToMs(String input) {
+ /**
+ * Converts short duration string (XX:YY) to milliseconds. If durationIsInHours is true then the
+ * format is HH:MM, otherwise it's MM:SS.
+ * */
+ public static int durationStringShortToMs(String input, boolean durationIsInHours) {
String[] parts = input.split(":");
if (parts.length != 2) {
return 0;
}
- return Integer.parseInt(parts[0]) * 3600 * 1000 +
- Integer.parseInt(parts[1]) * 1000 * 60;
+
+ int modifier = durationIsInHours ? 60 : 1;
+
+ return Integer.parseInt(parts[0]) * 60 * 1000 * modifier+
+ Integer.parseInt(parts[1]) * 1000 * modifier;
}
/** Converts milliseconds to a localized string containing hours and minutes */
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java
index 34cfe6d05..56550bb06 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java
@@ -7,6 +7,7 @@ import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.TypedValue;
import org.jsoup.Jsoup;
@@ -14,6 +15,7 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
+import java.util.ArrayList;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -68,7 +70,7 @@ public class Timeline {
private static final Pattern TIMECODE_LINK_REGEX = Pattern.compile("antennapod://timecode/((\\d+))");
private static final String TIMECODE_LINK = "<a class=\"timecode\" href=\"antennapod://timecode/%d\">%s</a>";
- private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b(?:(?:(([0-9][0-9])):))?(([0-9][0-9])):(([0-9][0-9]))\\b");
+ private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b((\\d+):)?(\\d+):(\\d{2})\\b");
private static final Pattern LINE_BREAK_REGEX = Pattern.compile("<br */?>");
@@ -127,35 +129,12 @@ public class Timeline {
// apply timecode links
if (addTimecodes) {
- Elements elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX);
- Log.d(TAG, "Recognized " + elementsWithTimeCodes.size() + " timecodes");
- for (Element element : elementsWithTimeCodes) {
- Matcher matcherLong = TIMECODE_REGEX.matcher(element.html());
- StringBuffer buffer = new StringBuffer();
- while (matcherLong.find()) {
- String h = matcherLong.group(1);
- String group = matcherLong.group(0);
- int time = (h != null) ? Converter.durationStringLongToMs(group) :
- Converter.durationStringShortToMs(group);
-
- String rep;
- if (playable == null || playable.getDuration() > time) {
- rep = String.format(Locale.getDefault(), TIMECODE_LINK, time, group);
- } else {
- rep = group;
- }
- matcherLong.appendReplacement(buffer, rep);
- }
- matcherLong.appendTail(buffer);
-
- element.html(buffer.toString());
- }
+ addTimecodes(document, playable);
}
return document.toString();
}
-
/**
* Returns true if the given link is a timecode link.
*/
@@ -186,4 +165,69 @@ public class Timeline {
public void setShownotesProvider(@NonNull ShownotesProvider shownotesProvider) {
this.shownotesProvider = shownotesProvider;
}
+
+ private void addTimecodes(Document document, final Playable playable) {
+ Elements elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX);
+ Log.d(TAG, "Recognized " + elementsWithTimeCodes.size() + " timecodes");
+
+ if (elementsWithTimeCodes.size() == 0) {
+ // No elements with timecodes
+ return;
+ }
+
+ int playableDuration = playable == null ? Integer.MAX_VALUE : playable.getDuration();
+ boolean useHourFormat = true;
+
+ if (playableDuration != Integer.MAX_VALUE) {
+
+ // We need to decide if we are going to treat short timecodes as HH:MM or MM:SS. To do
+ // so we will parse all the short timecodes and see if they fit in the duration. If one
+ // does not we will use MM:SS, otherwise all will be parsed as HH:MM.
+ for (Element element : elementsWithTimeCodes) {
+ Matcher matcherForElement = TIMECODE_REGEX.matcher(element.html());
+ while (matcherForElement.find()) {
+
+ // We only want short timecodes right now.
+ if (matcherForElement.group(1) == null) {
+ int time = Converter.durationStringShortToMs(matcherForElement.group(0), true);
+
+ // If the parsed timecode is greater then the duration then we know we need to
+ // use the minute format so we are done.
+ if (time > playableDuration) {
+ useHourFormat = false;
+ break;
+ }
+ }
+ }
+
+ if (!useHourFormat) {
+ break;
+ }
+ }
+ }
+
+ for (Element element : elementsWithTimeCodes) {
+
+ Matcher matcherForElement = TIMECODE_REGEX.matcher(element.html());
+ StringBuffer buffer = new StringBuffer();
+
+ while (matcherForElement.find()) {
+ String group = matcherForElement.group(0);
+
+ int time = matcherForElement.group(1) != null
+ ? Converter.durationStringLongToMs(group)
+ : Converter.durationStringShortToMs(group, useHourFormat);
+
+ String replacementText = group;
+ if (time < playableDuration) {
+ replacementText = String.format(Locale.getDefault(), TIMECODE_LINK, time, group);
+ }
+
+ matcherForElement.appendReplacement(buffer, replacementText);
+ }
+
+ matcherForElement.appendTail(buffer);
+ element.html(buffer.toString());
+ }
+ }
}