Initial database support for chapter images

This commit is contained in:
ByteHamster 2020-02-07 00:41:44 +01:00
parent cf5a876dc4
commit 5caaaa9e5a
9 changed files with 194 additions and 217 deletions

View File

@ -58,7 +58,8 @@ class DBTestUtils {
List<Chapter> chapters = new ArrayList<>(); List<Chapter> chapters = new ArrayList<>();
item.setChapters(chapters); item.setChapters(chapters);
for (int k = 0; k < numChapters; k++) { for (int k = 0; k < numChapters; k++) {
chapters.add(new SimpleChapter(k, "item " + j + " chapter " + k, item, "http://example.com")); chapters.add(new SimpleChapter(k, "item " + j + " chapter " + k,
"http://example.com", "http://example.com/image.png"));
} }
} }
f.getItems().add(item); f.getItems().add(item);

View File

@ -2,85 +2,99 @@ package de.danoeh.antennapod.core.feed;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
import java.util.Objects;
public abstract class Chapter extends FeedComponent { public abstract class Chapter extends FeedComponent {
/** Defines starting point in milliseconds. */ /** Defines starting point in milliseconds. */
long start; long start;
String title; String title;
String link; String link;
String imageUrl;
Chapter() { Chapter() {
} }
Chapter(long start) {
super();
this.start = start;
}
Chapter(long start, String title, FeedItem item, String link) { Chapter(long start) {
super(); super();
this.start = start; this.start = start;
this.title = title; }
this.link = link;
}
public static Chapter fromCursor(Cursor cursor, FeedItem item) { Chapter(long start, String title, String link, String imageUrl) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID); super();
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE); this.start = start;
int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START); this.title = title;
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK); this.link = link;
int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE); this.imageUrl = imageUrl;
}
long id = cursor.getLong(indexId); public static Chapter fromCursor(Cursor cursor) {
String title = cursor.getString(indexTitle); int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
long start = cursor.getLong(indexStart); int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
String link = cursor.getString(indexLink); int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
int chapterType = cursor.getInt(indexChapterType); int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
int indexImage = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE);
Chapter chapter = null; long id = cursor.getLong(indexId);
switch (chapterType) { String title = cursor.getString(indexTitle);
case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER: long start = cursor.getLong(indexStart);
chapter = new SimpleChapter(start, title, item, link); String link = cursor.getString(indexLink);
break; String imageUrl = cursor.getString(indexImage);
case ID3Chapter.CHAPTERTYPE_ID3CHAPTER: int chapterType = cursor.getInt(indexChapterType);
chapter = new ID3Chapter(start, title, item, link);
break;
case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
chapter = new VorbisCommentChapter(start, title, item, link);
break;
}
chapter.setId(id);
return chapter;
}
Chapter chapter = null;
switch (chapterType) {
case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER:
chapter = new SimpleChapter(start, title, link, imageUrl);
break;
case ID3Chapter.CHAPTERTYPE_ID3CHAPTER:
chapter = new ID3Chapter(start, title, link, imageUrl);
break;
case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
chapter = new VorbisCommentChapter(start, title, link, imageUrl);
break;
}
chapter.setId(id);
return chapter;
}
public abstract int getChapterType(); public abstract int getChapterType();
public long getStart() { public long getStart() {
return start; return start;
} }
public String getTitle() { public String getTitle() {
return title; return title;
} }
public String getLink() { public String getLink() {
return link; return link;
} }
public void setStart(long start) { public void setStart(long start) {
this.start = start; this.start = start;
} }
public void setTitle(String title) { public void setTitle(String title) {
this.title = title; this.title = title;
} }
public void setLink(String link) { public void setLink(String link) {
this.link = link; this.link = link;
} }
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
@Override @Override
public String getHumanReadableIdentifier() { public String getHumanReadableIdentifier() {

View File

@ -1,36 +1,36 @@
package de.danoeh.antennapod.core.feed; package de.danoeh.antennapod.core.feed;
public class ID3Chapter extends Chapter { public class ID3Chapter extends Chapter {
public static final int CHAPTERTYPE_ID3CHAPTER = 2; public static final int CHAPTERTYPE_ID3CHAPTER = 2;
/** /**
* Identifies the chapter in its ID3 tag. This attribute does not have to be * Identifies the chapter in its ID3 tag. This attribute does not have to be
* store in the DB and is only used for parsing. * store in the DB and is only used for parsing.
*/ */
private String id3ID; private String id3ID;
public ID3Chapter(String id3ID, long start) { public ID3Chapter(String id3ID, long start) {
super(start); super(start);
this.id3ID = id3ID; this.id3ID = id3ID;
} }
public ID3Chapter(long start, String title, FeedItem item, String link) { public ID3Chapter(long start, String title, String link, String imageUrl) {
super(start, title, item, link); super(start, title, link, imageUrl);
} }
@Override @Override
public String toString() { public String toString() {
return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start=" return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start="
+ start + ", url=" + link + "]"; + start + ", url=" + link + "]";
} }
@Override @Override
public int getChapterType() { public int getChapterType() {
return CHAPTERTYPE_ID3CHAPTER; return CHAPTERTYPE_ID3CHAPTER;
} }
public String getId3ID() { public String getId3ID() {
return id3ID; return id3ID;
} }
} }

View File

@ -1,25 +1,14 @@
package de.danoeh.antennapod.core.feed; package de.danoeh.antennapod.core.feed;
public class SimpleChapter extends Chapter { public class SimpleChapter extends Chapter {
public static final int CHAPTERTYPE_SIMPLECHAPTER = 0; public static final int CHAPTERTYPE_SIMPLECHAPTER = 0;
public SimpleChapter(long start, String title, FeedItem item, String link) {
super(start, title, item, link);
}
@Override public SimpleChapter(long start, String title, String link, String imageUrl) {
public int getChapterType() { super(start, title, link, imageUrl);
return CHAPTERTYPE_SIMPLECHAPTER; }
}
public void updateFromOther(SimpleChapter other) { @Override
super.updateFromOther(other); public int getChapterType() {
start = other.start; return CHAPTERTYPE_SIMPLECHAPTER;
if (other.title != null) { }
title = other.title;
}
if (other.link != null) {
link = other.link;
}
}
} }

View File

@ -5,105 +5,84 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException; import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
public class VorbisCommentChapter extends Chapter { public class VorbisCommentChapter extends Chapter {
public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3; public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
private static final int CHAPTERXXX_LENGTH = "chapterxxx".length(); private static final int CHAPTERXXX_LENGTH = "chapterxxx".length();
private int vorbisCommentId; private int vorbisCommentId;
public VorbisCommentChapter(int vorbisCommentId) { public VorbisCommentChapter(int vorbisCommentId) {
this.vorbisCommentId = vorbisCommentId; this.vorbisCommentId = vorbisCommentId;
} }
public VorbisCommentChapter(long start, String title, FeedItem item, public VorbisCommentChapter(long start, String title, String link, String imageUrl) {
String link) { super(start, title, link, imageUrl);
super(start, title, item, link); }
}
@Override @Override
public String toString() { public String toString() {
return "VorbisCommentChapter [id=" + id + ", title=" + title return "VorbisCommentChapter [id=" + id + ", title=" + title
+ ", link=" + link + ", start=" + start + "]"; + ", link=" + link + ", start=" + start + "]";
} }
public static long getStartTimeFromValue(String value) public static long getStartTimeFromValue(String value)
throws VorbisCommentReaderException { throws VorbisCommentReaderException {
String[] parts = value.split(":"); String[] parts = value.split(":");
if (parts.length >= 3) { if (parts.length >= 3) {
try { try {
long hours = TimeUnit.MILLISECONDS.convert( long hours = TimeUnit.MILLISECONDS.convert(
Long.parseLong(parts[0]), TimeUnit.HOURS); Long.parseLong(parts[0]), TimeUnit.HOURS);
long minutes = TimeUnit.MILLISECONDS.convert( long minutes = TimeUnit.MILLISECONDS.convert(
Long.parseLong(parts[1]), TimeUnit.MINUTES); Long.parseLong(parts[1]), TimeUnit.MINUTES);
if (parts[2].contains("-->")) { if (parts[2].contains("-->")) {
parts[2] = parts[2].substring(0, parts[2].indexOf("-->")); parts[2] = parts[2].substring(0, parts[2].indexOf("-->"));
} }
long seconds = TimeUnit.MILLISECONDS.convert( long seconds = TimeUnit.MILLISECONDS.convert(
((long) Float.parseFloat(parts[2])), TimeUnit.SECONDS); ((long) Float.parseFloat(parts[2])), TimeUnit.SECONDS);
return hours + minutes + seconds; return hours + minutes + seconds;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new VorbisCommentReaderException(e); throw new VorbisCommentReaderException(e);
} }
} else { } else {
throw new VorbisCommentReaderException("Invalid time string"); throw new VorbisCommentReaderException("Invalid time string");
} }
} }
/** /**
* Return the id of a vorbiscomment chapter from a string like CHAPTERxxx* * Return the id of a vorbiscomment chapter from a string like CHAPTERxxx*
* *
* @return the id of the chapter key or -1 if the id couldn't be read. * @return the id of the chapter key or -1 if the id couldn't be read.
* @throws VorbisCommentReaderException * @throws VorbisCommentReaderException
* */ * */
public static int getIDFromKey(String key) public static int getIDFromKey(String key) throws VorbisCommentReaderException {
throws VorbisCommentReaderException { if (key.length() >= CHAPTERXXX_LENGTH) { // >= CHAPTERxxx
if (key.length() >= CHAPTERXXX_LENGTH) { // >= CHAPTERxxx try {
try { String strId = key.substring(8, 10);
String strId = key.substring(8, 10); return Integer.parseInt(strId);
return Integer.parseInt(strId); } catch (NumberFormatException e) {
} catch (NumberFormatException e) { throw new VorbisCommentReaderException(e);
throw new VorbisCommentReaderException(e); }
} }
} throw new VorbisCommentReaderException("key is too short (" + key + ")");
throw new VorbisCommentReaderException("key is too short (" + key + ")"); }
}
/** /**
* Get the string that comes after 'CHAPTERxxx', for example 'name' or * Get the string that comes after 'CHAPTERxxx', for example 'name' or
* 'url'. * 'url'.
*/ */
public static String getAttributeTypeFromKey(String key) { public static String getAttributeTypeFromKey(String key) {
if (key.length() > CHAPTERXXX_LENGTH) { if (key.length() > CHAPTERXXX_LENGTH) {
return key.substring(CHAPTERXXX_LENGTH, key.length()); return key.substring(CHAPTERXXX_LENGTH);
} }
return null; return null;
} }
@Override @Override
public int getChapterType() { public int getChapterType() {
return CHAPTERTYPE_VORBISCOMMENT_CHAPTER; return CHAPTERTYPE_VORBISCOMMENT_CHAPTER;
} }
public void setTitle(String title) {
this.title = title;
}
public void setLink(String link) {
this.link = link;
}
public void setStart(long start) {
this.start = start;
}
public int getVorbisCommentId() {
return vorbisCommentId;
}
public void setVorbisCommentId(int vorbisCommentId) {
this.vorbisCommentId = vorbisCommentId;
}
public int getVorbisCommentId() {
return vorbisCommentId;
}
} }

View File

@ -795,9 +795,7 @@ public final class DBReader {
} }
private static void loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) { private static void loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) {
Cursor cursor = null; try (Cursor cursor = adapter.getSimpleChaptersOfFeedItemCursor(item)) {
try {
cursor = adapter.getSimpleChaptersOfFeedItemCursor(item);
int chaptersCount = cursor.getCount(); int chaptersCount = cursor.getCount();
if (chaptersCount == 0) { if (chaptersCount == 0) {
item.setChapters(null); item.setChapters(null);
@ -805,14 +803,7 @@ public final class DBReader {
} }
item.setChapters(new ArrayList<>(chaptersCount)); item.setChapters(new ArrayList<>(chaptersCount));
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
Chapter chapter = Chapter.fromCursor(cursor, item); item.getChapters().add(Chapter.fromCursor(cursor));
if (chapter != null) {
item.getChapters().add(chapter);
}
}
} finally {
if (cursor != null) {
cursor.close();
} }
} }
} }

