Search in sources :

Example 11 with Episode

use of com.uwetrottmann.trakt5.entities.Episode in project SeriesGuide by UweTrottmann.

the class TraktRatingsSync method downloadForEpisodes.

/**
 * Downloads trakt episode ratings and applies the latest ones to the database.
 *
 * <p> To apply all ratings, set {@link TraktSettings#KEY_LAST_EPISODES_RATED_AT} to 0.
 */
public boolean downloadForEpisodes(@Nullable OffsetDateTime ratedAt) {
    if (ratedAt == null) {
        Timber.e("downloadForEpisodes: null rated_at");
        return false;
    }
    long lastRatedAt = TraktSettings.getLastEpisodesRatedAt(context);
    if (!TimeTools.isAfterMillis(ratedAt, lastRatedAt)) {
        // not initial sync, no ratings have changed
        Timber.d("downloadForEpisodes: no changes since %tF %tT", lastRatedAt, lastRatedAt);
        return true;
    }
    if (!TraktCredentials.get(context).hasCredentials()) {
        return false;
    }
    // download rated episodes
    List<RatedEpisode> ratedEpisodes;
    try {
        Response<List<RatedEpisode>> response = traktSync.ratingsEpisodes(RatingsFilter.ALL, null, null, null).execute();
        if (response.isSuccessful()) {
            ratedEpisodes = response.body();
        } else {
            if (SgTrakt.isUnauthorized(context, response)) {
                return false;
            }
            Errors.logAndReport("get episode ratings", response);
            return false;
        }
    } catch (Exception e) {
        Errors.logAndReport("get episode ratings", e);
        return false;
    }
    if (ratedEpisodes == null) {
        Timber.e("downloadForEpisodes: null response");
        return false;
    }
    if (ratedEpisodes.isEmpty()) {
        Timber.d("downloadForEpisodes: no ratings on trakt");
        return true;
    }
    // trakt last activity rated_at timestamp is set after the rating timestamp
    // so include ratings that are a little older
    long ratedAtThreshold = lastRatedAt - 5 * DateUtils.MINUTE_IN_MILLIS;
    Map<Integer, Integer> tmdbIdsToRatings = new HashMap<>();
    for (RatedEpisode episode : ratedEpisodes) {
        if (episode.rating == null || episode.episode == null || episode.episode.ids == null || episode.episode.ids.tmdb == null) {
            // skip, can't handle
            continue;
        }
        if (episode.rated_at != null && TimeTools.isBeforeMillis(episode.rated_at, ratedAtThreshold)) {
            // no need to apply older ratings again
            break;
        }
        // if an episode does not exist, this update will do nothing
        tmdbIdsToRatings.put(episode.episode.ids.tmdb, episode.rating.value);
    }
    // apply database updates
    SgRoomDatabase.getInstance(context).sgEpisode2Helper().updateUserRatings(tmdbIdsToRatings);
    // save last rated instant
    long ratedAtTime = ratedAt.toInstant().toEpochMilli();
    PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(TraktSettings.KEY_LAST_EPISODES_RATED_AT, ratedAtTime).apply();
    Timber.d("downloadForEpisodes: success, last rated_at %tF %tT", ratedAtTime, ratedAtTime);
    return true;
}
Also used : RatedEpisode(com.uwetrottmann.trakt5.entities.RatedEpisode) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) List(java.util.List) OperationApplicationException(android.content.OperationApplicationException)

Example 12 with Episode

use of com.uwetrottmann.trakt5.entities.Episode in project SeriesGuide by UweTrottmann.

the class EpisodeHistoryAdapter method getView.

