From 142f3cb9dbd9babae91146d1d972f80b598c59a5 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 22 Sep 2012 11:56:50 +0200 Subject: Added support for reading chapters from streams --- .../antennapod/activity/AudioplayerActivity.java | 19 +++++- .../danoeh/antennapod/service/PlaybackService.java | 20 +++++- .../service/download/DownloadService.java | 2 +- src/de/danoeh/antennapod/util/ChapterUtils.java | 72 ++++++++++++++++++++-- .../antennapod/util/id3reader/ID3Reader.java | 30 +++++++-- 5 files changed, 129 insertions(+), 14 deletions(-) diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index c469e70f9..8d0b4a0f5 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -81,7 +81,13 @@ public class AudioplayerActivity extends MediaplayerActivity { @Override protected void loadMediaInfo() { super.loadMediaInfo(); - if (controller.getMedia() != null) { + final FeedMedia media = controller.getMedia(); + if (media != null) { + if (media.getItem().getChapters() != null + && pagerAdapter.getCount() < MediaPlayerPagerAdapter.NUM_ITEMS_WITH_CHAPTERS) { + pagerAdapter + .setNumItems(MediaPlayerPagerAdapter.NUM_ITEMS_WITH_CHAPTERS); + } pagerAdapter.notifyDataSetChanged(); } } @@ -96,6 +102,9 @@ public class AudioplayerActivity extends MediaplayerActivity { private static final int POS_DESCR = 1; private static final int POS_CHAPTERS = 2; + public static final int NUM_ITEMS_WITH_CHAPTERS = 3; + public static final int NUM_ITMEMS_WITHOUT_CHAPTERS = 2; + public MediaPlayerPagerAdapter(FragmentManager fm, int numItems, AudioplayerActivity activity) { super(fm); @@ -123,8 +132,8 @@ public class AudioplayerActivity extends MediaplayerActivity { public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); - Chapter chapter = (Chapter) this.getListAdapter().getItem( - position); + Chapter chapter = (Chapter) this.getListAdapter() + .getItem(position); controller.seekToChapter(chapter); } @@ -161,6 +170,10 @@ public class AudioplayerActivity extends MediaplayerActivity { return numItems; } + public void setNumItems(int numItems) { + this.numItems = numItems; + } + @Override public int getItemPosition(Object object) { return POSITION_UNCHANGED; diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 2a425e9fe..e83912970 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -46,6 +46,7 @@ import de.danoeh.antennapod.feed.FeedMedia; import de.danoeh.antennapod.feed.SimpleChapter; import de.danoeh.antennapod.receiver.MediaButtonReceiver; import de.danoeh.antennapod.receiver.PlayerWidget; +import de.danoeh.antennapod.util.ChapterUtils; /** Controls the MediaPlayer that plays a FeedMedia-file */ public class PlaybackService extends Service { @@ -505,6 +506,23 @@ public class PlaybackService extends Service { if (startWhenPrepared) { play(); } + if (shouldStream && media.getItem().getChapters() == null) { + // load chapters if available + Thread chapterLoader = new Thread() { + + @Override + public void run() { + if (AppConfig.DEBUG) Log.d(TAG, "Starting chapterLoader thread"); + ChapterUtils.readID3ChaptersFromFeedMediaDownloadUrl(media.getItem()); + if (media.getItem().getChapters() != null) { + sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0); + } + if (AppConfig.DEBUG) Log.d(TAG, "ChapterLoaderThread has finished"); + } + + }; + chapterLoader.start(); + } } }; @@ -961,7 +979,7 @@ public class PlaybackService extends Service { try { Thread.sleep(UPDATE_INTERVALL); waitingTime -= UPDATE_INTERVALL; - + if (waitingTime <= 0) { if (AppConfig.DEBUG) Log.d(TAG, "Waiting completed"); diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index 5341c54aa..fae0f3b0f 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -695,7 +695,7 @@ public class DownloadService extends Service { } if (media.getItem().getChapters() == null) { - ChapterUtils.readID3ChaptersFromFeedItem(media.getItem()); + ChapterUtils.readID3ChaptersFromFeedMediaFileUrl(media.getItem()); if (media.getItem().getChapters() != null) { chaptersRead = true; } diff --git a/src/de/danoeh/antennapod/util/ChapterUtils.java b/src/de/danoeh/antennapod/util/ChapterUtils.java index dd9dbce5a..65fb6daf2 100644 --- a/src/de/danoeh/antennapod/util/ChapterUtils.java +++ b/src/de/danoeh/antennapod/util/ChapterUtils.java @@ -5,6 +5,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -25,7 +28,63 @@ public class ChapterUtils { private ChapterUtils() { } - public static void readID3ChaptersFromFeedItem(FeedItem item) { + /** + * Uses the download URL of a media object of a feeditem to read its ID3 + * chapters. + */ + public static void readID3ChaptersFromFeedMediaDownloadUrl(FeedItem item) { + if (AppConfig.DEBUG) + Log.d(TAG, "Reading id3 chapters from item " + item.getTitle()); + final FeedMedia media = item.getMedia(); + if (media != null && media.getDownload_url() != null) { + InputStream in = null; + try { + URL url = new URL(media.getDownload_url()); + ChapterReader reader = new ChapterReader(); + + in = url.openStream(); + reader.readInputStream(in); + List 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 ID3 chapters"); + } + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ID3ReaderException e) { + e.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } else { + Log.e(TAG, + "Unable to read ID3 chapters: media or download URL was null"); + } + } + + /** + * Uses the file URL of a media object of a feeditem to read its ID3 + * chapters. + */ + public static void readID3ChaptersFromFeedMediaFileUrl(FeedItem item) { if (AppConfig.DEBUG) Log.d(TAG, "Reading id3 chapters from item " + item.getTitle()); final FeedMedia media = item.getMedia(); @@ -40,17 +99,20 @@ public class ChapterUtils { in = new BufferedInputStream(new FileInputStream(source)); reader.readInputStream(in); List chapters = reader.getChapters(); - + if (chapters != null) { - Collections.sort(chapters, new ChapterStartTimeComparator()); + 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 ID3 chapters"); + Log.i(TAG, + "ChapterReader could not find any ID3 chapters"); } } catch (IOException e) { e.printStackTrace(); @@ -93,5 +155,5 @@ public class ChapterUtils { } return true; } - + } diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java index 8164170bf..7db8dbee5 100644 --- a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java +++ b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java @@ -43,9 +43,13 @@ public class ID3Reader { while (readerPosition < tagHeader.getSize()) { FrameHeader frameHeader = createFrameHeader(readBytes( input, HEADER_LENGTH)); - rc = onStartFrameHeader(frameHeader, input); - if (rc == ACTION_SKIP) { - skipBytes(input, frameHeader.getSize()); + if (checkForNullString(frameHeader.getId())) { + break; + } else { + rc = onStartFrameHeader(frameHeader, input); + if (rc == ACTION_SKIP) { + skipBytes(input, frameHeader.getSize()); + } } } onEndTag(); @@ -53,6 +57,25 @@ public class ID3Reader { } } + /** Returns true if string only contains null-bytes. */ + private boolean checkForNullString(String s) { + if (!s.isEmpty()) { + int i = 0; + if (s.charAt(i) == 0) { + for (i = 1; i < s.length(); i++) { + if (s.charAt(i) != 0) { + return false; + } + } + return true; + } + return false; + } else { + return true; + } + + } + /** * Read a certain number of bytes from the given input stream. This method * changes the readerPosition-attribute. @@ -80,7 +103,6 @@ public class ID3Reader { int skipped = 0; while (skipped < number) { skipped += input.skip(number - skipped); - System.out.println("Skipped = " + skipped); } readerPosition += number; -- cgit v1.2.3