diff --git a/anki/media.py b/anki/media.py index 0f25108c0..2473e5298 100644 --- a/anki/media.py +++ b/anki/media.py @@ -175,6 +175,9 @@ If the same name exists, compare checksums.""" def clearLog(self): self.db.execute("delete from log") + def hasChanged(self): + return self.db.scalar("select 1 from log limit 1") + # Tracking changes (private) ########################################################################## diff --git a/anki/sync.py b/anki/sync.py index 725cff79a..e98ddc2fc 100644 --- a/anki/sync.py +++ b/anki/sync.py @@ -47,8 +47,8 @@ class Syncer(object): "Returns 'noChanges', 'fullSync', or 'success'." # step 1: login & metadata self.status("login") - self.rmod, rscm, self.maxUsn, rts = self.server.meta() - self.lmod, lscm, self.minUsn, lts = self.meta() + self.rmod, rscm, self.maxUsn, rts, self.mediaUsn = self.server.meta() + self.lmod, lscm, self.minUsn, lts, dummy = self.meta() if abs(rts - lts) > 300: return "clockOff" if self.lmod == self.rmod: @@ -90,7 +90,7 @@ class Syncer(object): return "success" def meta(self): - return (self.deck.mod, self.deck.scm, self.deck._usn, intTime()) + return (self.deck.mod, self.deck.scm, self.deck._usn, intTime(), None) def changes(self): "Bundle up deletions and small objects, and apply if server." @@ -535,21 +535,25 @@ class MediaSyncer(object): self.server = server self.added = None - def sync(self): - # step 1: send/recv deletions + def sync(self, mediaUsn): + # step 1: check if there have been any changes + self.deck.media.findChanges() + lusn = self.deck.media.usn() + if lusn == mediaUsn and not self.deck.media.hasChanged(): + return "noChanges" + # step 2: send/recv deletions runHook("mediaSync", "remove") - usn = self.deck.media.usn() lrem = self.removed() - rrem = self.server.remove(fnames=lrem, minUsn=usn) + rrem = self.server.remove(fnames=lrem, minUsn=lusn) self.remove(rrem) - # step 2: stream files from server + # step 3: stream files from server runHook("mediaSync", "server") while 1: runHook("mediaSync", "stream") zip = self.server.files() if self.addFiles(zip=zip) != "continue": break - # step 3: stream files to the server + # step 4: stream files to the server runHook("mediaSync", "client") while 1: runHook("mediaSync", "stream") @@ -558,10 +562,12 @@ class MediaSyncer(object): if usn != "continue": # when server has run out of files, it returns bumped usn break + # step 5: finalize self.deck.media.setUsn(usn) self.deck.media.clearLog() # clear cursor so successive calls work self.added = None + return "success" def removed(self): return self.deck.media.removed() diff --git a/tests/test_sync.py b/tests/test_sync.py index eda1d92a2..225993a2c 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -19,6 +19,7 @@ deck1=None deck2=None client=None server=None +server2=None def setup_basic(): global deck1, deck2, client, server @@ -271,7 +272,7 @@ def setup_remote(): def test_meta(): global TEST_REMOTE try: - (mod, scm, usn, ts) = server.meta() + (mod, scm, usn, ts, dummy) = server.meta() except Exception, e: if e.errno == 61: TEST_REMOTE = False @@ -330,9 +331,10 @@ def test_remoteSync(): # the current directory is the media folder. def setup_remoteMedia(): - global client, server + global client, server, server2 setup_basic() server = RemoteMediaServer(TEST_HKEY) + server2 = RemoteServer(TEST_USER, TEST_HKEY) client = MediaSyncer(deck1, server) @nose.with_setup(setup_remoteMedia) @@ -340,26 +342,29 @@ def test_media(): server.mediatest("reset") assert len(os.listdir(deck1.media.dir())) == 0 assert server.mediatest("count") == 0 + # initially, nothing to do + assert client.sync(server2.meta()[4]) == "noChanges" # add a file + time.sleep(1) os.chdir(deck1.media.dir()) p = os.path.join(deck1.media.dir(), "foo.jpg") open(p, "wb").write("foo") assert len(os.listdir(deck1.media.dir())) == 1 assert server.mediatest("count") == 0 - client.sync() + assert client.sync(server2.meta()[4]) == "success" time.sleep(1) # should have been synced assert len(os.listdir(deck1.media.dir())) == 1 assert server.mediatest("count") == 1 # if we remove the file, should be removed os.unlink(p) - client.sync() + assert client.sync(server2.meta()[4]) == "success" assert len(os.listdir(deck1.media.dir())) == 0 assert server.mediatest("count") == 0 # we should be able to add it again time.sleep(1) open(p, "wb").write("foo") - client.sync() + assert client.sync(server2.meta()[4]) == "success" assert len(os.listdir(deck1.media.dir())) == 1 assert server.mediatest("count") == 1 # if we modify it, it should get sent too. also we set the zip size very @@ -369,7 +374,7 @@ def test_media(): open(p, "wb").write("bar") open(p+"2", "wb").write("baz") assert len(os.listdir(deck1.media.dir())) == 2 - client.sync() + client.sync(server2.meta()[4]) assert len(os.listdir(deck1.media.dir())) == 2 assert server.mediatest("count") == 2 # if we lose our media db, we should be able to bring it back in sync @@ -379,12 +384,12 @@ def test_media(): deck1.media.connect() changes = deck1.media.added().fetchall() assert len(changes) == 2 - client.sync() + client.sync(server2.meta()[4]) assert len(os.listdir(deck1.media.dir())) == 2 assert server.mediatest("count") == 2 # if we send an unchanged file, the server should cope time.sleep(1) deck1.media.db.execute("insert into log values ('foo.jpg', 0)") - client.sync() + client.sync(server2.meta()[4]) assert len(os.listdir(deck1.media.dir())) == 2 assert server.mediatest("count") == 2