Timecode parsing logic now handles two short formats
We now handle both HH:MM and MM:SS when paring timecodes. We will move in reverse order (assuming that the timecodes will increase over the course of the document) and parse short codes as HH:MM. When we get a result that does not fit into the duration we will change to parse as MM:SS and use that for the rest of the document.
This commit is contained in:
parent
d0f617880c
commit
39b9df5064
|
@ -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,27 @@ 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 testProcessShownotesAddTimecodeHHMMNoChapters() 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>", 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});
|
||||
|
@ -54,7 +74,7 @@ public class TimelineTest extends InstrumentationTestCase {
|
|||
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>");
|
||||
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});
|
||||
|
@ -64,47 +84,55 @@ public class TimelineTest extends InstrumentationTestCase {
|
|||
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>");
|
||||
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 testProcessShownotesAddTimecodeMMSSNoChapters() throws Exception {
|
||||
final String timeStr = "10:11";
|
||||
final long time = 60 * 1000 * 10 + 1000 * 11;
|
||||
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 " + timeStr + " here.</p>");
|
||||
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[]{time}, new String[]{timeStr});
|
||||
checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000 }, timeStrings);
|
||||
}
|
||||
|
||||
public void testProcessShownotesAddTimecodeMultipleShortFormatNoChapters() throws Exception {
|
||||
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 * 60 * 1000 + 12 * 60 * 1000 }, timeStrings);
|
||||
}
|
||||
|
||||
public void testProcessShownotesAddTimecodeParentheses() throws Exception {
|
||||
final String timeStr = "10:11:00";
|
||||
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 testProcessShownotesAddTimecodeBrackets() throws Exception {
|
||||
final String timeStr = "10:11:00";
|
||||
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 testProcessShownotesAddTimecodeAngleBrackets() throws Exception {
|
||||
final String timeStr = "10:11:00";
|
||||
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});
|
||||
|
|
|
@ -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;
|
||||
|
@ -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,75 @@ 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");
|
||||
|
||||
// Assuming the timecodes are going to increase through the document loop through the
|
||||
// elements backwards so we can determine when/if we need to shift from HH:MM to MM:SS
|
||||
boolean useHourFormat = true;
|
||||
for (int i = elementsWithTimeCodes.size() - 1; i >= 0 ; i--) {
|
||||
Element element = elementsWithTimeCodes.get(i);
|
||||
Matcher matcherLong = TIMECODE_REGEX.matcher(element.html());
|
||||
|
||||
// Get all matches and store in reverse order
|
||||
ArrayList<Pair<Boolean, String>> matches = new ArrayList<>();
|
||||
while (matcherLong.find()) {
|
||||
matches.add(0, new Pair<>(matcherLong.group(1) != null, matcherLong.group(0)));
|
||||
}
|
||||
|
||||
// Now loop through the reversed matches and get the replacements. Store them in
|
||||
// non-reversed order.
|
||||
ArrayList<String> replacements = new ArrayList<>();
|
||||
for (Pair<Boolean, String> matchPair : matches) {
|
||||
boolean isLongFormat = matchPair.first;
|
||||
String group = matchPair.second;
|
||||
int time = isLongFormat
|
||||
? Converter.durationStringLongToMs(group)
|
||||
: Converter.durationStringShortToMs(group, useHourFormat);
|
||||
|
||||
String rep = group;
|
||||
if (playable == null) {
|
||||
rep = createTimeLink(time, group);
|
||||
} else {
|
||||
int duration = playable.getDuration();
|
||||
|
||||
if (duration > time) {
|
||||
rep = createTimeLink(time, group);
|
||||
} else if (!isLongFormat && useHourFormat) {
|
||||
|
||||
// The duration calculated in hours is too long and the timecode format is
|
||||
// short. So try and see if it will work when treated as minutes.
|
||||
time = Converter.durationStringShortToMs(group, false);
|
||||
|
||||
if (duration > time) {
|
||||
// We have found the treating a short timecode as minutes works, do that
|
||||
// from now on.
|
||||
rep = createTimeLink(time, group);
|
||||
useHourFormat = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replacements.add(0, rep);
|
||||
}
|
||||
|
||||
// Now that we have all the replacements, replace.
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
int index = 0;
|
||||
matcherLong.reset();
|
||||
while (matcherLong.find()) {
|
||||
matcherLong.appendReplacement(buffer, replacements.get(index));
|
||||
index++;
|
||||
}
|
||||
|
||||
matcherLong.appendTail(buffer);
|
||||
element.html(buffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String createTimeLink(int time, String group) {
|
||||
return String.format(Locale.getDefault(), TIMECODE_LINK, time, group);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue