use of de.danoeh.antennapod.model.feed.FeedMedia in project AntennaPod by AntennaPod.
the class SyncService method syncEpisodeActions.
private void syncEpisodeActions(ISyncService syncServiceImpl) throws SyncServiceException {
final long lastSync = SynchronizationSettings.getLastEpisodeActionSynchronizationTimestamp();
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_episodes_download));
EpisodeActionChanges getResponse = syncServiceImpl.getEpisodeActionChanges(lastSync);
long newTimeStamp = getResponse.getTimestamp();
List<EpisodeAction> remoteActions = getResponse.getEpisodeActions();
processEpisodeActions(remoteActions);
// upload local actions
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_episodes_upload));
List<EpisodeAction> queuedEpisodeActions = synchronizationQueueStorage.getQueuedEpisodeActions();
if (lastSync == 0) {
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_upload_played));
List<FeedItem> readItems = DBReader.getPlayedItems();
Log.d(TAG, "First sync. Upload state for all " + readItems.size() + " played episodes");
for (FeedItem item : readItems) {
FeedMedia media = item.getMedia();
if (media == null) {
continue;
}
EpisodeAction played = new EpisodeAction.Builder(item, EpisodeAction.PLAY).currentTimestamp().started(media.getDuration() / 1000).position(media.getDuration() / 1000).total(media.getDuration() / 1000).build();
queuedEpisodeActions.add(played);
}
}
if (queuedEpisodeActions.size() > 0) {
LockingAsyncExecutor.lock.lock();
try {
Log.d(TAG, "Uploading " + queuedEpisodeActions.size() + " actions: " + StringUtils.join(queuedEpisodeActions, ", "));
UploadChangesResponse postResponse = syncServiceImpl.uploadEpisodeActions(queuedEpisodeActions);
newTimeStamp = postResponse.timestamp;
Log.d(TAG, "Upload episode response: " + postResponse);
synchronizationQueueStorage.clearEpisodeActionQueue();
} finally {
LockingAsyncExecutor.lock.unlock();
}
}
SynchronizationSettings.setLastEpisodeActionSynchronizationAttemptTimestamp(newTimeStamp);
}
use of de.danoeh.antennapod.model.feed.FeedMedia in project AntennaPod by AntennaPod.
the class FeedMediaCursorMapper method convert.
/**
* Create a {@link FeedMedia} instance from a database row (cursor).
*/
@NonNull
public static FeedMedia convert(@NonNull Cursor cursor) {
int indexId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_MEDIA_ID);
int indexPlaybackCompletionDate = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE);
int indexDuration = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DURATION);
int indexPosition = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_POSITION);
int indexSize = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_SIZE);
int indexMimeType = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_MIME_TYPE);
int indexFileUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_FILE_URL);
int indexDownloadUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOAD_URL);
int indexDownloaded = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOADED);
int indexPlayedDuration = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PLAYED_DURATION);
int indexLastPlayedTime = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_LAST_PLAYED_TIME);
long mediaId = cursor.getLong(indexId);
Date playbackCompletionDate = null;
long playbackCompletionTime = cursor.getLong(indexPlaybackCompletionDate);
if (playbackCompletionTime > 0) {
playbackCompletionDate = new Date(playbackCompletionTime);
}
Boolean hasEmbeddedPicture;
switch(cursor.getInt(cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) {
case 1:
hasEmbeddedPicture = Boolean.TRUE;
break;
case 0:
hasEmbeddedPicture = Boolean.FALSE;
break;
default:
hasEmbeddedPicture = null;
break;
}
return new FeedMedia(mediaId, null, cursor.getInt(indexDuration), cursor.getInt(indexPosition), cursor.getLong(indexSize), cursor.getString(indexMimeType), cursor.getString(indexFileUrl), cursor.getString(indexDownloadUrl), cursor.getInt(indexDownloaded) > 0, playbackCompletionDate, cursor.getInt(indexPlayedDuration), hasEmbeddedPicture, cursor.getLong(indexLastPlayedTime));
}
use of de.danoeh.antennapod.model.feed.FeedMedia in project AntennaPod by AntennaPod.
the class PlaybackService method onPostPlayback.
/**
* This method processes the media object after its playback ended, either because it completed
* or because a different media object was selected for playback.
* <p>
* Even though these tasks aren't supposed to be resource intensive, a good practice is to
* usually call this method on a background thread.
*
* @param playable the media object that was playing. It is assumed that its position
* property was updated before this method was called.
* @param ended if true, it signals that {@param playable} was played until its end.
* In such case, the position property of the media becomes irrelevant for
* most of the tasks (although it's still a good practice to keep it
* accurate).
* @param skipped if the user pressed a skip >| button.
* @param playingNext if true, it means another media object is being loaded in place of this
* one.
* Instances when we'd set it to false would be when we're not following the
* queue or when the queue has ended.
*/
private void onPostPlayback(final Playable playable, boolean ended, boolean skipped, boolean playingNext) {
if (playable == null) {
Log.e(TAG, "Cannot do post-playback processing: media was null");
return;
}
Log.d(TAG, "onPostPlayback(): media=" + playable.getEpisodeTitle());
if (!(playable instanceof FeedMedia)) {
Log.d(TAG, "Not doing post-playback processing: media not of type FeedMedia");
if (ended) {
playable.onPlaybackCompleted(getApplicationContext());
} else {
playable.onPlaybackPause(getApplicationContext());
}
return;
}
FeedMedia media = (FeedMedia) playable;
FeedItem item = media.getItem();
boolean smartMarkAsPlayed = FeedItemUtil.hasAlmostEnded(media);
if (!ended && smartMarkAsPlayed) {
Log.d(TAG, "smart mark as played");
}
boolean autoSkipped = false;
if (autoSkippedFeedMediaId != null && autoSkippedFeedMediaId.equals(item.getIdentifyingValue())) {
autoSkippedFeedMediaId = null;
autoSkipped = true;
}
if (ended || smartMarkAsPlayed) {
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(getApplicationContext(), media, true);
media.onPlaybackCompleted(getApplicationContext());
} else {
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(getApplicationContext(), media, false);
media.onPlaybackPause(getApplicationContext());
}
if (item != null) {
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !UserPreferences.shouldSkipKeepEpisode())) {
// only mark the item as played if we're not keeping it anyways
DBWriter.markItemPlayed(item, FeedItem.PLAYED, ended);
// don't know if it actually matters to not autodownload when smart mark as played is triggered
DBWriter.removeQueueItem(PlaybackService.this, ended, item);
// Delete episode if enabled
FeedPreferences.AutoDeleteAction action = item.getFeed().getPreferences().getCurrentAutoDelete();
boolean shouldAutoDelete = action == FeedPreferences.AutoDeleteAction.YES || (action == FeedPreferences.AutoDeleteAction.GLOBAL && UserPreferences.isAutoDelete());
if (shouldAutoDelete && (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) {
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
Log.d(TAG, "Episode Deleted");
}
}
}
if (ended || skipped || playingNext) {
DBWriter.addItemToPlaybackHistory(media);
}
}
use of de.danoeh.antennapod.model.feed.FeedMedia in project AntennaPod by AntennaPod.
the class ChapterUtils method loadChapters.
public static void loadChapters(Playable playable, Context context) {
if (playable.getChapters() != null) {
// Already loaded
return;
}
List<Chapter> chaptersFromDatabase = null;
if (playable instanceof FeedMedia) {
FeedMedia feedMedia = (FeedMedia) playable;
if (feedMedia.getItem() == null) {
feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId()));
}
if (feedMedia.getItem().hasChapters()) {
chaptersFromDatabase = DBReader.loadChaptersOfFeedItem(feedMedia.getItem());
}
}
List<Chapter> chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(playable, context);
List<Chapter> chapters = ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile);
if (chapters == null) {
// Do not try loading again. There are no chapters.
playable.setChapters(Collections.emptyList());
} else {
playable.setChapters(chapters);
}
}
use of de.danoeh.antennapod.model.feed.FeedMedia in project AntennaPod by AntennaPod.
the class MediaDownloadedHandler method run.
@Override
public void run() {
updatedStatus = status;
FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
if (media == null) {
Log.e(TAG, "Could not find downloaded media object in database");
return;
}
// media.setDownloaded modifies played state
boolean broadcastUnreadStateUpdate = media.getItem() != null && media.getItem().isNew();
media.setDownloaded(true);
media.setFile_url(request.getDestination());
media.setSize(new File(request.getDestination()).length());
// enforce check
media.checkEmbeddedPicture();
// check if file has chapters
if (media.getItem() != null && !media.getItem().hasChapters()) {
media.setChapters(ChapterUtils.loadChaptersFromMediaFile(media, context));
}
// Get duration
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
String durationStr = null;
try {
mmr.setDataSource(media.getFile_url());
durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
media.setDuration(Integer.parseInt(durationStr));
Log.d(TAG, "Duration of file is " + media.getDuration());
} catch (NumberFormatException e) {
Log.d(TAG, "Invalid file duration: " + durationStr);
} catch (Exception e) {
Log.e(TAG, "Get duration failed", e);
} finally {
mmr.release();
}
final FeedItem item = media.getItem();
try {
DBWriter.setFeedMedia(media).get();
// we've received the media, we don't want to autodownload it again
if (item != null) {
item.disableAutoDownload();
// setFeedItem() signals (via EventBus) that the item has been updated,
// so we do it after the enclosing media has been updated above,
// to ensure subscribers will get the updated FeedMedia as well
DBWriter.setFeedItem(item).get();
if (broadcastUnreadStateUpdate) {
EventBus.getDefault().post(new UnreadItemsUpdateEvent());
}
}
} catch (InterruptedException e) {
Log.e(TAG, "MediaHandlerThread was interrupted");
} catch (ExecutionException e) {
Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage());
updatedStatus = new DownloadStatus(media, media.getEpisodeTitle(), DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage(), request.isInitiatedByUser());
}
if (item != null) {
EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD).currentTimestamp().build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
}
}
Aggregations