View File

@ -95,7 +95,7 @@ class DBUpgrader {
+ " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD + " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD
+ " TEXT"); + " TEXT");
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ " ADD COLUMN " + PodDBAdapter.KEY_IMAGE + " ADD COLUMN image"
+ " INTEGER"); + " INTEGER");
} }
if (oldVersion <= 12) { if (oldVersion <= 12) {
@ -280,13 +280,13 @@ class DBUpgrader {
+ " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL + " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL
+ " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES
+ " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID + " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID
+ " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_IMAGE + ")"); + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + ".image)");
db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + " SET " + PodDBAdapter.KEY_IMAGE_URL + " = (" db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + " SET " + PodDBAdapter.KEY_IMAGE_URL + " = ("
+ " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL + " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL
+ " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES
+ " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID + " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID
+ " = " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_IMAGE + ")"); + " = " + PodDBAdapter.TABLE_NAME_FEEDS + ".image)");
db.execSQL("DROP TABLE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES); db.execSQL("DROP TABLE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES);
} }
@ -301,6 +301,8 @@ class DBUpgrader {
if (oldVersion < 1090000) { if (oldVersion < 1090000) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0"); + " ADD COLUMN " + PodDBAdapter.KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
+ " ADD COLUMN " + PodDBAdapter.KEY_IMAGE_URL + " TEXT DEFAULT NULL");
} }
} }

