Java tutorial
/* * Copyright (c) 2016 OpenSilk Productions LLC. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opensilk.video.data; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.media.MediaDescription; import android.media.browse.MediaBrowser; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.opensilk.common.core.dagger2.ForApplication; import org.opensilk.tmdb.api.model.Image; import org.opensilk.tmdb.api.model.ImageList; import org.opensilk.tmdb.api.model.Movie; import org.opensilk.tmdb.api.model.TMDbConfig; import org.opensilk.tvdb.api.model.Actor; import org.opensilk.tvdb.api.model.AllZipped; import org.opensilk.tvdb.api.model.Banner; import org.opensilk.tvdb.api.model.Episode; import org.opensilk.tvdb.api.model.Series; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.Locale; import javax.inject.Inject; import javax.inject.Named; import timber.log.Timber; /** * Created by drew on 4/1/16. */ public class VideosProviderClient { private final ContentResolver mResolver; private final VideosUris mUris; private final TVDbClient mTvDbClient; private final MovieDbClient mMovieDbClient; @Inject public VideosProviderClient(@ForApplication Context mContext, VideosUris mUris, @Named("tvdb_root") String tvdbRoot) { this.mResolver = mContext.getContentResolver(); this.mUris = mUris; this.mTvDbClient = new TVDbClient(Uri.parse(tvdbRoot)); this.mMovieDbClient = new MovieDbClient(); } public VideosUris uris() { return mUris; } public TVDbClient tvdb() { return mTvDbClient; } public MovieDbClient moviedb() { return mMovieDbClient; } public Integer getMediaType(@NonNull Uri mediaUri) { Cursor c = mResolver.query(mUris.media(), new String[] { "media_category" }, "media_uri=?", new String[] { mediaUri.toString() }, null); try { if (c != null && c.moveToFirst()) { return c.getInt(0); } return null; } finally { closeCursor(c); } } public boolean insertMedia(MediaBrowser.MediaItem mediaItem) { ContentValues cv = new ContentValues(10); MediaDescription description = mediaItem.getDescription(); MediaMetaExtras metaExtras = MediaMetaExtras.from(description.getExtras()); Uri mediaUri = MediaDescriptionUtil.getMediaUri(description); cv.put("_display_name", metaExtras.getMediaTitle()); String descriptionTitle = description.getTitle() != null ? description.getTitle().toString() : null; cv.put("_title", descriptionTitle); String descriptionSubtitle = description.getSubtitle() != null ? description.getSubtitle().toString() : null; cv.put("_subtitle", descriptionSubtitle); cv.put("parent_media_uri", metaExtras.getParentUri().toString()); cv.put("server_id", metaExtras.getServerId()); cv.put("media_category", metaExtras.getMediaType()); if (description.getIconUri() != null) { cv.put("artwork_uri", description.getIconUri().toString()); } cv.put("is_indexed", metaExtras.isIndexed() ? 1 : 0); //Not setting last_played or duration service does that if (metaExtras.isTvEpisode()) { tvdb().onInsertMedia(mediaItem, cv); } else if (metaExtras.isMovie()) { moviedb().onInsertMedia(mediaItem, cv); } try { int num = mResolver.update(mUris.media(), cv, "media_uri=?", new String[] { mediaUri.toString() }); if (num > 0) { Timber.d("Updated %d rows for %s", num, metaExtras.getMediaTitle()); return true; } cv.put("media_uri", mediaUri.toString()); cv.put("date_added", System.currentTimeMillis()); return mResolver.insert(mUris.media(), cv) != null; } catch (SQLiteException e) { Timber.w(e, "Failed updating %s values=%s", metaExtras.getMediaTitle(), cv.toString()); return false; } } public int removeMedia(MediaBrowser.MediaItem mediaItem) { Uri mediaUri = MediaDescriptionUtil.getMediaUri(mediaItem.getDescription()); if (mediaUri == null) { Timber.e("Refusing delete of %s no mediaUri", MediaItemUtil.getMediaTitle(mediaItem)); return 0; } int num = mResolver.delete(mUris.media(), "media_uri=?", new String[] { mediaUri.toString() }); Timber.d("Deleted %d rows for %s", num, mediaUri); return num; } public void removeOrphans(MediaBrowser.MediaItem parentItem, List<MediaBrowser.MediaItem> childItems) { List<MediaBrowser.MediaItem> indexedItems = getChildren(parentItem); for (MediaBrowser.MediaItem item : childItems) { Uri itemUri = MediaItemUtil.getMediaUri(item); ListIterator<MediaBrowser.MediaItem> indexedII = indexedItems.listIterator(); while (indexedII.hasNext()) { MediaBrowser.MediaItem indexedItem = indexedII.next(); Uri indexedItemUri = MediaItemUtil.getMediaUri(indexedItem); if (itemUri.equals(indexedItemUri)) { indexedII.remove(); break; } } } for (MediaBrowser.MediaItem mediaItem : indexedItems) { removeMediaRecursive(mediaItem); } } private void removeMediaRecursive(MediaBrowser.MediaItem mediaItem) { if (mediaItem.isBrowsable()) { List<MediaBrowser.MediaItem> orphanChildren = getChildren(mediaItem); for (MediaBrowser.MediaItem orphanChild : orphanChildren) { removeMediaRecursive(orphanChild); } } Timber.i("Removing orphaned media %s@%s", MediaItemUtil.getMediaTitle(mediaItem), MediaItemUtil.getMediaUri(mediaItem)); removeMedia(mediaItem); } public @Nullable MediaBrowser.MediaItem getMedia(@NonNull Uri mediaUri) { Integer type = getMediaType(mediaUri); if (type == null) { return null; } switch (type) { case MediaMetaExtras.MEDIA_TYPE.TV_EPISODE: { MediaBrowser.MediaItem mediaItem = tvdb().getMedia(mediaUri); if (mediaItem != null) { return mediaItem; } return getShallowMedia(mediaUri); } case MediaMetaExtras.MEDIA_TYPE.MOVIE: { MediaBrowser.MediaItem mediaItem = moviedb().getMedia(mediaUri); if (mediaItem != null) { return mediaItem; } return getShallowMedia(mediaUri); } case MediaMetaExtras.MEDIA_TYPE.DIRECTORY: case MediaMetaExtras.MEDIA_TYPE.VIDEO: { return getShallowMedia(mediaUri); } default: Timber.w("Unsupported type %s for %s", type, mediaUri); return null; } } //media not joined to other tables to provide overviews and such private MediaBrowser.MediaItem getShallowMedia(@NonNull Uri mediaUri) { Cursor c = mResolver.query(mUris.media(), SHALLOW_MEDIA_PROJ, "media_uri=?", new String[] { mediaUri.toString() }, null); try { if (c != null && c.moveToFirst()) { return buildShallowMedia(c); } return null; } finally { closeCursor(c); } } static final String[] SHALLOW_MEDIA_PROJ = new String[] { "_display_name", "parent_media_uri", "server_id", "_title", "_subtitle", "artwork_uri", "is_indexed", "last_position", "duration", "media_category", "media_uri" }; MediaBrowser.MediaItem buildShallowMedia(Cursor c) { String displayName = c.getString(0); Uri parentUri = Uri.parse(c.getString(1)); String serverId = c.getString(2); String title = c.getString(3); String subtitle = c.getString(4); Uri artworkUri = StringUtils.isEmpty(c.getString(5)) ? null : Uri.parse(c.getString(5)); boolean isIndexed = c.getInt(6) == 1; long lastPosition = c.isNull(7) ? -1 : c.getLong(7); long duration = c.isNull(8) ? -1 : c.getLong(8); int type = c.getInt(9); Uri mediaUri = Uri.parse(c.getString(10)); String pfx = "media:"; int flag = 0; switch (type) { case MediaMetaExtras.MEDIA_TYPE.DIRECTORY: pfx = "directory:"; flag = MediaBrowser.MediaItem.FLAG_BROWSABLE; break; case MediaMetaExtras.MEDIA_TYPE.TV_EPISODE: pfx = "tv_episode:"; flag = MediaBrowser.MediaItem.FLAG_PLAYABLE; break; case MediaMetaExtras.MEDIA_TYPE.MOVIE: pfx = "movie:"; flag = MediaBrowser.MediaItem.FLAG_PLAYABLE; break; case MediaMetaExtras.MEDIA_TYPE.VIDEO: pfx = "video:"; flag = MediaBrowser.MediaItem.FLAG_PLAYABLE; break; } MediaDescription.Builder builder = new MediaDescription.Builder().setMediaId(pfx + mediaUri) .setTitle(StringUtils.isEmpty(title) ? displayName : title).setSubtitle(subtitle) .setIconUri(artworkUri); MediaMetaExtras metaExtras = MediaMetaExtras.unknown().setMediaType(type).setMediaTitle(displayName) .setParentUri(parentUri).setServerId(serverId).setIndexed(isIndexed).setLastPosition(lastPosition) .setDuration(duration); MediaDescriptionUtil.setMediaUri(builder, metaExtras, mediaUri); builder.setExtras(metaExtras.getBundle()); return new MediaBrowser.MediaItem(builder.build(), flag); } public @NonNull List<MediaBrowser.MediaItem> getChildren(@NonNull MediaBrowser.MediaItem mediaItem) { Uri parentUri = MediaDescriptionUtil.getMediaUri(mediaItem.getDescription()); return getChildren(parentUri); } public @NonNull List<MediaBrowser.MediaItem> getChildren(@NonNull Uri parentUri) { Cursor c = mResolver.query(mUris.media(), SHALLOW_MEDIA_PROJ, "parent_media_uri=?", new String[] { parentUri.toString() }, "_display_name"); try { if (c != null && c.moveToFirst()) { ArrayList<MediaBrowser.MediaItem> lst = new ArrayList<>(c.getCount()); do { lst.add(buildShallowMedia(c)); } while (c.moveToNext()); return lst; } return Collections.emptyList(); } finally { closeCursor(c); } } public List<MediaBrowser.MediaItem> getTopLevelDirectories() { Cursor c = mResolver.query(mUris.media(), new String[] { "media_uri", "parent_media_uri" }, String.format( Locale.US, "media_category=%d AND is_indexed=1", MediaMetaExtras.MEDIA_TYPE.DIRECTORY), null, null); try { if (c != null && c.moveToFirst()) { List<Pair<String, String>> pairs = new ArrayList<>(); do { String mediaUri = c.getString(0); String parentUri = c.getString(1); pairs.add(Pair.of(parentUri, mediaUri)); } while (c.moveToNext()); Tree<String> tree = Tree.newTree(pairs); List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>(); for (Tree.Node<String> node : tree.getNodes()) { MediaBrowser.MediaItem mediaItem = getShallowMedia(Uri.parse(node.getSelf())); if (mediaItem != null) { mediaItems.add(mediaItem); } } return mediaItems; } return Collections.emptyList(); } finally { closeCursor(c); } } public void markIndexed(Uri mediaUri, boolean indexed) { try { ContentValues values = new ContentValues(); values.put("is_indexed", indexed ? 1 : 0); mResolver.update(mUris.media(), values, "media_uri=?", new String[] { mediaUri.toString() }); } catch (SQLiteException e) { Timber.w("Failed to mark %s as indexed", mediaUri); } } public int updateMediaLastPosition(@NonNull Uri mediaUri, long position) { ContentValues values = new ContentValues(1); values.put("last_position", position); values.put("last_played", System.currentTimeMillis()); return updateMedia(mediaUri, values); } public long updateMediaDuration(@NonNull Uri mediaUri, long duration) { ContentValues values = new ContentValues(1); values.put("duration", duration); return updateMedia(mediaUri, values); } public long updateMediaFileSize(@NonNull Uri mediaUri, long size) { ContentValues values = new ContentValues(1); values.put("file_size", size); return updateMedia(mediaUri, values); } private int updateMedia(@NonNull Uri mediaUri, ContentValues values) { try { return mResolver.update(mUris.media(), values, "media_uri=?", new String[] { mediaUri.toString() }); } catch (SQLiteException e) { Timber.w(e, "Unable to update media %s", mediaUri); return 0; } } public class TVDbClient { private final Uri tvdbRoot; public TVDbClient(Uri tvdbRoot) { this.tvdbRoot = tvdbRoot; } public Uri rootUri() { return tvdbRoot; } public Uri bannerRootUri() { return Uri.withAppendedPath(tvdbRoot, "banners/"); } public Uri makeBannerUri(String path) { return Uri.withAppendedPath(bannerRootUri(), path); } void onInsertMedia(MediaBrowser.MediaItem mediaItem, ContentValues cv) { MediaMetaExtras metaExtras = MediaMetaExtras.from(mediaItem.getDescription()); //TODO it should be an error to get here without at least series_id set. //there are cases such as rescan where these wont be set because the //item was pulled with getShallowMedia and we dont want to override //our good values with zeros if (metaExtras.getEpisodeId() > 0) { cv.put("episode_id", metaExtras.getEpisodeId()); } if (metaExtras.getSeriesId() > 0) { cv.put("series_id", metaExtras.getSeriesId()); } } public @Nullable MediaBrowser.MediaItem getMedia(Uri mediaUri) { Cursor c = mResolver.query(mUris.mediaTvEpisode(), TV_EPISODE_PROJ, "media_uri=?", new String[] { mediaUri.toString() }, null); try { if (c != null && c.moveToFirst()) { return buildMedia(c); } return null; } finally { closeCursor(c); } } public List<MediaBrowser.MediaItem> getTvEpisodes(String mediaId) { long id; try { id = Long.valueOf(StringUtils.removeStart(mediaId, "tv_series:")); } catch (NumberFormatException e) { Timber.e(e, "getTvEpisodes(%s)", mediaId); return null; } Cursor c = mResolver.query(mUris.mediaTvEpisode(), TV_EPISODE_PROJ, "series_id=" + id, null, "season_number, episode_number"); try { if (c != null && c.moveToFirst()) { ArrayList<MediaBrowser.MediaItem> list = new ArrayList<>(c.getCount()); do { list.add(buildMedia(c)); } while (c.moveToNext()); return list; } return Collections.emptyList(); } finally { closeCursor(c); } } String[] TV_EPISODE_PROJ = new String[] { "_display_name", "parent_media_uri", "server_id", "_title", "episode_number", "season_number", "_subtitle", "poster_path", "overview", "backdrop_path", "artwork_uri", "media_uri", "last_position", "duration" }; MediaBrowser.MediaItem buildMedia(Cursor c) { String displayName = c.getString(0); Uri parentUri = Uri.parse(c.getString(1)); String serverId = c.getString(2); String title = c.getString(3); int episodeNumber = c.isNull(4) ? -1 : c.getInt(4); int seasonNumber = c.isNull(5) ? -1 : c.getInt(5); String subtitle = c.getString(6); String posterPath = c.getString(7); String overview = c.getString(8); String backdropPath = c.getString(9); Uri artworkUri = StringUtils.isEmpty(c.getString(10)) ? null : Uri.parse(c.getString(10)); Uri mediaUri = Uri.parse(c.getString(11)); long lastPositino = c.isNull(12) ? -1 : c.getLong(12); long duration = c.isNull(13) ? -1 : c.getLong(13); MediaDescription.Builder builder = new MediaDescription.Builder(); MediaMetaExtras metaExtras = MediaMetaExtras.tvEpisode(); builder.setMediaId("tv_episode:" + mediaUri); if (seasonNumber >= 0 && episodeNumber >= 0) { builder.setTitle(title).setSubtitle(subtitle).setDescription(overview); } metaExtras.setIndexed(true).setMediaTitle(displayName).setParentUri(parentUri).setServerId(serverId) .setSeasonNumber(seasonNumber).setEpisodeId(episodeNumber).setLastPosition(lastPositino) .setDuration(duration); if (!StringUtils.isEmpty(backdropPath)) { metaExtras.setBackdropUri(makeBannerUri(backdropPath)); } if (artworkUri != null) { metaExtras.setPosterUri(artworkUri); builder.setIconUri(artworkUri); } else if (!StringUtils.isEmpty(posterPath)) { metaExtras.setPosterUri(makeBannerUri(posterPath)); builder.setIconUri(makeBannerUri(posterPath)); } MediaDescriptionUtil.setMediaUri(builder, metaExtras, mediaUri); builder.setExtras(metaExtras.getBundle()); return new MediaBrowser.MediaItem(builder.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE); } public MediaBrowser.MediaItem getTvSeries(String mediaId) { try { long id = Long.valueOf(StringUtils.removeStart(mediaId, "tv_series:")); return getTvSeries(id); } catch (NumberFormatException e) { Timber.e(e, "getTvSeries(%s)", mediaId); return null; } } public MediaBrowser.MediaItem getTvSeries(long id) { Cursor c = mResolver.query(mUris.tvSeries(id), TV_SERIES_PROJ, null, null, null); try { if (c != null && c.moveToFirst()) { return buildTvSeries(c); } return null; } finally { closeCursor(c); } } public final String[] TV_SERIES_PROJ = new String[] { "_display_name", "overview", "poster_path", "backdrop_path", "_id" }; public MediaBrowser.MediaItem buildTvSeries(Cursor c) { String displayName = c.getString(0); String overview = c.getString(1); String posterPath = c.getString(2); String backdropPath = c.getString(3); long id = c.getLong(4); MediaDescription.Builder builder = new MediaDescription.Builder().setMediaId("tv_series:" + id) .setTitle(displayName).setDescription(overview); MediaMetaExtras metaExtras = MediaMetaExtras.tvSeries(); if (!StringUtils.isEmpty(posterPath)) { builder.setIconUri(makeBannerUri(posterPath)); metaExtras.setPosterUri(makeBannerUri(posterPath)); } if (!StringUtils.isEmpty(backdropPath)) { metaExtras.setBackdropUri(makeBannerUri(backdropPath)); } builder.setExtras(metaExtras.getBundle()); return new MediaBrowser.MediaItem(builder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE | MediaBrowser.MediaItem.FLAG_PLAYABLE); } public String makeSubtitle(String seriesName, int seasonNumber, int episodeNumber) { return String.format(Locale.getDefault(), "%s - S%02dE%02d", seriesName, seasonNumber, episodeNumber); } public long getSeriesAssociation(String query) { Cursor c = mResolver.query(mUris.tvLookups(), new String[] { "series_id" }, "q=?", new String[] { query }, null); try { long id = -1; if (c != null && c.moveToFirst()) { id = c.getLong(0); } return id; } finally { closeCursor(c); } } public void setSeriesAssociation(String query, long series_id) { ContentValues cv = new ContentValues(2); cv.put("q", query); cv.put("series_id", series_id); Uri uri = mResolver.insert(mUris.tvLookups(), cv); // } public @Nullable Episode getEpisode(long seriesId, int season, int episode) { Cursor c = mResolver.query(mUris.tvEpisodes(), new String[] { "_id", "_display_name", "first_aired" }, String.format(Locale.US, "series_id=%d AND season_number=%d AND episode_number=%d", seriesId, season, episode), null, null); try { if (c != null && c.moveToFirst()) { long id = c.getLong(0); String displayName = c.getString(1); String firstAired = c.getString(2); return new Episode(id, displayName, firstAired, null, episode, season, null, seriesId); } return null; } finally { closeCursor(c); } } public @Nullable Series getSeries(long id) { Cursor c = mResolver.query(mUris.tvSeries(id), new String[] { "_display_name", "overview", "first_aired", "poster_path", "backdrop_path" }, null, null, null); try { if (c != null && c.moveToFirst()) { String displayName = c.getString(0); String overview = c.getString(1); String firstAired = c.getString(2); String posterPath = c.getString(3); String backdropPath = c.getString(4); return new Series(id, displayName, overview, backdropPath, posterPath, firstAired); } return null; } finally { closeCursor(c); } } public void insertAllZipped(AllZipped allZipped) { insertTvSeries(allZipped.getSeries()); if (allZipped.getEpisodes() != null && !allZipped.getEpisodes().isEmpty()) { for (Episode episode : allZipped.getEpisodes()) { insertTvEpisode(episode); } } if (allZipped.getBanners() != null && !allZipped.getBanners().isEmpty()) { for (Banner banner : allZipped.getBanners()) { insertTVBanner(allZipped.getSeries().getId(), banner); } } if (allZipped.getActors() != null && !allZipped.getActors().isEmpty()) { for (Actor actor : allZipped.getActors()) { insertTvActor(allZipped.getSeries().getId(), actor); } } } public Uri insertTvSeries(Series series) { ContentValues values = new ContentValues(10); values.put("_id", series.getId()); values.put("_display_name", series.getSeriesName()); if (!StringUtils.isEmpty(series.getOverview())) { values.put("overview", series.getOverview()); } if (!StringUtils.isEmpty(series.getFirstAired())) { values.put("first_aired", series.getFirstAired()); } if (!StringUtils.isEmpty(series.getPosterPath())) { values.put("poster_path", series.getPosterPath()); } if (!StringUtils.isEmpty(series.getFanartPath())) { values.put("backdrop_path", series.getFanartPath()); } return mResolver.insert(mUris.tvSeries(), values); } public Uri insertTvEpisode(Episode episode) { ContentValues values = new ContentValues(10); values.put("_id", episode.getId()); if (!StringUtils.isEmpty(episode.getEpisodeName())) { values.put("_display_name", episode.getEpisodeName()); } if (!StringUtils.isEmpty(episode.getOverview())) { values.put("overview", episode.getOverview()); } if (!StringUtils.isEmpty(episode.getFirstAired())) { values.put("first_aired", episode.getFirstAired()); } values.put("episode_number", episode.getEpisodeNumber()); values.put("season_number", episode.getSeasonNumber()); values.put("series_id", episode.getSeriesId()); return mResolver.insert(mUris.tvEpisodes(), values); } public @NonNull List<Episode> getEpisodes(long seriesId) { Cursor c = mResolver.query(mUris.tvEpisodes(), new String[] { "_id", "_display_name", "episode_number", "season_number" }, "series_id=" + seriesId, null, "season_number, episode_number"); try { if (c != null && c.moveToFirst()) { List<Episode> episodes = new ArrayList<>(c.getCount()); do { long id = c.getLong(0); String name = c.getString(1); int episode_number = c.getInt(2); int season_number = c.getInt(3); episodes.add( new Episode(id, name, null, null, episode_number, season_number, null, seriesId)); } while (c.moveToNext()); return episodes; } return Collections.emptyList(); } finally { closeCursor(c); } } public Uri insertTVBanner(long seriesId, Banner banner) { ContentValues values = new ContentValues(10); values.put("_id", banner.getId()); values.put("path", banner.getBannerPath()); values.put("type", banner.getBannerType()); values.put("type2", banner.getBannerType2()); if (banner.getRating() != null) { values.put("rating", banner.getRating()); } if (banner.getRatingCount() != null) { values.put("rating_count", banner.getRatingCount()); } if (banner.getThumbnailPath() != null) { values.put("thumb_path", banner.getThumbnailPath()); } if (banner.getSeason() != null) { values.put("season", banner.getSeason()); } values.put("series_id", seriesId); return mResolver.insert(mUris.tvBanners(), values); } public List<Banner> getBanners(long series_id) { return getBanners(series_id, -1); } public List<Banner> getBanners(long series_id, int seasonNumber) { ArrayList<Banner> banners = new ArrayList<>(); String selection; if (seasonNumber < 0) { selection = "series_id=" + series_id; } else { selection = String.format(Locale.US, "series_id=%d AND type='season' " + "AND type2='season' AND season=%d", series_id, seasonNumber); } Cursor c = mResolver.query(mUris.tvBanners(), new String[] { "path", "type", "type2", "rating", "rating_count", "thumb_path", "season", "_id" }, selection, null, "rating DESC"); try { if (c != null && c.moveToFirst()) { do { String path = c.getString(0); String type = c.getString(1); String type2 = c.getString(2); Float rating = c.isNull(3) ? null : c.getFloat(3); Integer ratingCount = c.isNull(4) ? null : c.getInt(4); String thumbPath = c.getString(5); Integer season = c.isNull(6) ? null : c.getInt(6); Long id = c.getLong(7); banners.add(new Banner(id, path, type, type2, rating, ratingCount, thumbPath, season)); } while (c.moveToNext()); } return banners; } finally { closeCursor(c); } } public Uri insertTvActor(long seriesId, Actor actor) { ContentValues values = new ContentValues(10); values.put("_id", actor.getId()); values.put("_display_name", actor.getName()); values.put("role", actor.getRole()); values.put("sort_order", actor.getSortOrder()); if (!StringUtils.isEmpty(actor.getImagePath())) { values.put("image_path", actor.getImagePath()); } values.put("series_id", seriesId); return mResolver.insert(mUris.tvActors(), values); } } public class MovieDbClient { void onInsertMedia(MediaBrowser.MediaItem mediaItem, ContentValues cv) { MediaMetaExtras metaExtras = MediaMetaExtras.from(mediaItem.getDescription()); if (metaExtras.getMovieId() > 0) { cv.put("movie_id", metaExtras.getMovieId()); } } public @Nullable MediaBrowser.MediaItem getMedia(Uri mediaUri) { Cursor c = mResolver.query(mUris.mediaMovie(), MOVIE_PROJ, "media_uri=?", new String[] { mediaUri.toString() }, null); try { if (c != null && c.moveToFirst()) { return buildMedia(c); } return null; } finally { closeCursor(c); } } public String[] MOVIE_PROJ = new String[] { "_display_name", "parent_media_uri", "server_id", "_title", "poster_path", "release_date", "image_base_url", "overview", "backdrop_path", "last_position", "duration", "media_uri", "_subtitle" }; public MediaBrowser.MediaItem buildMedia(Cursor c) { String displayName = c.getString(0); Uri parentUri = Uri.parse(c.getString(1)); String serverId = c.getString(2); String title = c.getString(3); String posterPath = c.getString(4); String releaseDate = c.getString(5); String imagebase = c.getString(6); String overview = c.getString(7); String backdropPath = c.getString(8); long lastPosition = c.isNull(9) ? -1 : c.getLong(9); long duration = c.isNull(10) ? -1 : c.getLong(10); Uri mediaUri = Uri.parse(c.getString(11)); String subtitle = c.getString(12); MediaDescription.Builder builder = new MediaDescription.Builder().setMediaId("movie:" + mediaUri) .setTitle(title).setSubtitle(subtitle).setDescription(overview); MediaMetaExtras metaExtras = MediaMetaExtras.movie().setIndexed(true).setMediaTitle(displayName) .setParentUri(parentUri).setServerId(serverId).setDate(releaseDate) .setLastPosition(lastPosition).setDuration(duration); if (!StringUtils.isEmpty(backdropPath)) { metaExtras.setBackdropUri(makeBackdropUri(imagebase, backdropPath)); } if (!StringUtils.isEmpty(posterPath)) { metaExtras.setPosterUri(makePosterUri(imagebase, posterPath)); builder.setIconUri(makePosterUri(imagebase, posterPath)); } MediaDescriptionUtil.setMediaUri(builder, metaExtras, mediaUri); builder.setExtras(metaExtras.getBundle()); return new MediaBrowser.MediaItem(builder.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE); } public Uri makePosterUri(String base, String path) { return Uri.parse(base + "w342" + path); } public Uri makeBackdropUri(String base, String path) { return Uri.parse(base + "w1280" + path); } public void updateConfig(TMDbConfig config) { ContentValues values = new ContentValues(11); values.put("image_base_url", config.getImages().getBaseUrl()); try { mResolver.update(mUris.movies(), values, null, null); mResolver.update(mUris.movieImages(), values, null, null); } catch (SQLiteException e) { Timber.e(e, "updateConfig %s", config); } } public void setMovieAssociation(String q, long id) { ContentValues contentValues = new ContentValues(2); contentValues.put("q", q); contentValues.put("movie_id", id); mResolver.insert(mUris.movieLookups(), contentValues); } public long getMovieAssociation(String q) { Cursor c = mResolver.query(mUris.movieLookups(), new String[] { "movie_id" }, "q=?", new String[] { q }, null); try { long id = -1; if (c != null && c.moveToFirst()) { id = c.getLong(0); } return id; } finally { closeCursor(c); } } public @Nullable Movie getMovie(long id) { Cursor c = mResolver.query(mUris.movie(id), new String[] { "_display_name", "overview", "release_date", "poster_path", "backdrop_path" }, null, null, null); try { if (c != null && c.moveToFirst()) { String displayName = c.getString(0); String overview = c.getString(1); String releaseDate = c.getString(2); String posterPath = c.getString(3); String backdropPath = c.getString(4); return new Movie(id, displayName, null, overview, releaseDate, posterPath, backdropPath); } return null; } finally { closeCursor(c); } } public Uri insertMovie(Movie movie, TMDbConfig config) { ContentValues values = new ContentValues(10); values.put("_id", movie.getId()); values.put("_display_name", movie.getTitle()); values.put("overview", movie.getOverview()); values.put("release_date", movie.getReleaseDate()); values.put("poster_path", movie.getPosterPath()); values.put("backdrop_path", movie.getBackdropPath()); values.put("image_base_url", config.getImages().getSecureBaseUrl()); return mResolver.insert(mUris.movies(), values); } public void insertImages(ImageList imageList, TMDbConfig config) { int numPosters = imageList.getPosters() != null ? imageList.getPosters().size() : 0; int numBackdrops = imageList.getPosters() != null ? imageList.getBackdrops().size() : 0; ContentValues[] contentValues = new ContentValues[numPosters + numBackdrops]; int idx = 0; if (numPosters > 0) { for (Image image : imageList.getPosters()) { ContentValues values = makeImageValues(image); values.put("movie_id", imageList.getId()); values.put("image_base_url", config.getImages().getSecureBaseUrl()); values.put("image_type", "poster"); contentValues[idx++] = values; } } if (numBackdrops > 0) { for (Image image : imageList.getBackdrops()) { ContentValues values = makeImageValues(image); values.put("movie_id", imageList.getId()); values.put("image_base_url", config.getImages().getSecureBaseUrl()); values.put("image_type", "backdrop"); contentValues[idx++] = values; } } mResolver.bulkInsert(mUris.movieImages(), contentValues); } ContentValues makeImageValues(Image image) { ContentValues values = new ContentValues(10); values.put("height", image.getHeight()); values.put("width", image.getWidth()); values.put("file_path", image.getFilePath()); values.put("vote_average", image.getVoteAverage()); values.put("vote_count", image.getVoteCount()); return values; } } static void closeCursor(Cursor c) { if (c != null && !c.isClosed()) { try { c.close(); } catch (Exception e) { Timber.w(e, "closeCursor()"); } } } }