Kore/app/src/main/java/org/xbmc/kore/service/library/SyncMovies.java

246 lines
10 KiB
Java

/*
* Copyright 2015 Synced Synapse. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbmc.kore.service.library;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.os.Bundle;
import android.os.Handler;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.ApiList;
import org.xbmc.kore.jsonrpc.HostConnection;
import org.xbmc.kore.jsonrpc.method.VideoLibrary;
import org.xbmc.kore.jsonrpc.type.ListType;
import org.xbmc.kore.jsonrpc.type.VideoType;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.utils.LogUtils;
import java.util.ArrayList;
import java.util.List;
public class SyncMovies extends SyncItem {
public static final String TAG = LogUtils.makeLogTag(SyncMovies.class);
private static final int LIMIT_SYNC_MOVIES = 300;
private final int hostId;
private final int movieId;
private final Bundle syncExtras;
/**
* Syncs all the movies on selected XBMC to the local database
* @param hostId XBMC host id
*/
public SyncMovies(final int hostId, Bundle syncExtras) {
this.hostId = hostId;
this.movieId = -1;
this.syncExtras = syncExtras;
}
/**
* Syncs a specific movie on selected XBMC to the local database
* @param hostId XBMC host id
*/
public SyncMovies(final int hostId, final int movieId, Bundle syncExtras) {
this.hostId = hostId;
this.movieId = movieId;
this.syncExtras = syncExtras;
}
/** {@inheritDoc} */
public String getDescription() {
return (movieId != -1) ?
"Sync movies for host: " + hostId :
"Sync movie " + movieId + " for host: " + hostId;
}
/** {@inheritDoc} */
public String getSyncType() {
return (movieId == -1) ? LibrarySyncService.SYNC_ALL_MOVIES
: LibrarySyncService.SYNC_SINGLE_MOVIE;
}
/** {@inheritDoc} */
public Bundle getSyncExtras() {
return syncExtras;
}
/** {@inheritDoc} */
public void sync(final SyncOrchestrator orchestrator,
final HostConnection hostConnection,
final Handler callbackHandler,
final ContentResolver contentResolver) {
String properties[] = {
VideoType.FieldsMovie.TITLE, VideoType.FieldsMovie.GENRE,
VideoType.FieldsMovie.YEAR, VideoType.FieldsMovie.RATING,
VideoType.FieldsMovie.DIRECTOR, VideoType.FieldsMovie.TRAILER,
VideoType.FieldsMovie.TAGLINE, VideoType.FieldsMovie.PLOT,
// VideoType.FieldsMovie.PLOTOUTLINE, VideoType.FieldsMovie.ORIGINALTITLE,
// VideoType.FieldsMovie.LASTPLAYED,
VideoType.FieldsMovie.PLAYCOUNT, VideoType.FieldsMovie.DATEADDED,
VideoType.FieldsMovie.WRITER, VideoType.FieldsMovie.STUDIO,
VideoType.FieldsMovie.MPAA, VideoType.FieldsMovie.CAST,
VideoType.FieldsMovie.COUNTRY, VideoType.FieldsMovie.IMDBNUMBER,
VideoType.FieldsMovie.RUNTIME, VideoType.FieldsMovie.SET,
// VideoType.FieldsMovie.SHOWLINK,
VideoType.FieldsMovie.STREAMDETAILS, VideoType.FieldsMovie.TOP250,
VideoType.FieldsMovie.VOTES, VideoType.FieldsMovie.FANART,
VideoType.FieldsMovie.THUMBNAIL, VideoType.FieldsMovie.FILE,
// VideoType.FieldsMovie.SORTTITLE, VideoType.FieldsMovie.RESUME,
VideoType.FieldsMovie.SETID,
// VideoType.FieldsMovie.DATEADDED, VideoType.FieldsMovie.TAG,
// VideoType.FieldsMovie.ART
};
if (movieId == -1) {
syncAllMovies(orchestrator, hostConnection, callbackHandler, contentResolver, properties, 0);
} else {
// Sync a specific movie
VideoLibrary.GetMovieDetails action =
new VideoLibrary.GetMovieDetails(movieId, properties);
action.execute(hostConnection, new ApiCallback<VideoType.DetailsMovie>() {
@Override
public void onSuccess(VideoType.DetailsMovie result) {
deleteMovies(contentResolver, hostId, movieId);
List<VideoType.DetailsMovie> movies = new ArrayList<VideoType.DetailsMovie>(1);
movies.add(result);
insertMovies(orchestrator, contentResolver, movies);
orchestrator.syncItemFinished();
}
@Override
public void onError(int errorCode, String description) {
// Ok, something bad happend, just quit
orchestrator.syncItemFailed(errorCode, description);
}
}, callbackHandler);
}
}
/**
* Syncs all the movies, calling itself recursively
* Uses the {@link VideoLibrary.GetMovies} version with limits to make sure
* that Kodi doesn't blow up, and calls itself recursively until all the
* movies are returned
*/
private void syncAllMovies(final SyncOrchestrator orchestrator,
final HostConnection hostConnection,
final Handler callbackHandler,
final ContentResolver contentResolver,
final String properties[],
final int startIdx) {
// Call GetMovies with the current limits set
ListType.Limits limits = new ListType.Limits(startIdx, startIdx + LIMIT_SYNC_MOVIES);
VideoLibrary.GetMovies action = new VideoLibrary.GetMovies(limits, properties);
action.execute(hostConnection, new ApiCallback<ApiList<VideoType.DetailsMovie>>() {
@Override
public void onSuccess(ApiList<VideoType.DetailsMovie> result) {
ListType.LimitsReturned limitsReturned = null;
if (result != null) {
limitsReturned = result.limits;
}
if (startIdx == 0) {
// First call, delete movies from DB
deleteMovies(contentResolver, hostId, -1);
}
if (!result.items.isEmpty()) {
insertMovies(orchestrator, contentResolver, result.items);
}
LogUtils.LOGD(TAG, "syncAllMovies, movies gotten: " + result.items.size());
if (SyncUtils.moreItemsAvailable(limitsReturned)) {
// Max limit returned, there may be some more movies
// As we're going to recurse, these result objects can add up, so
// let's help the GC and indicate that we don't need this memory
// (hopefully this works)
result = null;
syncAllMovies(orchestrator, hostConnection, callbackHandler, contentResolver,
properties, startIdx + LIMIT_SYNC_MOVIES);
} else {
// Less than the limit was returned so we can finish
// (if it returned more there's a bug in Kodi but it
// shouldn't be a problem as they got inserted in the DB)
orchestrator.syncItemFinished();
}
}
@Override
public void onError(int errorCode, String description) {
// Ok, something bad happened, just quit
orchestrator.syncItemFailed(errorCode, description);
}
}, callbackHandler);
}
/**
* Deletes one or all movies from the database (pass -1 on movieId to delete all)
*/
private void deleteMovies(final ContentResolver contentResolver,
int hostId, int movieId) {
if (movieId == -1) {
// Delete all movies
String where = MediaContract.MoviesColumns.HOST_ID + "=?";
contentResolver.delete(MediaContract.MovieCast.CONTENT_URI,
where, new String[]{String.valueOf(hostId)});
contentResolver.delete(MediaContract.Movies.CONTENT_URI,
where, new String[]{String.valueOf(hostId)});
} else {
// Delete a movie
contentResolver.delete(MediaContract.MovieCast.buildMovieCastListUri(hostId, movieId),
null, null);
contentResolver.delete(MediaContract.Movies.buildMovieUri(hostId, movieId),
null, null);
}
}
/**
* Inserts the given movies in the database
*/
private void insertMovies(final SyncOrchestrator orchestrator,
final ContentResolver contentResolver,
final List<VideoType.DetailsMovie> movies) {
ContentValues movieValuesBatch[] = new ContentValues[movies.size()];
int castCount = 0;
// Iterate on each movie
for (int i = 0; i < movies.size(); i++) {
VideoType.DetailsMovie movie = movies.get(i);
movieValuesBatch[i] = SyncUtils.contentValuesFromMovie(hostId, movie);
castCount += movie.cast.size();
}
// Insert the movies
contentResolver.bulkInsert(MediaContract.Movies.CONTENT_URI, movieValuesBatch);
ContentValues movieCastValuesBatch[] = new ContentValues[castCount];
int count = 0;
// Iterate on each movie/cast
for (VideoType.DetailsMovie movie : movies) {
for (VideoType.Cast cast : movie.cast) {
movieCastValuesBatch[count] = SyncUtils.contentValuesFromCast(hostId, cast);
movieCastValuesBatch[count].put(MediaContract.MovieCastColumns.MOVIEID, movie.movieid);
count++;
}
}
// Insert the cast list for this movie
contentResolver.bulkInsert(MediaContract.MovieCast.CONTENT_URI, movieCastValuesBatch);
}
}