Change the source of AlbumGenres table to support Kodi v18

As detailed in https://github.com/xbmc/xbmc/pull/13051, Kodi v18 changes the way album genres are handled, as the genre (by which i mean `genreId`) ceases to be available at the album level, being only available at the songs level.
This impacts Kore because `GetAlbums` and `GetAlbumDetails` won't return the `genreId` tag, from which the local AlbumGenres table was populated.

This PR changes the source of the local AlbumGenres table to be the `genreId` returned at the song level (by `GetSongs`), to make it somewhat more consistent with the way Kodi will handle things from now on.
This commit is contained in:
Synced Synapse 2018-02-21 20:11:21 +00:00 committed by Martijn Brekhof
parent 1b329e2dd6
commit 246693a7cc
10 changed files with 12680 additions and 58 deletions

View File

@ -16,7 +16,6 @@
package org.xbmc.kore.host;
import org.xbmc.kore.jsonrpc.HostConnection;
import org.xbmc.kore.jsonrpc.method.System;
import org.xbmc.kore.utils.LogUtils;
import java.io.UnsupportedEncodingException;
@ -55,11 +54,20 @@ public class HostInfo {
*/
public static final int DEFAULT_EVENT_SERVER_PORT = 9777;
public static final int DEFAULT_KODI_VERSION_MAJOR = 16;
public static final int KODI_V12_FRODO = 12;
public static final int KODI_V13_GOTHAM = 13;
public static final int KODI_V14_HELIX = 14;
public static final int KODI_V15_ISENGARD = 15;
public static final int KODI_V16_JARVIS = 16;
public static final int KODI_V17_KRYPTON = 17;
public static final int KODI_V18_LEIA = 18;
public static final int DEFAULT_KODI_VERSION_MAJOR = KODI_V16_JARVIS;
public static final int DEFAULT_KODI_VERSION_MINOR = 1;
public static final String DEFAULT_KODI_VERSION_REVISION = "Unknown";
public static final String DEFAULT_KODI_VERSION_TAG = "stable";
/**
* Internal id of the host
*/
@ -291,6 +299,14 @@ public class HostInfo {
this.kodiVersionTag = kodiVersionTag;
}
public boolean isGothamOrLater() {
return kodiVersionMajor > KODI_V13_GOTHAM;
}
public boolean isKryptonOrLater() {
return kodiVersionMajor > KODI_V17_KRYPTON;
}
/**
* Returns the URL of the host
* @return HTTP URL eg. http://192.168.1.1:8080

View File

@ -15,7 +15,6 @@
*/
package org.xbmc.kore.jsonrpc;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Process;
import android.text.TextUtils;
@ -213,6 +212,14 @@ public class HostConnection {
return ((protocol == PROTOCOL_TCP) || (protocol == PROTOCOL_HTTP));
}
/**
* Returns this connection {@link HostInfo}
* @return This connection {@link HostInfo}
*/
public HostInfo getHostInfo() {
return hostInfo;
}
/**
* Registers an observer for player notifications
* @param observer The {@link PlayerNotificationsObserver}
@ -421,6 +428,7 @@ public class HostConnection {
final Handler handler) {
OkHttpClient client = getOkHttpClient();
String jsonRequest = method.toJsonString();
LogUtils.LOGD(TAG, "Sending request via HTTP: " + jsonRequest);
try {
Request request = new Request.Builder()

View File

@ -140,7 +140,7 @@ public class LibrarySyncService extends Service {
// Sync all music
boolean syncAllMusic = intent.getBooleanExtra(SYNC_ALL_MUSIC, false);
if (syncAllMusic) {
syncOrchestrator.addSyncItem(new SyncMusic(hostInfo.getId(), syncExtras));
syncOrchestrator.addSyncItem(new SyncMusic(syncExtras));
}
// Sync all music videos

View File

@ -41,21 +41,18 @@ public class SyncMusic extends SyncItem {
private static final int LIMIT_SYNC_ALBUMS = 300;
private static final int LIMIT_SYNC_SONGS = 600;
private final int hostId;
private final Bundle syncExtras;
/**
* Syncs all the music on selected XBMC to the local database
* @param hostId XBMC host id
* Syncs all the music to the local database
*/
public SyncMusic(final int hostId, Bundle syncExtras) {
this.hostId = hostId;
public SyncMusic(Bundle syncExtras) {
this.syncExtras = syncExtras;
}
/** {@inheritDoc} */
public String getDescription() {
return "Sync music for host: " + hostId;
return "Sync music";
}
/** {@inheritDoc} */
@ -91,11 +88,13 @@ public class SyncMusic extends SyncItem {
* Gets all artists recursively and forwards the call to Genres
* Genres->Albums->Songs
*/
public void chainCallSyncArtists(final SyncOrchestrator orchestrator,
private void chainCallSyncArtists(final SyncOrchestrator orchestrator,
final HostConnection hostConnection,
final Handler callbackHandler,
final ContentResolver contentResolver,
final int startIdx) {
final int hostId = hostConnection.getHostInfo().getId();
// Artists->Genres->Albums->Songs
// Only gets album artists (first parameter)
ListType.Limits limits = new ListType.Limits(startIdx, startIdx + LIMIT_SYNC_ARTISTS);
@ -116,7 +115,7 @@ public class SyncMusic extends SyncItem {
// First delete all music info
if (startIdx == 0) deleteMusicInfo(contentResolver, hostId);
insertArtists(items, contentResolver);
insertArtists(hostId, items, contentResolver);
if (SyncUtils.moreItemsAvailable(limitsReturned)) {
LogUtils.LOGD(TAG, "chainCallSyncArtists: More results on media center, recursing.");
@ -170,13 +169,15 @@ public class SyncMusic extends SyncItem {
final HostConnection hostConnection,
final Handler callbackHandler,
final ContentResolver contentResolver) {
final int hostId = hostConnection.getHostInfo().getId();
// Genres->Albums->Songs
AudioLibrary.GetGenres action = new AudioLibrary.GetGenres(getGenresProperties);
action.execute(hostConnection, new ApiCallback<List<LibraryType.DetailsGenre>>() {
@Override
public void onSuccess(List<LibraryType.DetailsGenre> result) {
if (result != null)
insertGenresItems(result, contentResolver);
insertGenresItems(hostId, result, contentResolver);
chainCallSyncAlbums(orchestrator, hostConnection, callbackHandler, contentResolver, 0);
}
@ -199,7 +200,7 @@ public class SyncMusic extends SyncItem {
//AudioType.FieldsAlbum.MUSICBRAINZALBUMID,
//AudioType.FieldsAlbum.MUSICBRAINZALBUMARTISTID,
AudioType.FieldsAlbum.FANART, AudioType.FieldsAlbum.THUMBNAIL,
AudioType.FieldsAlbum.PLAYCOUNT, AudioType.FieldsAlbum.GENREID,
AudioType.FieldsAlbum.PLAYCOUNT, // AudioType.FieldsAlbum.GENREID,
AudioType.FieldsAlbum.ARTISTID, AudioType.FieldsAlbum.DISPLAYARTIST
};
@ -212,10 +213,13 @@ public class SyncMusic extends SyncItem {
final Handler callbackHandler,
final ContentResolver contentResolver,
final int startIdx) {
final int hostId = hostConnection.getHostInfo().getId();
final long albumSyncStartTime = System.currentTimeMillis();
// Albums->Songs
ListType.Limits limits = new ListType.Limits(startIdx, startIdx + LIMIT_SYNC_ALBUMS);
AudioLibrary.GetAlbums action = new AudioLibrary.GetAlbums(limits, getAlbumsProperties);
action.execute(hostConnection, new ApiCallback<ApiList<AudioType.DetailsAlbum>>() {
@Override
public void onSuccess(ApiList<AudioType.DetailsAlbum> result) {
@ -230,7 +234,7 @@ public class SyncMusic extends SyncItem {
}
// Insert the partial results
insertAlbumsItems(items, contentResolver);
insertAlbumsItems(hostId, items, contentResolver);
LogUtils.LOGD(TAG, "Finished inserting artists and genres in: " +
(System.currentTimeMillis() - albumSyncStartTime));
@ -271,7 +275,7 @@ public class SyncMusic extends SyncItem {
AudioType.FieldsSong.ALBUMID,
//AudioType.FieldsSong.LASTPLAYED,
AudioType.FieldsSong.DISC,
//AudioType.FieldsSong.GENREID,
AudioType.FieldsSong.GENREID,
AudioType.FieldsSong.ARTISTID,
// AudioType.FieldsSong.ALBUMARTISTID,
AudioType.FieldsSong.DISPLAYARTIST,
@ -285,6 +289,7 @@ public class SyncMusic extends SyncItem {
final Handler callbackHandler,
final ContentResolver contentResolver,
final int startIdx) {
final int hostId = hostConnection.getHostInfo().getId();
// Songs
ListType.Limits limits = new ListType.Limits(startIdx, startIdx + LIMIT_SYNC_SONGS);
AudioLibrary.GetSongs action = new AudioLibrary.GetSongs(limits, getSongsProperties);
@ -302,7 +307,7 @@ public class SyncMusic extends SyncItem {
}
// Save partial results to DB
insertSongsItems(items, contentResolver);
insertSongsItems(hostId, items, contentResolver);
if (SyncUtils.moreItemsAvailable(limitsReturned)) {
LogUtils.LOGD(TAG, "chainCallSyncSongs: More results on media center, recursing.");
@ -324,7 +329,7 @@ public class SyncMusic extends SyncItem {
}, callbackHandler);
}
public void insertArtists(List<AudioType.DetailsArtist> items, ContentResolver contentResolver) {
public void insertArtists(int hostId, List<AudioType.DetailsArtist> items, ContentResolver contentResolver) {
ContentValues artistValuesBatch[] = new ContentValues[items.size()];
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsArtist artist = items.get(i);
@ -333,7 +338,7 @@ public class SyncMusic extends SyncItem {
contentResolver.bulkInsert(MediaContract.Artists.CONTENT_URI, artistValuesBatch);
}
public void insertGenresItems(List<LibraryType.DetailsGenre> items, ContentResolver contentResolver) {
public void insertGenresItems(int hostId, List<LibraryType.DetailsGenre> items, ContentResolver contentResolver) {
ContentValues genresValuesBatch[] = new ContentValues[items.size()];
for (int i = 0; i < items.size(); i++) {
@ -345,22 +350,20 @@ public class SyncMusic extends SyncItem {
contentResolver.bulkInsert(MediaContract.AudioGenres.CONTENT_URI, genresValuesBatch);
}
public void insertAlbumsItems(List<AudioType.DetailsAlbum> items, ContentResolver contentResolver) {
public void insertAlbumsItems(int hostId, List<AudioType.DetailsAlbum> items, ContentResolver contentResolver) {
ContentValues albumValuesBatch[] = new ContentValues[items.size()];
int artistsCount = 0, genresCount = 0;
int artistsCount = 0;
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsAlbum album = items.get(i);
albumValuesBatch[i] = SyncUtils.contentValuesFromAlbum(hostId, album);
artistsCount += album.artistid.size();
genresCount += album.genreid.size();
}
contentResolver.bulkInsert(MediaContract.Albums.CONTENT_URI, albumValuesBatch);
// Iterate on each album, collect the artists and the genres and insert them
// Iterate on each album, collect the artists and insert them
ContentValues albumArtistsValuesBatch[] = new ContentValues[artistsCount];
ContentValues albumGenresValuesBatch[] = new ContentValues[genresCount];
int artistCount = 0, genreCount = 0;
int artistCount = 0;
for (AudioType.DetailsAlbum album : items) {
for (int artistId : album.artistid) {
albumArtistsValuesBatch[artistCount] = new ContentValues();
@ -369,34 +372,27 @@ public class SyncMusic extends SyncItem {
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ARTISTID, artistId);
artistCount++;
}
for (int genreId : album.genreid) {
albumGenresValuesBatch[genreCount] = new ContentValues();
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.HOST_ID, hostId);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.ALBUMID, album.albumid);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.GENREID, genreId);
genreCount++;
}
}
contentResolver.bulkInsert(MediaContract.AlbumArtists.CONTENT_URI, albumArtistsValuesBatch);
contentResolver.bulkInsert(MediaContract.AlbumGenres.CONTENT_URI, albumGenresValuesBatch);
}
public void insertSongsItems(List<AudioType.DetailsSong> items, ContentResolver contentResolver) {
public void insertSongsItems(int hostId, List<AudioType.DetailsSong> items, ContentResolver contentResolver) {
ContentValues songValuesBatch[] = new ContentValues[items.size()];
int totalArtistsCount = 0;
int totalArtistsCount = 0, totalGenresCount = 0;
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsSong song = items.get(i);
songValuesBatch[i] = SyncUtils.contentValuesFromSong(hostId, song);
totalArtistsCount += song.artistid.size();
totalGenresCount += song.genreid.size();
}
contentResolver.bulkInsert(MediaContract.Songs.CONTENT_URI, songValuesBatch);
// Iterate on each song, collect the artists and insert them
// Iterate on each song, collect the artists and the genres and insert them
ContentValues songArtistsValuesBatch[] = new ContentValues[totalArtistsCount];
int artistCount = 0;
ContentValues songGenresValuesBatch[] = new ContentValues[totalGenresCount];
int artistCount = 0, genreCount = 0;
for (AudioType.DetailsSong song : items) {
for (int artistId : song.artistid) {
songArtistsValuesBatch[artistCount] = new ContentValues();
@ -405,8 +401,17 @@ public class SyncMusic extends SyncItem {
songArtistsValuesBatch[artistCount].put(MediaContract.SongArtists.ARTISTID, artistId);
artistCount++;
}
for (int genreId : song.genreid) {
songGenresValuesBatch[genreCount] = new ContentValues();
songGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.HOST_ID, hostId);
songGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.ALBUMID, song.albumid);
songGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.GENREID, genreId);
genreCount++;
}
}
contentResolver.bulkInsert(MediaContract.SongArtists.CONTENT_URI, songArtistsValuesBatch);
contentResolver.bulkInsert(MediaContract.AlbumGenres.CONTENT_URI, songGenresValuesBatch);
}
}

View File

@ -21,7 +21,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MenuItem;
@ -451,10 +450,10 @@ public class NowPlayingFragment extends Fragment
case 0:
// Download subtitles. First check host version to see which method to call
HostInfo hostInfo = hostManager.getHostInfo();
if (hostInfo.getKodiVersionMajor() < 13) {
showDownloadSubtitlesPreGotham();
} else {
if (hostInfo.isGothamOrLater()) {
showDownloadSubtitlesPostGotham();
} else {
showDownloadSubtitlesPreGotham();
}
break;
case 1:

View File

@ -629,10 +629,10 @@ public class RemoteFragment extends Fragment
HostInfo hostInfo = hostManager.getHostInfo();
// Info button, v17 uses a different window to display codec info so check version number
if (hostInfo.getKodiVersionMajor() < 17) {
action = new Input.ExecuteAction(Input.ExecuteAction.CODECINFO);
} else {
if (hostInfo.isKryptonOrLater()) {
action = new Input.ExecuteAction(Input.ExecuteAction.PLAYERPROCESSINFO);
} else {
action = new Input.ExecuteAction(Input.ExecuteAction.CODECINFO);
}
action.execute(hostManager.getConnection(), defaultActionCallback, callbackHandler);

View File

@ -44,12 +44,12 @@ public class Database {
public static final String TAG = LogUtils.makeLogTag(Database.class);
public static HostInfo fill(HostInfo hostInfo, Context context, ContentResolver contentResolver) throws ApiException, IOException {
SyncMusic syncMusic = new SyncMusic(hostInfo.getId(), null);
SyncMusic syncMusic = new SyncMusic(null);
insertMovies(context, contentResolver, hostInfo.getId());
insertArtists(context, contentResolver, syncMusic);
insertGenres(context, contentResolver, syncMusic);
insertAlbums(context, contentResolver, syncMusic);
insertSongs(context, contentResolver, syncMusic);
insertArtists(context, contentResolver, syncMusic, hostInfo.getId());
insertGenres(context, contentResolver, syncMusic, hostInfo.getId());
insertAlbums(context, contentResolver, syncMusic, hostInfo.getId());
insertSongs(context, contentResolver, syncMusic, hostInfo.getId());
SyncTVShows syncTVShows = new SyncTVShows(hostInfo.getId(), null);
insertTVShows(context, contentResolver, syncTVShows);
@ -115,37 +115,37 @@ public class Database {
contentResolver.bulkInsert(MediaContract.MovieCast.CONTENT_URI, movieCastValuesBatch);
}
private static void insertArtists(Context context, ContentResolver contentResolver, SyncMusic syncMusic) throws ApiException, IOException {
private static void insertArtists(Context context, ContentResolver contentResolver, SyncMusic syncMusic, int hostId) throws ApiException, IOException {
AudioLibrary.GetArtists getArtists = new AudioLibrary.GetArtists(false);
String result = FileUtils.readFile(context, "AudioLibrary.GetArtists.json");
ArrayList<AudioType.DetailsArtist> artistList = (ArrayList) getArtists.resultFromJson(result).items;
syncMusic.insertArtists(artistList, contentResolver);
syncMusic.insertArtists(hostId, artistList, contentResolver);
}
private static void insertGenres(Context context, ContentResolver contentResolver, SyncMusic syncMusic) throws ApiException, IOException {
private static void insertGenres(Context context, ContentResolver contentResolver, SyncMusic syncMusic, int hostId) throws ApiException, IOException {
AudioLibrary.GetGenres getGenres = new AudioLibrary.GetGenres();
ArrayList<LibraryType.DetailsGenre> genreList =
(ArrayList) getGenres.resultFromJson(FileUtils.readFile(context,
"AudioLibrary.GetGenres.json"));
syncMusic.insertGenresItems(genreList, contentResolver);
syncMusic.insertGenresItems(hostId, genreList, contentResolver);
}
private static void insertAlbums(Context context, ContentResolver contentResolver, SyncMusic syncMusic) throws ApiException, IOException {
private static void insertAlbums(Context context, ContentResolver contentResolver, SyncMusic syncMusic, int hostId) throws ApiException, IOException {
AudioLibrary.GetAlbums getAlbums = new AudioLibrary.GetAlbums();
String result = FileUtils.readFile(context, "AudioLibrary.GetAlbums.json");
ArrayList<AudioType.DetailsAlbum> albumList = (ArrayList) getAlbums.resultFromJson(result).items;
syncMusic.insertAlbumsItems(albumList, contentResolver);
syncMusic.insertAlbumsItems(hostId, albumList, contentResolver);
}
private static void insertSongs(Context context, ContentResolver contentResolver, SyncMusic syncMusic) throws ApiException, IOException {
private static void insertSongs(Context context, ContentResolver contentResolver, SyncMusic syncMusic, int hostId) throws ApiException, IOException {
AudioLibrary.GetSongs getSongs = new AudioLibrary.GetSongs();
ArrayList<AudioType.DetailsSong> songList = (ArrayList)
getSongs.resultFromJson(FileUtils.readFile(context, "AudioLibrary.GetSongs.json")).items;
syncMusic.insertSongsItems(songList, contentResolver);
syncMusic.insertSongsItems(hostId, songList, contentResolver);
}
private static void insertTVShows(Context context, ContentResolver contentResolver, SyncTVShows syncTVShows)

File diff suppressed because it is too large Load Diff