@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
    // A ViewHolder keeps references to child views to avoid
    // unnecessary calls to findViewById() on each row.
    ViewHolder holder;
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.item_history, parent, false);
        holder = new ViewHolder(convertView);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    HistoryEntry item = getItem(position);
    if (item == null) {
        // all bets are off!
        return convertView;
    }
    // show title
    holder.title.setText(item.show == null ? null : item.show.title);
    // show poster, use a TVDB one
    String posterUrl;
    Integer showTvdbId = (item.show == null || item.show.ids == null) ? null : item.show.ids.tvdb;
    if (localShowPosters != null && showTvdbId != null) {
        // prefer poster of already added show, fall back to first uploaded poster
        posterUrl = TvdbImageTools.smallSizeOrFirstUrl(localShowPosters.get(showTvdbId), showTvdbId);
    } else {
        posterUrl = null;
    }
    TvdbImageTools.loadShowPosterResizeSmallCrop(getContext(), holder.poster, posterUrl);
    // timestamp
    if (item.watched_at != null) {
        CharSequence timestamp = DateUtils.getRelativeTimeSpanString(item.watched_at.getMillis(), System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_ALL);
        holder.timestamp.setText(timestamp);
    } else {
        holder.timestamp.setText(null);
    }
    // action type indicator
    if ("watch".equals(item.action)) {
        // marked watched
        holder.type.setImageResource(getResIdDrawableWatched());
    } else {
        // check-in, scrobble
        holder.type.setImageResource(getResIdDrawableCheckin());
    }
    // episode
    if (item.episode != null && item.episode.season != null && item.episode.number != null) {
        holder.description.setText(TextTools.getNextEpisodeString(getContext(), item.episode.season, item.episode.number, item.episode.title));
    } else {
        holder.description.setText(null);
    }
    return convertView;
}
Also used : HistoryEntry(com.uwetrottmann.trakt5.entities.HistoryEntry) NonNull(android.support.annotation.NonNull)

Example 13 with Episode

use of com.uwetrottmann.trakt5.entities.Episode in project SeriesGuide by UweTrottmann.

the class SgSyncAdapter method performTraktSync.

private UpdateResult performTraktSync(HashSet<Integer> localShows, long currentTime) {
    if (!TraktCredentials.get(getContext()).hasCredentials()) {
        Timber.d("performTraktSync: no auth, skip");
        return UpdateResult.SUCCESS;
    }
    if (!AndroidUtils.isNetworkConnected(getContext())) {
        return UpdateResult.INCOMPLETE;
    }
    // get last activity timestamps
    TraktTools traktTools = app.getTraktTools();
    LastActivities lastActivity = traktTools.getLastActivity();
    if (lastActivity == null) {
        // trakt is likely offline or busy, try later
        Timber.e("performTraktSync: last activity download failed");
        return UpdateResult.INCOMPLETE;
    }
    if (!AndroidUtils.isNetworkConnected(getContext())) {
        return UpdateResult.INCOMPLETE;
    }
    if (localShows.size() == 0) {
        Timber.d("performTraktSync: no local shows, skip shows");
    } else {
        // download and upload episode watched and collected flags
        if (performTraktEpisodeSync(localShows, lastActivity.episodes, currentTime) != UpdateResult.SUCCESS) {
            return UpdateResult.INCOMPLETE;
        }
        if (!AndroidUtils.isNetworkConnected(getContext())) {
            return UpdateResult.INCOMPLETE;
        }
        // download show ratings
        if (traktTools.downloadShowRatings(lastActivity.shows.rated_at) != UpdateResult.SUCCESS) {
            return UpdateResult.INCOMPLETE;
        }
        if (!AndroidUtils.isNetworkConnected(getContext())) {
            return UpdateResult.INCOMPLETE;
        }
        // download episode ratings
        if (traktTools.downloadEpisodeRatings(lastActivity.episodes.rated_at) != UpdateResult.SUCCESS) {
            return UpdateResult.INCOMPLETE;
        }
        if (!AndroidUtils.isNetworkConnected(getContext())) {
            return UpdateResult.INCOMPLETE;
        }
    }
    // sync watchlist and collection with trakt
    if (app.getMovieTools().syncMovieListsWithTrakt(lastActivity.movies) != UpdateResult.SUCCESS) {
        return UpdateResult.INCOMPLETE;
    }
    if (!AndroidUtils.isNetworkConnected(getContext())) {
        return UpdateResult.INCOMPLETE;
    }
    // download watched movies
    if (traktTools.downloadWatchedMovies(lastActivity.movies.watched_at) != UpdateResult.SUCCESS) {
        return UpdateResult.INCOMPLETE;
    }
    // clean up any useless movies (not watched or not in any list)
    MovieTools.deleteUnusedMovies(getContext());
    if (!AndroidUtils.isNetworkConnected(getContext())) {
        return UpdateResult.INCOMPLETE;
    }
    // download movie ratings
    return traktTools.downloadMovieRatings(lastActivity.movies.rated_at);
}
Also used : TraktTools(com.battlelancer.seriesguide.util.TraktTools) LastActivities(com.uwetrottmann.trakt5.entities.LastActivities)

