Added core support for DB synchronization.

This commit is contained in:
Jonathan Bernard 2016-03-30 01:12:41 -05:00
parent 9c007b3de5
commit d6edd3f11d
2 changed files with 89 additions and 2 deletions

View File

@ -50,7 +50,10 @@ public class MediaLibrary {
staleBookmarks.each { dbapi.delete(it) } } }
public def rescanLibrary() {
def results = [ total: 0, ignored: 0, new: 0]
def results = [ total: 0, ignored: 0, new: 0, present: 0, absent: 0]
List<MediaFile> missingFiles = dbapi.getMediaFiles()
List<MediaFile> foundFiles = []
Date startDate = new Date()
libraryRoot.eachFileRecurse { file ->
@ -60,7 +63,23 @@ public class MediaLibrary {
results.total++
if (!mf) results.ignored++
else if (mf.dateAdded > startDate) results.new++ }
else {
foundFiles << mf
if (missingFiles.contains(mf)) missingFiles.remove(mf)
if (mf.dateAdded > startDate) results.new++ } }
foundFiles.each { mf ->
if (!mf.presentLocally) {
mf.presentLocally = true
dbapi.update(mf) } }
missingFiles.each { mf ->
if (mf.presentLocally) {
mf.presentLocally = false
dbapi.update(mf) } }
results.present = foundFiles.size()
results.absent = missingFiles.size()
return results }

View File

@ -927,4 +927,72 @@ public class DbApi {
static def getInstanceFields(Class modelClass) {
return modelClass.fields.findAll { !Modifier.isStatic(it.modifiers) } }
/// ### DB Sync/Replication
public def diffWith(DbApi that) {
def results = [
ours: [ modelIds: [:], associations: [:] ],
theirs:[ modelIds: [:], associations: [:] ] ]
[Album, Artist, Image, MediaFile, Playlist, Bookmark,
Tag].each { modelClass ->
List<UUID> ourIds = this.getAllIds(modelClass)
List<UUID> theirIds = that.getAllIds(modelClass)
results.ours.modelIds[modelClass] = ourIds - theirIds
results.theirs.modelIds[modelClass] = theirIds - ourIds }
['albums_images', 'albums_media_files', 'artists_albums',
'artists_images', 'artists_media_files', 'media_files_tags',
'playlists_media_files'].each { tableName ->
def query = 'SELECT * FROM ' + tableName;
def allOurRows = this.sql.rows(query)
def allTheirRows = that.sql.rows(query)
def ourRows = allOurRows.clone()
def theirRows = allTheirRows.clone()
allOurRows.each { ourRow ->
allTheirRows.each { theirRow ->
if (ourRow[0] == theirRow[0]) {
ourRows.remove(ourRow)
theirRows.remove(theirRow) } } }
results.ours.associations[tableName] = ourRows
results.theirs.associations[tableName] = theirRows }
return results }
public def ingestDiff(DbApi that, def diff) {
diff.modelIds.each { modelClass, ids ->
ids.each { id -> this.create(that.getById(modelClass, id)) } }
diff.associations.each { tableName, rows ->
String placeholders = null
String query = null
rows.each { row ->
if (!placeholders)
placeholders = (1..row.size()).collect { '?' }.join(', ')
if (!query)
query = "INSERT INTO ${tableName} VALUES (${placeholders})"
logger.debug("Adding association.\n\tSQL: {}\n\tPARAMS: {}",
query, row.values() as List)
this.sql.executeInsert(query, row.values() as List) } } }
public def syncWith(DbApi that, boolean pull = true,
boolean push = false) {
def diff = this.diffWith(that)
if (pull) this.ingestDiff(that, diff.theirs)
if (push) that.ingestDiff(this, diff.ours)
return diff }
}