summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/danoeh/antennapod')
-rw-r--r--src/de/danoeh/antennapod/PodcastApp.java43
-rw-r--r--src/de/danoeh/antennapod/activity/AudioplayerActivity.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/ChapterListAdapter.java40
-rw-r--r--src/de/danoeh/antennapod/feed/Chapter.java3
-rw-r--r--src/de/danoeh/antennapod/feed/Feed.java1
-rw-r--r--src/de/danoeh/antennapod/feed/FeedItem.java11
-rw-r--r--src/de/danoeh/antennapod/feed/FeedManager.java8
-rw-r--r--src/de/danoeh/antennapod/feed/SimpleChapter.java20
-rw-r--r--src/de/danoeh/antennapod/feed/VorbisCommentChapter.java109
-rw-r--r--src/de/danoeh/antennapod/fragment/FeedlistFragment.java2
-rw-r--r--src/de/danoeh/antennapod/service/PlaybackService.java24
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java7
-rw-r--r--src/de/danoeh/antennapod/storage/DownloadRequester.java61
-rw-r--r--src/de/danoeh/antennapod/syndication/handler/SyndHandler.java2
-rw-r--r--src/de/danoeh/antennapod/syndication/handler/TypeGetter.java28
-rw-r--r--src/de/danoeh/antennapod/syndication/namespace/rss20/NSRSS20.java2
-rw-r--r--src/de/danoeh/antennapod/util/ChapterUtils.java71
-rw-r--r--src/de/danoeh/antennapod/util/FileNameGenerator.java36
-rw-r--r--src/de/danoeh/antennapod/util/NumberGenerator.java21
-rw-r--r--src/de/danoeh/antennapod/util/vorbiscommentreader/OggInputStream.java81
-rw-r--r--src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentChapterReader.java102
-rw-r--r--src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentHeader.java26
-rw-r--r--src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java197
-rw-r--r--src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReaderException.java24
24 files changed, 844 insertions, 77 deletions
diff --git a/src/de/danoeh/antennapod/PodcastApp.java b/src/de/danoeh/antennapod/PodcastApp.java
index 9bca9a5e2..f4d238c60 100644
--- a/src/de/danoeh/antennapod/PodcastApp.java
+++ b/src/de/danoeh/antennapod/PodcastApp.java
@@ -39,9 +39,11 @@ public class PodcastApp extends Application implements
private static float LOGICAL_DENSITY;
private static PodcastApp singleton;
-
+
private boolean displayOnlyEpisodes;
+ private static long currentlyPlayingMediaId;
+
public static PodcastApp getInstance() {
return singleton;
}
@@ -53,7 +55,11 @@ public class PodcastApp extends Application implements
LOGICAL_DENSITY = getResources().getDisplayMetrics().density;
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(this);
- displayOnlyEpisodes = prefs.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
+ displayOnlyEpisodes = prefs.getBoolean(PREF_DISPLAY_ONLY_EPISODES,
+ false);
+ currentlyPlayingMediaId = prefs.getLong(
+ PlaybackService.PREF_CURRENTLY_PLAYING_MEDIA,
+ PlaybackService.NO_MEDIA_PLAYING);
createImportDirectory();
createNoMediaFile();
prefs.registerOnSharedPreferenceChangeListener(this);
@@ -71,7 +77,8 @@ public class PodcastApp extends Application implements
Log.e(TAG, "Could not create .nomedia file");
e.printStackTrace();
}
- if (AppConfig.DEBUG) Log.d(TAG, ".nomedia file created");
+ if (AppConfig.DEBUG)
+ Log.d(TAG, ".nomedia file created");
}
}
@@ -130,11 +137,15 @@ public class PodcastApp extends Application implements
Log.d(TAG, "Automatic update was deactivated");
}
} else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
- if (AppConfig.DEBUG) Log.d(TAG, "PREF_DISPLAY_ONLY_EPISODES changed");
- displayOnlyEpisodes = sharedPreferences.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "PREF_DISPLAY_ONLY_EPISODES changed");
+ displayOnlyEpisodes = sharedPreferences.getBoolean(
+ PREF_DISPLAY_ONLY_EPISODES, false);
} else if (key.equals(PlaybackService.PREF_LAST_PLAYED_ID)) {
- if (AppConfig.DEBUG) Log.d(TAG, "PREF_LAST_PLAYED_ID changed");
- long mediaId = sharedPreferences.getLong(PlaybackService.PREF_AUTODELETE_MEDIA_ID, -1);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "PREF_LAST_PLAYED_ID changed");
+ long mediaId = sharedPreferences.getLong(
+ PlaybackService.PREF_AUTODELETE_MEDIA_ID, -1);
if (mediaId != -1) {
FeedManager manager = FeedManager.getInstance();
FeedMedia media = manager.getFeedMedia(mediaId);
@@ -142,19 +153,33 @@ public class PodcastApp extends Application implements
manager.autoDeleteIfPossible(this, media);
}
}
+ } else if (key.equals(PlaybackService.PREF_CURRENTLY_PLAYING_MEDIA)) {
+ long id = sharedPreferences.getLong(
+ PlaybackService.PREF_CURRENTLY_PLAYING_MEDIA,
+ PlaybackService.NO_MEDIA_PLAYING);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Currently playing media set to " + id);
+ if (id != currentlyPlayingMediaId) {
+ currentlyPlayingMediaId = id;
+ }
}
}
public static float getLogicalDensity() {
return LOGICAL_DENSITY;
}
-
+
public boolean displayOnlyEpisodes() {
return displayOnlyEpisodes;
}
+ public static long getCurrentlyPlayingMediaId() {
+ return currentlyPlayingMediaId;
+ }
+
public boolean isLargeScreen() {
- return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE || (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE
+ || (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
}
diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 8d0b4a0f5..d6299e602 100644
--- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -140,7 +140,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
};
sCChapterFragment.setListAdapter(new ChapterListAdapter(
- activity, 0, media.getItem().getChapters()));
+ activity, 0, media.getItem().getChapters(), media));
return sCChapterFragment;
default:
diff --git a/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
index 16a868437..9357d0659 100644
--- a/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
@@ -21,15 +21,21 @@ import android.widget.ArrayAdapter;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.util.Converter;
public class ChapterListAdapter extends ArrayAdapter<Chapter> {
private static final String TAG = "ChapterListAdapter";
+ private List<Chapter> chapters;
+ private FeedMedia media;
+
public ChapterListAdapter(Context context, int textViewResourceId,
- List<Chapter> objects) {
+ List<Chapter> objects, FeedMedia media) {
super(context, textViewResourceId, objects);
+ this.chapters = objects;
+ this.media = media;
}
@Override
@@ -181,4 +187,36 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
}
};
+
+ @Override
+ public int getCount() {
+ // ignore invalid chapters
+ int counter = 0;
+ for (Chapter chapter : chapters) {
+ if (!ignoreChapter(chapter)) {
+ counter++;
+ }
+ }
+ return counter;
+ }
+
+ private boolean ignoreChapter(Chapter c) {
+ return media.getDuration() > 0 && media.getDuration() < c.getStart();
+ }
+
+ @Override
+ public Chapter getItem(int position) {
+ int i = 0;
+ for (Chapter chapter : chapters) {
+ if (!ignoreChapter(chapter)) {
+ if (i == position) {
+ return chapter;
+ } else {
+ i++;
+ }
+ }
+ }
+ return super.getItem(position);
+ }
+
}
diff --git a/src/de/danoeh/antennapod/feed/Chapter.java b/src/de/danoeh/antennapod/feed/Chapter.java
index f50053f93..10575e03d 100644
--- a/src/de/danoeh/antennapod/feed/Chapter.java
+++ b/src/de/danoeh/antennapod/feed/Chapter.java
@@ -8,6 +8,9 @@ public abstract class Chapter extends FeedComponent {
protected FeedItem item;
protected String link;
+ public Chapter() {
+ }
+
public Chapter(long start) {
super();
this.start = start;
diff --git a/src/de/danoeh/antennapod/feed/Feed.java b/src/de/danoeh/antennapod/feed/Feed.java
index c3677447b..55256ea31 100644
--- a/src/de/danoeh/antennapod/feed/Feed.java
+++ b/src/de/danoeh/antennapod/feed/Feed.java
@@ -17,6 +17,7 @@ import de.danoeh.antennapod.PodcastApp;
public class Feed extends FeedFile {
public static final int FEEDFILETYPE_FEED = 0;
public static final String TYPE_RSS2 = "rss";
+ public static final String TYPE_RSS091 = "rss";
public static final String TYPE_ATOM1 = "atom";
private String title;
diff --git a/src/de/danoeh/antennapod/feed/FeedItem.java b/src/de/danoeh/antennapod/feed/FeedItem.java
index ebfff80ad..6227298df 100644
--- a/src/de/danoeh/antennapod/feed/FeedItem.java
+++ b/src/de/danoeh/antennapod/feed/FeedItem.java
@@ -4,6 +4,8 @@ import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.List;
+import de.danoeh.antennapod.PodcastApp;
+
/**
* Data Object for a XML message
*
@@ -184,6 +186,15 @@ public class FeedItem extends FeedComponent {
this.itemIdentifier = itemIdentifier;
}
+ public boolean isPlaying() {
+ if (media != null) {
+ if (PodcastApp.getCurrentlyPlayingMediaId() == media.getId()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void setCachedDescription(String d) {
cachedDescription = new SoftReference<String>(d);
}
diff --git a/src/de/danoeh/antennapod/feed/FeedManager.java b/src/de/danoeh/antennapod/feed/FeedManager.java
index 285fc3539..7f4a1c5aa 100644
--- a/src/de/danoeh/antennapod/feed/FeedManager.java
+++ b/src/de/danoeh/antennapod/feed/FeedManager.java
@@ -39,7 +39,7 @@ import de.danoeh.antennapod.util.comparator.PlaybackCompletionDateComparator;
public class FeedManager {
private static final String TAG = "FeedManager";
- public static final String ACITON_FEED_LIST_UPDATE = "de.danoeh.antennapod.action.feed.feedlistUpdate";
+ public static final String ACTION_FEED_LIST_UPDATE = "de.danoeh.antennapod.action.feed.feedlistUpdate";
public static final String ACTION_UNREAD_ITEMS_UPDATE = "de.danoeh.antennapod.action.feed.unreadItemsUpdate";
public static final String ACTION_QUEUE_UPDATE = "de.danoeh.antennapod.action.feed.queueUpdate";
public static final String ACTION_DOWNLOADLOG_UPDATE = "de.danoeh.antennapod.action.feed.downloadLogUpdate";
@@ -264,7 +264,7 @@ public class FeedManager {
}
private void sendFeedUpdateBroadcast(Context context) {
- context.sendBroadcast(new Intent(ACITON_FEED_LIST_UPDATE));
+ context.sendBroadcast(new Intent(ACTION_FEED_LIST_UPDATE));
}
private void sendPlaybackHistoryUpdateBroadcast(Context context) {
@@ -1198,6 +1198,10 @@ public class FeedManager {
chapter = new ID3Chapter(start, title, item,
link);
break;
+ case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
+ chapter = new VorbisCommentChapter(start,
+ title, item, link);
+ break;
}
chapter.setId(chapterCursor
.getLong(PodDBAdapter.KEY_ID_INDEX));
diff --git a/src/de/danoeh/antennapod/feed/SimpleChapter.java b/src/de/danoeh/antennapod/feed/SimpleChapter.java
index 2462497ef..7b74f28f6 100644
--- a/src/de/danoeh/antennapod/feed/SimpleChapter.java
+++ b/src/de/danoeh/antennapod/feed/SimpleChapter.java
@@ -7,26 +7,6 @@ public class SimpleChapter extends Chapter {
super(start, title, item, link);
}
- public String getTitle() {
- return title;
- }
-
- public FeedItem getItem() {
- return item;
- }
-
- public long getStart() {
- return start;
- }
-
- public void setItem(FeedItem item) {
- this.item = item;
- }
-
- public String getLink() {
- return link;
- }
-
@Override
public int getChapterType() {
return CHAPTERTYPE_SIMPLECHAPTER;
diff --git a/src/de/danoeh/antennapod/feed/VorbisCommentChapter.java b/src/de/danoeh/antennapod/feed/VorbisCommentChapter.java
new file mode 100644
index 000000000..544e762d3
--- /dev/null
+++ b/src/de/danoeh/antennapod/feed/VorbisCommentChapter.java
@@ -0,0 +1,109 @@
+package de.danoeh.antennapod.feed;
+
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.util.vorbiscommentreader.VorbisCommentReaderException;
+
+public class VorbisCommentChapter extends Chapter {
+ public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
+
+ private static final int CHAPTERXXX_LENGTH = "chapterxxx".length();
+
+ private int vorbisCommentId;
+
+ public VorbisCommentChapter(int vorbisCommentId) {
+ this.vorbisCommentId = vorbisCommentId;
+ }
+
+ public VorbisCommentChapter(long start, String title, FeedItem item,
+ String link) {
+ super(start, title, item, link);
+ }
+
+ @Override
+ public String toString() {
+ return "VorbisCommentChapter [id=" + id + ", title=" + title
+ + ", link=" + link + ", start=" + start + "]";
+ }
+
+ public static long getStartTimeFromValue(String value)
+ throws VorbisCommentReaderException {
+ String[] parts = value.split(":");
+ if (parts.length >= 3) {
+ try {
+ long hours = TimeUnit.MILLISECONDS.convert(
+ Long.parseLong(parts[0]), TimeUnit.HOURS);
+ long minutes = TimeUnit.MILLISECONDS.convert(
+ Long.parseLong(parts[1]), TimeUnit.MINUTES);
+ if (parts[2].contains("-->")) {
+ parts[2] = parts[2].substring(0, parts[2].indexOf("-->"));
+ }
+ long seconds = TimeUnit.MILLISECONDS.convert(
+ ((long) Float.parseFloat(parts[2])), TimeUnit.SECONDS);
+ return hours + minutes + seconds;
+ } catch (NumberFormatException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ } else {
+ throw new VorbisCommentReaderException("Invalid time string");
+ }
+ }
+
+ /**
+ * 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.
+ * @throws VorbisCommentReaderException
+ * */
+ public static int getIDFromKey(String key)
+ throws VorbisCommentReaderException {
+ if (key.length() >= CHAPTERXXX_LENGTH) { // >= CHAPTERxxx
+ try {
+ String strId = key.substring(8, 10);
+ return Integer.parseInt(strId);
+ } catch (NumberFormatException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ }
+ throw new VorbisCommentReaderException("key is too short (" + key + ")");
+ }
+
+ /**
+ * Get the string that comes after 'CHAPTERxxx', for example 'name' or
+ * 'url'.
+ */
+ public static String getAttributeTypeFromKey(String key) {
+ if (key.length() > CHAPTERXXX_LENGTH) {
+ return key.substring(CHAPTERXXX_LENGTH, key.length());
+ }
+ return null;
+ }
+
+ @Override
+ public int getChapterType() {
+ 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;
+ }
+
+
+
+}
diff --git a/src/de/danoeh/antennapod/fragment/FeedlistFragment.java b/src/de/danoeh/antennapod/fragment/FeedlistFragment.java
index 1a205f521..caf6c6a7f 100644
--- a/src/de/danoeh/antennapod/fragment/FeedlistFragment.java
+++ b/src/de/danoeh/antennapod/fragment/FeedlistFragment.java
@@ -116,7 +116,7 @@ public class FeedlistFragment extends SherlockFragment implements
IntentFilter filter = new IntentFilter();
filter.addAction(DownloadRequester.ACTION_DOWNLOAD_QUEUED);
filter.addAction(FeedManager.ACTION_UNREAD_ITEMS_UPDATE);
- filter.addAction(FeedManager.ACITON_FEED_LIST_UPDATE);
+ filter.addAction(FeedManager.ACTION_FEED_LIST_UPDATE);
filter.addAction(DownloadService.ACTION_DOWNLOAD_HANDLED);
pActivity.registerReceiver(contentUpdate, filter);
fla.notifyDataSetChanged();
diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java
index 98721877e..0bf7ab075 100644
--- a/src/de/danoeh/antennapod/service/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/PlaybackService.java
@@ -58,6 +58,12 @@ public class PlaybackService extends Service {
public static final String PREF_LAST_PLAYED_ID = "de.danoeh.antennapod.preferences.lastPlayedId";
/** Contains the feed id of the last played item. */
public static final String PREF_LAST_PLAYED_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
+ /**
+ * ID of the media object that is currently being played. This preference is
+ * set to NO_MEDIA_PLAYING after playback has been completed and is set as
+ * soon as the 'play' button is pressed.
+ */
+ public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
/** True if last played media was streamed. */
public static final String PREF_LAST_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
/** True if last played media was a video. */
@@ -159,6 +165,9 @@ public class PlaybackService extends Service {
/** True if mediaplayer was paused because it lost audio focus temporarily */
private boolean pausedBecauseOfTransientAudiofocusLoss;
+ /** Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. */
+ public static final long NO_MEDIA_PLAYING = -1;
+
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
@@ -602,6 +611,11 @@ public class PlaybackService extends Service {
ChapterUtils
.readID3ChaptersFromFeedMediaDownloadUrl(media
.getItem());
+ if (media.getItem().getChapters() == null) {
+ ChapterUtils
+ .readOggChaptersFromMediaDownloadUrl(media
+ .getItem());
+ }
if (media.getItem().getChapters() != null
&& !interrupted()) {
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
@@ -657,6 +671,7 @@ public class PlaybackService extends Service {
pause(true, true);
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
+ setCurrentlyPlayingMedia(NO_MEDIA_PLAYING);
stopSelf();
return true;
}
@@ -692,6 +707,7 @@ public class PlaybackService extends Service {
autoDeleteMediaId = -1;
}
SharedPreferences.Editor editor = prefs.edit();
+ editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING);
editor.putLong(PREF_AUTODELETE_MEDIA_ID, autoDeleteMediaId);
editor.putBoolean(PREF_AUTO_DELETE_MEDIA_PLAYBACK_COMPLETED, true);
editor.commit();
@@ -794,6 +810,7 @@ public class PlaybackService extends Service {
if (AppConfig.DEBUG)
Log.d(TAG, "Stopping playback");
player.stop();
+ setCurrentlyPlayingMedia(NO_MEDIA_PLAYING);
stopSelf();
}
@@ -834,6 +851,7 @@ public class PlaybackService extends Service {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext())
.edit();
+ editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, media.getId());
editor.putLong(PREF_LAST_PLAYED_FEED_ID, feed.getId());
editor.putBoolean(PREF_LAST_IS_STREAM, shouldStream);
editor.putBoolean(PREF_LAST_IS_VIDEO, playingVideo);
@@ -1262,4 +1280,10 @@ public class PlaybackService extends Service {
}
}
+ private void setCurrentlyPlayingMedia(long id) {
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+ editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, id);
+ editor.commit();
+ }
}
diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index e2709d141..6da651838 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -632,7 +632,8 @@ public class DownloadService extends Service {
return false;
}
if (item.getPubDate() == null) {
- Log.e(TAG, "Item has no pubDate. Using current time as pubDate");
+ Log.e(TAG,
+ "Item has no pubDate. Using current time as pubDate");
if (item.getTitle() != null) {
Log.e(TAG, "Title of invalid item: " + item.getTitle());
}
@@ -720,6 +721,10 @@ public class DownloadService extends Service {
if (media.getItem().getChapters() == null) {
ChapterUtils.readID3ChaptersFromFeedMediaFileUrl(media
.getItem());
+ if (media.getItem().getChapters() == null) {
+ ChapterUtils.readOggChaptersFromMediaFileUrl(media
+ .getItem());
+ }
if (media.getItem().getChapters() != null) {
chaptersRead = true;
}
diff --git a/src/de/danoeh/antennapod/storage/DownloadRequester.java b/src/de/danoeh/antennapod/storage/DownloadRequester.java
index 928b923fd..0201cc542 100644
--- a/src/de/danoeh/antennapod/storage/DownloadRequester.java
+++ b/src/de/danoeh/antennapod/storage/DownloadRequester.java
@@ -4,6 +4,8 @@ import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.commons.io.FilenameUtils;
+
import android.content.Context;
import android.content.Intent;
import android.util.Log;
@@ -14,7 +16,7 @@ import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.service.download.DownloadService;
-import de.danoeh.antennapod.util.NumberGenerator;
+import de.danoeh.antennapod.util.FileNameGenerator;
import de.danoeh.antennapod.util.URLChecker;
public class DownloadRequester {
@@ -44,12 +46,40 @@ public class DownloadRequester {
return downloader;
}
- private void download(Context context, FeedFile item, File dest) {
+ private void download(Context context, FeedFile item, File dest,
+ boolean overwriteIfExists) {
if (!isDownloadingFile(item)) {
if (dest.exists()) {
if (AppConfig.DEBUG)
- Log.d(TAG, "File already exists. Deleting !");
- dest.delete();
+ Log.d(TAG, "File already exists.");
+ if (overwriteIfExists) {
+ boolean result = dest.delete();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Deleting file. Result: " + result);
+ } else {
+ // find different name
+ File newDest = null;
+ for (int i = 1; i < Integer.MAX_VALUE; i++) {
+ String newName = FilenameUtils.getBaseName(dest
+ .getName())
+ + "-"
+ + i
+ + "."
+ + FilenameUtils.getExtension(dest.getName());
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Testing filename " + newName);
+ newDest = new File(dest.getParent(), newName);
+ if (!newDest.exists()) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "File doesn't exist yet. Using "
+ + newName);
+ break;
+ }
+ }
+ if (newDest != null) {
+ dest = newDest;
+ }
+ }
}
if (AppConfig.DEBUG)
Log.d(TAG,
@@ -82,7 +112,7 @@ public class DownloadRequester {
throws DownloadRequestException {
if (feedFileValid(feed)) {
download(context, feed, new File(getFeedfilePath(context),
- getFeedfileName(feed)));
+ getFeedfileName(feed)), true);
}
}
@@ -90,15 +120,16 @@ public class DownloadRequester {
throws DownloadRequestException {
if (feedFileValid(image)) {
download(context, image, new File(getImagefilePath(context),
- getImagefileName(image)));
+ getImagefileName(image)), true);
}
}
- public void downloadMedia(Context context, FeedMedia feedmedia) throws DownloadRequestException {
+ public void downloadMedia(Context context, FeedMedia feedmedia)
+ throws DownloadRequestException {
if (feedFileValid(feedmedia)) {
download(context, feedmedia,
new File(getMediafilePath(context, feedmedia),
- getMediafilename(feedmedia)));
+ getMediafilename(feedmedia)), false);
}
}
@@ -199,7 +230,11 @@ public class DownloadRequester {
}
public String getFeedfileName(Feed feed) {
- return "feed-" + NumberGenerator.generateLong(feed.getDownload_url());
+ String filename = feed.getDownload_url();
+ if (feed.getTitle() != null && !feed.getTitle().isEmpty()) {
+ filename = feed.getTitle();
+ }
+ return "feed-" + FileNameGenerator.generateFileName(filename);
}
public String getImagefilePath(Context context)
@@ -209,7 +244,11 @@ public class DownloadRequester {
}
public String getImagefileName(FeedImage image) {
- return "image-" + NumberGenerator.generateLong(image.getDownload_url());
+ String filename = image.getDownload_url();
+ if (image.getFeed() != null && image.getFeed().getTitle() != null) {
+ filename = image.getFeed().getTitle();
+ }
+ return "image-" + FileNameGenerator.generateFileName(filename);
}
public String getMediafilePath(Context context, FeedMedia media)
@@ -217,7 +256,7 @@ public class DownloadRequester {
File externalStorage = getExternalFilesDirOrThrowException(
context,
MEDIA_DOWNLOADPATH
- + NumberGenerator.generateLong(media.getItem()
+ + FileNameGenerator.generateFileName(media.getItem()
.getFeed().getTitle()) + "/");
return externalStorage.toString();
}
diff --git a/src/de/danoeh/antennapod/syndication/handler/SyndHandler.java b/src/de/danoeh/antennapod/syndication/handler/SyndHandler.java
index ff7942cdb..55757ce18 100644
--- a/src/de/danoeh/antennapod/syndication/handler/SyndHandler.java
+++ b/src/de/danoeh/antennapod/syndication/handler/SyndHandler.java
@@ -23,7 +23,7 @@ public class SyndHandler extends DefaultHandler {
public SyndHandler(Feed feed, TypeGetter.Type type) {
state = new HandlerState(feed);
- if (type == TypeGetter.Type.RSS20) {
+ if (type == TypeGetter.Type.RSS20 || type == TypeGetter.Type.RSS091) {
state.defaultNamespaces.push(new NSRSS20());
}
}
diff --git a/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java b/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
index a2766526d..d2454f2b9 100644
--- a/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
+++ b/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
@@ -19,7 +19,7 @@ public class TypeGetter {
private static final String TAG = "TypeGetter";
enum Type {
- RSS20, ATOM, INVALID
+ RSS20, RSS091, ATOM, INVALID
}
private static final String ATOM_ROOT = "feed";
@@ -43,13 +43,25 @@ public class TypeGetter {
if (AppConfig.DEBUG)
Log.d(TAG, "Recognized type Atom");
return Type.ATOM;
- } else if (tag.equals(RSS_ROOT)
- && (xpp.getAttributeValue(null, "version")
- .equals("2.0"))) {
- feed.setType(Feed.TYPE_RSS2);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Recognized type RSS 2.0");
- return Type.RSS20;
+ } else if (tag.equals(RSS_ROOT)) {
+ String strVersion = xpp.getAttributeValue(null,
+ "version");
+ if (strVersion != null) {
+
+ if (strVersion.equals("2.0")) {
+ feed.setType(Feed.TYPE_RSS2);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Recognized type RSS 2.0");
+ return Type.RSS20;
+ } else if (strVersion.equals("0.91")
+ || strVersion.equals("0.92")) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG,
+ "Recognized type RSS 0.91/0.92");
+ return Type.RSS091;
+ }
+ }
+ throw new UnsupportedFeedtypeException(Type.INVALID);
} else {
if (AppConfig.DEBUG)
Log.d(TAG, "Type is invalid");
diff --git a/src/de/danoeh/antennapod/syndication/namespace/rss20/NSRSS20.java b/src/de/danoeh/antennapod/syndication/namespace/rss20/NSRSS20.java
index 388e1540e..696fef6e1 100644
--- a/src/de/danoeh/antennapod/syndication/namespace/rss20/NSRSS20.java
+++ b/src/de/danoeh/antennapod/syndication/namespace/rss20/NSRSS20.java
@@ -101,7 +101,7 @@ public class NSRSS20 extends Namespace {
} else if (second.equals(CHANNEL)) {
state.getFeed().setTitle(content);
} else if (second.equals(IMAGE) && third != null && third.equals(CHANNEL)) {
- state.getFeed().getImage().setTitle(IMAGE);
+ state.getFeed().getImage().setTitle(content);
}
} else if (top.equals(LINK)) {
if (second.equals(CHANNEL)) {
diff --git a/src/de/danoeh/antennapod/util/ChapterUtils.java b/src/de/danoeh/antennapod/util/ChapterUtils.java
index bf88d8fd1..3131b9e9a 100644
--- a/src/de/danoeh/antennapod/util/ChapterUtils.java
+++ b/src/de/danoeh/antennapod/util/ChapterUtils.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -10,6 +11,8 @@ import java.net.URL;
import java.util.Collections;
import java.util.List;
+import org.apache.commons.io.IOUtils;
+
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.feed.Chapter;
@@ -18,6 +21,8 @@ import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.util.comparator.ChapterStartTimeComparator;
import de.danoeh.antennapod.util.id3reader.ChapterReader;
import de.danoeh.antennapod.util.id3reader.ID3ReaderException;
+import de.danoeh.antennapod.util.vorbiscommentreader.VorbisCommentChapterReader;
+import de.danoeh.antennapod.util.vorbiscommentreader.VorbisCommentReaderException;
/** Utility class for getting chapter data from media files. */
public class ChapterUtils {
@@ -131,6 +136,72 @@ public class ChapterUtils {
}
}
+ public static void readOggChaptersFromMediaDownloadUrl(FeedItem item) {
+ final FeedMedia media = item.getMedia();
+ if (media != null && media.getDownload_url() != null) {
+ InputStream input = null;
+ try {
+ URL url = new URL(media.getDownload_url());
+ input = url.openStream();
+ if (input != null) {
+ readOggChaptersFromInputStream(item, input);
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+ }
+
+ public static void readOggChaptersFromMediaFileUrl(FeedItem item) {
+ final FeedMedia media = item.getMedia();
+ if (media != null && media.getFile_url() != null) {
+ File source = new File(media.getFile_url());
+ if (source.exists()) {
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(new FileInputStream(source));
+ readOggChaptersFromInputStream(item, input);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+ }
+ }
+
+ private static void readOggChaptersFromInputStream(FeedItem item,
+ InputStream input) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG,
+ "Trying to read chapters from item with title "
+ + item.getTitle());
+ try {
+ VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
+ reader.readInputStream(input);
+ List<Chapter> chapters = reader.getChapters();
+ if (chapters != null) {
+ Collections.sort(chapters, new ChapterStartTimeComparator());
+ processChapters(chapters, item);
+ if (chaptersValid(chapters)) {
+ item.setChapters(chapters);
+ Log.i(TAG, "Chapters loaded");
+ } else {
+ Log.e(TAG, "Chapter data was invalid");
+ }
+ } else {
+ Log.i(TAG,
+ "ChapterReader could not find any Ogg vorbis chapters");
+ }
+ } catch (VorbisCommentReaderException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Makes sure that chapter does a title and an item attribute. */
private static void processChapters(List<Chapter> chapters, FeedItem item) {
for (int i = 0; i < chapters.size(); i++) {
diff --git a/src/de/danoeh/antennapod/util/FileNameGenerator.java b/src/de/danoeh/antennapod/util/FileNameGenerator.java
new file mode 100644
index 000000000..3bc193080
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/FileNameGenerator.java
@@ -0,0 +1,36 @@
+package de.danoeh.antennapod.util;
+
+import java.util.Arrays;
+
+/** Generates valid filenames for a given string. */
+public class FileNameGenerator {
+
+ private static final char[] ILLEGAL_CHARACTERS = { '/', '\\', '?', '%',
+ '*', ':', '|', '"', '<', '>' };
+ static {
+ Arrays.sort(ILLEGAL_CHARACTERS);
+ }
+
+ private FileNameGenerator() {
+
+ }
+
+ /**
+ * This method will return a new string that doesn't contain any illegal
+ * characters of the given string.
+ */
+ public static String generateFileName(String string) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < string.length(); i++) {
+ char c = string.charAt(i);
+ if (Arrays.binarySearch(ILLEGAL_CHARACTERS, c) < 0) {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ }
+
+ public static long generateLong(final String str) {
+ return str.hashCode();
+ }
+}
diff --git a/src/de/danoeh/antennapod/util/NumberGenerator.java b/src/de/danoeh/antennapod/util/NumberGenerator.java
deleted file mode 100644
index ff89180e1..000000000
--- a/src/de/danoeh/antennapod/util/NumberGenerator.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.danoeh.antennapod.util;
-
-
-/**Utility class for creating numbers.*/
-public final class NumberGenerator {
- /** Class shall not be instantiated.*/
- private NumberGenerator() {
- }
-
- /**Logging tag.*/
- private static final String TAG = "NumberGenerator";
-
- /** Takes a string and generates a random value out of
- * the hash-value of that string.
- * @param strSeed The string to take for the return value
- * @return The generated random value
- * */
- public static long generateLong(final String strSeed) {
- return strSeed.hashCode();
- }
-}
diff --git a/src/de/danoeh/antennapod/util/vorbiscommentreader/OggInputStream.java b/src/de/danoeh/antennapod/util/vorbiscommentreader/OggInputStream.java
new file mode 100644
index 000000000..e3de5971f
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/vorbiscommentreader/OggInputStream.java
@@ -0,0 +1,81 @@
+package de.danoeh.antennapod.util.vorbiscommentreader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Scanner;
+
+import org.apache.commons.io.IOUtils;
+
+public class OggInputStream extends InputStream {
+ private InputStream input;
+
+ /** True if OggInputStream is currently inside an Ogg page. */
+ private boolean isInPage;
+ private long bytesLeft;
+
+ public OggInputStream(InputStream input) {
+ super();
+ isInPage = false;
+ this.input = input;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!isInPage) {
+ readOggPage();
+ }
+
+ if (isInPage && bytesLeft > 0) {
+ int result = input.read();
+ bytesLeft -= 1;
+ if (bytesLeft == 0) {
+ isInPage = false;
+ }
+ return result;
+ }
+ return -1;
+ }
+
+ private void readOggPage() throws IOException {
+ // find OggS
+ int[] buffer = new int[4];
+ int c = 0;
+ boolean isInOggS = false;
+ while ((c = input.read()) != -1) {
+ switch (c) {
+ case 'O':
+ isInOggS = true;
+ buffer[0] = c;
+ break;
+ case 'g':
+ if (buffer[1] != c) {
+ buffer[1] = c;
+ } else {
+ buffer[2] = c;
+ }
+ break;
+ case 'S':
+ buffer[3] = c;
+ break;
+ default:
+ if (isInOggS) {
+ Arrays.fill(buffer, 0);
+ isInOggS = false;
+ }
+ }
+ if (buffer[0] == 'O' && buffer[1] == 'g' && buffer[2] == 'g'
+ && buffer[3] == 'S') {
+ break;
+ }
+ }
+ // read segments
+ IOUtils.skipFully(input, 22);
+ bytesLeft = 0;
+ int numSegments = input.read();
+ for (int i = 0; i < numSegments; i++) {
+ bytesLeft += input.read();
+ }
+ isInPage = true;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentChapterReader.java b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentChapterReader.java
new file mode 100644
index 000000000..190d7f2b5
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentChapterReader.java
@@ -0,0 +1,102 @@
+package de.danoeh.antennapod.util.vorbiscommentreader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.util.Log;
+
+import de.danoeh.antennapod.AppConfig;
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.VorbisCommentChapter;
+
+public class VorbisCommentChapterReader extends VorbisCommentReader {
+ private static final String TAG = "VorbisCommentChapterReader";
+
+ private static final String CHAPTER_KEY = "chapter\\d\\d\\d.*";
+ private static final String CHAPTER_ATTRIBUTE_TITLE = "name";
+ private static final String CHAPTER_ATTRIBUTE_LINK = "url";
+
+ private List<Chapter> chapters;
+
+ public VorbisCommentChapterReader() {
+ }
+
+ @Override
+ public void onVorbisCommentFound() {
+ System.out.println("Vorbis comment found");
+ }
+
+ @Override
+ public void onVorbisCommentHeaderFound(VorbisCommentHeader header) {
+ chapters = new ArrayList<Chapter>();
+ System.out.println(header.toString());
+ }
+
+ @Override
+ public boolean onContentVectorKey(String content) {
+ return content.matches(CHAPTER_KEY);
+ }
+
+ @Override
+ public void onContentVectorValue(String key, String value)
+ throws VorbisCommentReaderException {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Key: " + key + ", value: " + value);
+ String attribute = VorbisCommentChapter.getAttributeTypeFromKey(key);
+ int id = VorbisCommentChapter.getIDFromKey(key);
+ Chapter chapter = getChapterById(id);
+ if (attribute == null) {
+ if (getChapterById(id) == null) {
+ // new chapter
+ long start = VorbisCommentChapter.getStartTimeFromValue(value);
+ chapter = new VorbisCommentChapter(id);
+ chapter.setStart(start);
+ chapters.add(chapter);
+ } else {
+ throw new VorbisCommentReaderException(
+ "Found chapter with duplicate ID (" + key + ", "
+ + value + ")");
+ }
+ } else if (attribute.equals(CHAPTER_ATTRIBUTE_TITLE)) {
+ if (chapter != null) {
+ chapter.setTitle(value);
+ }
+ } else if (attribute.equals(CHAPTER_ATTRIBUTE_LINK)) {
+ if (chapter != null) {
+ chapter.setLink(value);
+ }
+ }
+ }
+
+ @Override
+ public void onNoVorbisCommentFound() {
+ System.out.println("No vorbis comment found");
+ }
+
+ @Override
+ public void onEndOfComment() {
+ System.out.println("End of comment");
+ for (Chapter c : chapters) {
+ System.out.println(c.toString());
+ }
+ }
+
+ @Override
+ public void onError(VorbisCommentReaderException exception) {
+ exception.printStackTrace();
+ }
+
+ private Chapter getChapterById(long id) {
+ for (Chapter c : chapters) {
+ if (((VorbisCommentChapter) c).getVorbisCommentId() == id) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public List<Chapter> getChapters() {
+ return chapters;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentHeader.java b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentHeader.java
new file mode 100644
index 000000000..8c47393c9
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentHeader.java
@@ -0,0 +1,26 @@
+package de.danoeh.antennapod.util.vorbiscommentreader;
+public class VorbisCommentHeader {
+ private String vendorString;
+ private long userCommentLength;
+
+ public VorbisCommentHeader(String vendorString, long userCommentLength) {
+ super();
+ this.vendorString = vendorString;
+ this.userCommentLength = userCommentLength;
+ }
+
+ @Override
+ public String toString() {
+ return "VorbisCommentHeader [vendorString=" + vendorString
+ + ", userCommentLength=" + userCommentLength + "]";
+ }
+
+ public String getVendorString() {
+ return vendorString;
+ }
+
+ public long getUserCommentLength() {
+ return userCommentLength;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java
new file mode 100644
index 000000000..ceaa8d5cd
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java
@@ -0,0 +1,197 @@
+package de.danoeh.antennapod.util.vorbiscommentreader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.util.Arrays;
+
+import org.apache.commons.io.EndianUtils;
+import org.apache.commons.io.IOUtils;
+
+
+public abstract class VorbisCommentReader {
+ /** Length of first page in an ogg file in bytes. */
+ private static final int FIRST_PAGE_LENGTH = 58;
+ private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024;
+ private static final int PACKET_TYPE_IDENTIFICATION = 1;
+ private static final int PACKET_TYPE_COMMENT = 3;
+
+ /** Called when Reader finds identification header. */
+ public abstract void onVorbisCommentFound();
+
+ public abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
+
+ /**
+ * Is called every time the Reader finds a content vector. The handler
+ * should return true if it wants to handle the content vector.
+ */
+ public abstract boolean onContentVectorKey(String content);
+
+ /**
+ * Is called if onContentVectorKey returned true for the key.
+ *
+ * @throws VorbisCommentReaderException
+ */
+ public abstract void onContentVectorValue(String key, String value)
+ throws VorbisCommentReaderException;
+
+ public abstract void onNoVorbisCommentFound();
+
+ public abstract void onEndOfComment();
+
+ public abstract void onError(VorbisCommentReaderException exception);
+
+ public void readInputStream(InputStream input)
+ throws VorbisCommentReaderException {
+ try {
+ // look for identification header
+ if (findIdentificationHeader(input)) {
+
+ onVorbisCommentFound();
+ input = new OggInputStream(input);
+ if (findCommentHeader(input)) {
+ VorbisCommentHeader commentHeader = readCommentHeader(input);
+ if (commentHeader != null) {
+ onVorbisCommentHeaderFound(commentHeader);
+ for (int i = 0; i < commentHeader
+ .getUserCommentLength(); i++) {
+ try {
+ long vectorLength = EndianUtils
+ .readSwappedUnsignedInteger(input);
+ String key = readContentVectorKey(input,
+ vectorLength).toLowerCase();
+ boolean readValue = onContentVectorKey(key);
+ if (readValue) {
+ String value = readUTF8String(
+ input,
+ (int) (vectorLength - key.length() - 1));
+ onContentVectorValue(key, value);
+ } else {
+ IOUtils.skipFully(input,
+ vectorLength - key.length() - 1);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ onEndOfComment();
+ }
+
+ } else {
+ onError(new VorbisCommentReaderException(
+ "No comment header found"));
+ }
+ } else {
+ onNoVorbisCommentFound();
+ }
+ } catch (IOException e) {
+ onError(new VorbisCommentReaderException(e));
+ }
+ }
+
+ private String readUTF8String(InputStream input, long length)
+ throws IOException {
+ byte[] buffer = new byte[(int) length];
+
+ IOUtils.readFully(input, buffer);
+ Charset charset = Charset.forName("UTF-8");
+ return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString();
+ }
+
+ /**
+ * Looks for an identification header in the first page of the file. If an
+ * identification header is found, it will be skipped completely and the
+ * method will return true, otherwise false.
+ *
+ * @throws IOException
+ */
+ private boolean findIdentificationHeader(InputStream input)
+ throws IOException {
+ byte[] buffer = new byte[FIRST_PAGE_LENGTH];
+ IOUtils.readFully(input, buffer);
+ int i;
+ for (i = 6; i < buffer.length; i++) {
+ if (buffer[i - 5] == 'v' && buffer[i - 4] == 'o'
+ && buffer[i - 3] == 'r' && buffer[i - 2] == 'b'
+ && buffer[i - 1] == 'i' && buffer[i] == 's'
+ && buffer[i - 6] == PACKET_TYPE_IDENTIFICATION) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean findCommentHeader(InputStream input) throws IOException {
+ char[] buffer = new char["vorbis".length() + 1];
+ for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
+ char c = (char) input.read();
+ int dest = -1;
+ switch (c) {
+ case PACKET_TYPE_COMMENT:
+ dest = 0;
+ break;
+ case 'v':
+ dest = 1;
+ break;
+ case 'o':
+ dest = 2;
+ break;
+ case 'r':
+ dest = 3;
+ break;
+ case 'b':
+ dest = 4;
+ break;
+ case 'i':
+ dest = 5;
+ break;
+ case 's':
+ dest = 6;
+ break;
+ }
+ if (dest >= 0) {
+ buffer[dest] = c;
+ if (buffer[1] == 'v' && buffer[2] == 'o' && buffer[3] == 'r'
+ && buffer[4] == 'b' && buffer[5] == 'i'
+ && buffer[6] == 's' && buffer[0] == PACKET_TYPE_COMMENT) {
+ return true;
+ }
+ } else {
+ Arrays.fill(buffer, (char) 0);
+ }
+ }
+ return false;
+ }
+
+ private VorbisCommentHeader readCommentHeader(InputStream input)
+ throws IOException, VorbisCommentReaderException {
+ try {
+ long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
+ String vendorName = readUTF8String(input, vendorLength);
+ long userCommentLength = EndianUtils
+ .readSwappedUnsignedInteger(input);
+ return new VorbisCommentHeader(vendorName, userCommentLength);
+ } catch (UnsupportedEncodingException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ }
+
+ private String readContentVectorKey(InputStream input, long vectorLength)
+ throws IOException {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < vectorLength; i++) {
+ char c = (char) input.read();
+ if (c == '=') {
+ return buffer.toString();
+ } else {
+ buffer.append(c);
+ }
+ }
+ return null; // no key found
+ }
+}
diff --git a/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReaderException.java b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReaderException.java
new file mode 100644
index 000000000..574373241
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReaderException.java
@@ -0,0 +1,24 @@
+package de.danoeh.antennapod.util.vorbiscommentreader;
+public class VorbisCommentReaderException extends Exception {
+
+ public VorbisCommentReaderException() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public VorbisCommentReaderException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ // TODO Auto-generated constructor stub
+ }
+
+ public VorbisCommentReaderException(String arg0) {
+ super(arg0);
+ // TODO Auto-generated constructor stub
+ }
+
+ public VorbisCommentReaderException(Throwable arg0) {
+ super(arg0);
+ // TODO Auto-generated constructor stub
+ }
+
+}