Example 14 with Episode

use of com.uwetrottmann.trakt5.entities.Episode in project SeriesGuide by UweTrottmann.

the class UserEpisodeStreamFragment method onItemClick.

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    // do not respond if we get a header position (e.g. shortly after data was refreshed)
    if (position < 0) {
        return;
    }
    HistoryEntry item = mAdapter.getItem(position);
    if (item == null) {
        return;
    }
    if (item.episode == null || item.episode.season == null || item.episode.number == null || item.show == null || item.show.ids == null || item.show.ids.tvdb == null) {
        // no episode or show? give up
        return;
    }
    Cursor episodeQuery = getActivity().getContentResolver().query(SeriesGuideContract.Episodes.buildEpisodesOfShowUri(item.show.ids.tvdb), new String[] { SeriesGuideContract.Episodes._ID }, SeriesGuideContract.Episodes.NUMBER + "=" + item.episode.number + " AND " + SeriesGuideContract.Episodes.SEASON + "=" + item.episode.season, null, null);
    if (episodeQuery == null) {
        return;
    }
    if (episodeQuery.getCount() != 0) {
        // display the episode details if we have a match
        episodeQuery.moveToFirst();
        showDetails(view, episodeQuery.getInt(0));
    } else {
        // offer to add the show if it's not in the show database yet
        AddShowDialogFragment.showAddDialog(item.show.ids.tvdb, getFragmentManager());
    }
    episodeQuery.close();
}
Also used : HistoryEntry(com.uwetrottmann.trakt5.entities.HistoryEntry) Cursor(android.database.Cursor)

Example 15 with Episode

use of com.uwetrottmann.trakt5.entities.Episode in project SeriesGuide by UweTrottmann.

the class TraktTools method processTraktEpisodes.