View File

@ -69,7 +69,6 @@ public class PodDBAdapter {
public static final String KEY_POSITION = "position"; public static final String KEY_POSITION = "position";
public static final String KEY_SIZE = "filesize"; public static final String KEY_SIZE = "filesize";
public static final String KEY_MIME_TYPE = "mime_type"; public static final String KEY_MIME_TYPE = "mime_type";
public static final String KEY_IMAGE = "image";
public static final String KEY_IMAGE_URL = "image_url"; public static final String KEY_IMAGE_URL = "image_url";
public static final String KEY_FEED = "feed"; public static final String KEY_FEED = "feed";
public static final String KEY_MEDIA = "media"; public static final String KEY_MEDIA = "media";
@ -183,7 +182,7 @@ public class PodDBAdapter {
private static final String CREATE_TABLE_SIMPLECHAPTERS = "CREATE TABLE " private static final String CREATE_TABLE_SIMPLECHAPTERS = "CREATE TABLE "
+ TABLE_NAME_SIMPLECHAPTERS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE + TABLE_NAME_SIMPLECHAPTERS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_START + " INTEGER," + KEY_FEEDITEM + " INTEGER," + " TEXT," + KEY_START + " INTEGER," + KEY_FEEDITEM + " INTEGER,"
+ KEY_LINK + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)"; + KEY_LINK + " TEXT," + KEY_IMAGE_URL + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)";
// SQL Statements for creating indexes // SQL Statements for creating indexes
static final String CREATE_INDEX_FEEDITEMS_FEED = "CREATE INDEX " static final String CREATE_INDEX_FEEDITEMS_FEED = "CREATE INDEX "
@ -674,6 +673,7 @@ public class PodDBAdapter {
values.put(KEY_START, chapter.getStart()); values.put(KEY_START, chapter.getStart());
values.put(KEY_FEEDITEM, item.getId()); values.put(KEY_FEEDITEM, item.getId());
values.put(KEY_LINK, chapter.getLink()); values.put(KEY_LINK, chapter.getLink());
values.put(KEY_IMAGE_URL, chapter.getImageUrl());
values.put(KEY_CHAPTER_TYPE, chapter.getChapterType()); values.put(KEY_CHAPTER_TYPE, chapter.getChapterType());
if (chapter.getId() == 0) { if (chapter.getId() == 0) {
chapter.setId(db.insert(TABLE_NAME_SIMPLECHAPTERS, null, values)); chapter.setId(db.insert(TABLE_NAME_SIMPLECHAPTERS, null, values));

View File

@ -22,12 +22,12 @@ public class NSSimpleChapters extends Namespace {
private static final String START = "start"; private static final String START = "start";
private static final String TITLE = "title"; private static final String TITLE = "title";
private static final String HREF = "href"; private static final String HREF = "href";
private static final String IMAGE = "image";
@Override @Override
public SyndElement handleElementStart(String localName, HandlerState state, public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) {
Attributes attributes) {
FeedItem currentItem = state.getCurrentItem(); FeedItem currentItem = state.getCurrentItem();
if(currentItem != null) { if (currentItem != null) {
if (localName.equals(CHAPTERS)) { if (localName.equals(CHAPTERS)) {
currentItem.setChapters(new ArrayList<>()); currentItem.setChapters(new ArrayList<>());
} else if (localName.equals(CHAPTER)) { } else if (localName.equals(CHAPTER)) {
@ -35,7 +35,8 @@ public class NSSimpleChapters extends Namespace {
long start = DateUtils.parseTimeString(attributes.getValue(START)); long start = DateUtils.parseTimeString(attributes.getValue(START));
String title = attributes.getValue(TITLE); String title = attributes.getValue(TITLE);
String link = attributes.getValue(HREF); String link = attributes.getValue(HREF);
SimpleChapter chapter = new SimpleChapter(start, title, currentItem, link); String imageUrl = attributes.getValue(IMAGE);
SimpleChapter chapter = new SimpleChapter(start, title, link, imageUrl);
currentItem.getChapters().add(chapter); currentItem.getChapters().add(chapter);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.e(TAG, "Unable to read chapter", e); Log.e(TAG, "Unable to read chapter", e);