Search in sources :

Example 56 with FeedMedia

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);
}
Also used : FeedItem(de.danoeh.antennapod.model.feed.FeedItem) FeedMedia(de.danoeh.antennapod.model.feed.FeedMedia) SyncServiceEvent(de.danoeh.antennapod.event.SyncServiceEvent) UploadChangesResponse(de.danoeh.antennapod.net.sync.model.UploadChangesResponse) EpisodeActionChanges(de.danoeh.antennapod.net.sync.model.EpisodeActionChanges) EpisodeAction(de.danoeh.antennapod.net.sync.model.EpisodeAction)

Example 57 with FeedMedia

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));
}
Also used : FeedMedia(de.danoeh.antennapod.model.feed.FeedMedia) Date(java.util.Date) NonNull(androidx.annotation.NonNull)

Example 58 with FeedMedia

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);
    }
}
Also used : FeedPreferences(de.danoeh.antennapod.model.feed.FeedPreferences) FeedItem(de.danoeh.antennapod.model.feed.FeedItem) FeedMedia(de.danoeh.antennapod.model.feed.FeedMedia)

Example 59 with FeedMedia

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);
    }
}
Also used : FeedMedia(de.danoeh.antennapod.model.feed.FeedMedia) Chapter(de.danoeh.antennapod.model.feed.Chapter)

Example 60 with FeedMedia

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);
    }
}
Also used : ExecutionException(java.util.concurrent.ExecutionException) EpisodeAction(de.danoeh.antennapod.net.sync.model.EpisodeAction) FeedItem(de.danoeh.antennapod.model.feed.FeedItem) FeedMedia(de.danoeh.antennapod.model.feed.FeedMedia) UnreadItemsUpdateEvent(de.danoeh.antennapod.event.UnreadItemsUpdateEvent) MediaMetadataRetriever(android.media.MediaMetadataRetriever) DownloadStatus(de.danoeh.antennapod.core.service.download.DownloadStatus) ExecutionException(java.util.concurrent.ExecutionException) File(java.io.File)

Aggregations

FeedMedia (de.danoeh.antennapod.model.feed.FeedMedia)91 FeedItem (de.danoeh.antennapod.model.feed.FeedItem)46 Test (org.junit.Test)28 Feed (de.danoeh.antennapod.model.feed.Feed)26 Date (java.util.Date)25 ArrayList (java.util.ArrayList)18 FeedPreferences (de.danoeh.antennapod.model.feed.FeedPreferences)15 File (java.io.File)15 Playable (de.danoeh.antennapod.model.playback.Playable)8 View (android.view.View)6 NonNull (androidx.annotation.NonNull)6 Context (android.content.Context)5 Log (android.util.Log)5 PlaybackServiceStarter (de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter)5 Cursor (android.database.Cursor)4 Nullable (androidx.annotation.Nullable)4 R (de.danoeh.antennapod.R)4 DownloadRequest (de.danoeh.antennapod.core.service.download.DownloadRequest)4 DownloadService (de.danoeh.antennapod.core.service.download.DownloadService)4 Intent (android.content.Intent)3