private boolean processTraktEpisodes(boolean isInitialSync, String seasonId, BaseSeason traktSeason, List<SyncSeason> syncSeasons, Flag flag) {
    HashSet<Integer> traktEpisodes = buildTraktEpisodesMap(traktSeason.episodes);
    Cursor localEpisodesQuery = context.getContentResolver().query(SeriesGuideContract.Episodes.buildEpisodesOfSeasonUri(seasonId), new String[] { SeriesGuideContract.Episodes._ID, SeriesGuideContract.Episodes.NUMBER, flag.databaseColumn }, null, null, null);
    if (localEpisodesQuery == null) {
        return false;
    }
    ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    List<SyncEpisode> syncEpisodes = new ArrayList<>();
    int episodesAddFlagCount = 0;
    int episodesRemoveFlagCount = 0;
    while (localEpisodesQuery.moveToNext()) {
        int episodeId = localEpisodesQuery.getInt(0);
        int episodeNumber = localEpisodesQuery.getInt(1);
        int flagValue = localEpisodesQuery.getInt(2);
        boolean isFlagged = flag == Flag.WATCHED ? EpisodeTools.isWatched(flagValue) : EpisodeTools.isCollected(flagValue);
        if (traktEpisodes.contains(episodeNumber)) {
            // episode watched/collected on trakt
            if (!isFlagged) {
                // set as watched/collected
                batch.add(ContentProviderOperation.newUpdate(SeriesGuideContract.Episodes.buildEpisodeUri(episodeId)).withValue(flag.databaseColumn, flag.flaggedValue).build());
                episodesAddFlagCount++;
            }
        } else {
            // episode not watched/collected on trakt
            if (isFlagged) {
                if (isInitialSync) {
                    // upload to trakt
                    syncEpisodes.add(new SyncEpisode().number(episodeNumber));
                } else {
                    // set as not watched/collected if it is currently watched/collected
                    boolean isSkipped = flag == Flag.WATCHED && EpisodeTools.isSkipped(flagValue);
                    if (!isSkipped) {
                        batch.add(ContentProviderOperation.newUpdate(SeriesGuideContract.Episodes.buildEpisodeUri(episodeId)).withValue(flag.databaseColumn, flag.notFlaggedValue).build());
                        episodesRemoveFlagCount++;
                    }
                }
            }
        }
    }
    int localEpisodeCount = localEpisodesQuery.getCount();
    boolean addFlagToWholeSeason = episodesAddFlagCount == localEpisodeCount;
    boolean removeFlagFromWholeSeason = episodesRemoveFlagCount == localEpisodeCount;
    localEpisodesQuery.close();
    // if setting the whole season as (not) watched/collected, replace with single db op
    if (addFlagToWholeSeason || removeFlagFromWholeSeason) {
        batch.clear();
        batch.add(ContentProviderOperation.newUpdate(SeriesGuideContract.Episodes.buildEpisodesOfSeasonUri(seasonId)).withValue(flag.databaseColumn, addFlagToWholeSeason ? flag.flaggedValue : flag.notFlaggedValue).build());
    }
    try {
        DBUtils.applyInSmallBatches(context, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "Episodes watched/collected values database update failed.");
    }
    if (syncEpisodes.size() > 0) {
        syncSeasons.add(new SyncSeason().number(traktSeason.number).episodes(syncEpisodes));
    }
    return true;
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) SyncEpisode(com.uwetrottmann.trakt5.entities.SyncEpisode) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) OperationApplicationException(android.content.OperationApplicationException) SyncSeason(com.uwetrottmann.trakt5.entities.SyncSeason)

Aggregations

ArrayList (java.util.ArrayList)9 HistoryEntry (com.uwetrottmann.trakt5.entities.HistoryEntry)8 SyncEpisode (com.uwetrottmann.trakt5.entities.SyncEpisode)6 ContentProviderOperation (android.content.ContentProviderOperation)4 OperationApplicationException (android.content.OperationApplicationException)4 Friend (com.uwetrottmann.trakt5.entities.Friend)4 SyncSeason (com.uwetrottmann.trakt5.entities.SyncSeason)4 UserSlug (com.uwetrottmann.trakt5.entities.UserSlug)4 Cursor (android.database.Cursor)3 NowAdapter (com.battlelancer.seriesguide.adapters.NowAdapter)3 Show (com.uwetrottmann.trakt5.entities.Show)3 SyncMovie (com.uwetrottmann.trakt5.entities.SyncMovie)3 SuppressLint (android.annotation.SuppressLint)2 Nullable (androidx.annotation.Nullable)2 SgEpisode2Helper (com.battlelancer.seriesguide.provider.SgEpisode2Helper)2 SgEpisode2Numbers (com.battlelancer.seriesguide.provider.SgEpisode2Numbers)2 SgRoomDatabase (com.battlelancer.seriesguide.provider.SgRoomDatabase)2 TraktTools (com.battlelancer.seriesguide.util.TraktTools)2 Comment (com.uwetrottmann.trakt5.entities.Comment)2 Episode (com.uwetrottmann.trakt5.entities.Episode)2