mirror of
https://github.com/ankitects/anki.git
synced 2025-11-11 07:07:13 -05:00
bulk media support -> local media copy, always send media table
This commit is contained in:
parent
aca3ea2513
commit
3d81181323
4 changed files with 52 additions and 187 deletions
|
|
@ -12,7 +12,7 @@ import itertools, time, re
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from anki import DeckStorage
|
from anki import DeckStorage
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from anki.sync import SyncClient, SyncServer, BulkMediaSyncer
|
from anki.sync import SyncClient, SyncServer, copyLocalMedia
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from anki.utils import findTag, parseTags, stripHTML, ids2str
|
from anki.utils import findTag, parseTags, stripHTML, ids2str
|
||||||
from anki.tags import tagIds
|
from anki.tags import tagIds
|
||||||
|
|
@ -118,11 +118,7 @@ modified = :now
|
||||||
self.newDeck.s.statement("""
|
self.newDeck.s.statement("""
|
||||||
delete from stats""")
|
delete from stats""")
|
||||||
# media
|
# media
|
||||||
if client.mediaSyncPending:
|
copyLocalMedia(client.deck, server.deck)
|
||||||
bulkClient = BulkMediaSyncer(client.deck)
|
|
||||||
bulkServer = BulkMediaSyncer(server.deck)
|
|
||||||
bulkClient.server = bulkServer
|
|
||||||
bulkClient.sync()
|
|
||||||
# need to save manually
|
# need to save manually
|
||||||
self.newDeck.rebuildCounts()
|
self.newDeck.rebuildCounts()
|
||||||
self.newDeck.updateAllPriorities()
|
self.newDeck.updateAllPriorities()
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ __docformat__ = 'restructuredtext'
|
||||||
|
|
||||||
from anki import DeckStorage
|
from anki import DeckStorage
|
||||||
from anki.importing import Importer
|
from anki.importing import Importer
|
||||||
from anki.sync import SyncClient, SyncServer, BulkMediaSyncer
|
from anki.sync import SyncClient, SyncServer, copyLocalMedia
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from anki.utils import ids2str
|
from anki.utils import ids2str
|
||||||
from anki.deck import NEW_CARDS_RANDOM
|
from anki.deck import NEW_CARDS_RANDOM
|
||||||
|
|
@ -57,12 +57,7 @@ class Anki10Importer(Importer):
|
||||||
res = server.applyPayload(payload)
|
res = server.applyPayload(payload)
|
||||||
self.deck.updateProgress()
|
self.deck.updateProgress()
|
||||||
client.applyPayloadReply(res)
|
client.applyPayloadReply(res)
|
||||||
if client.mediaSyncPending:
|
copyLocalMedia(server.deck, client.deck)
|
||||||
bulkClient = BulkMediaSyncer(client.deck)
|
|
||||||
bulkServer = BulkMediaSyncer(server.deck)
|
|
||||||
bulkClient.oneWay = True
|
|
||||||
bulkClient.server = bulkServer
|
|
||||||
bulkClient.sync()
|
|
||||||
# add tags
|
# add tags
|
||||||
self.deck.updateProgress()
|
self.deck.updateProgress()
|
||||||
fids = [f[0] for f in res['added-facts']['facts']]
|
fids = [f[0] for f in res['added-facts']['facts']]
|
||||||
|
|
|
||||||
196
anki/sync.py
196
anki/sync.py
|
|
@ -20,7 +20,7 @@ createDeck(name): create a deck on the server
|
||||||
"""
|
"""
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
|
|
||||||
import zlib, re, urllib, urllib2, socket, simplejson, time
|
import zlib, re, urllib, urllib2, socket, simplejson, time, shutil
|
||||||
import os, base64, httplib, sys, tempfile, httplib
|
import os, base64, httplib, sys, tempfile, httplib
|
||||||
from datetime import date
|
from datetime import date
|
||||||
import anki, anki.deck, anki.cards
|
import anki, anki.deck, anki.cards
|
||||||
|
|
@ -31,7 +31,6 @@ from anki.cards import Card
|
||||||
from anki.stats import Stats, globalStats
|
from anki.stats import Stats, globalStats
|
||||||
from anki.history import CardHistoryEntry
|
from anki.history import CardHistoryEntry
|
||||||
from anki.stats import globalStats
|
from anki.stats import globalStats
|
||||||
from anki.media import checksum
|
|
||||||
from anki.utils import ids2str, hexifyID
|
from anki.utils import ids2str, hexifyID
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from hooks import runHook
|
from hooks import runHook
|
||||||
|
|
@ -47,6 +46,8 @@ SYNC_HOST = "anki.ichi2.net"; SYNC_PORT = 80
|
||||||
#SYNC_URL = "http://localhost:8001/sync/"
|
#SYNC_URL = "http://localhost:8001/sync/"
|
||||||
#SYNC_HOST = "localhost"; SYNC_PORT = 8001
|
#SYNC_HOST = "localhost"; SYNC_PORT = 8001
|
||||||
|
|
||||||
|
KEYS = ("models", "facts", "cards", "media")
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Monkey-patch httplib to incrementally send instead of chewing up large
|
# Monkey-patch httplib to incrementally send instead of chewing up large
|
||||||
# amounts of memory, and track progress.
|
# amounts of memory, and track progress.
|
||||||
|
|
@ -93,7 +94,6 @@ class SyncTools(object):
|
||||||
self.deck = deck
|
self.deck = deck
|
||||||
self.diffs = {}
|
self.diffs = {}
|
||||||
self.serverExcludedTags = []
|
self.serverExcludedTags = []
|
||||||
self.mediaSyncPending = False
|
|
||||||
|
|
||||||
# Control
|
# Control
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
@ -109,11 +109,6 @@ class SyncTools(object):
|
||||||
payload = self.genPayload(sums)
|
payload = self.genPayload(sums)
|
||||||
res = self.server.applyPayload(payload)
|
res = self.server.applyPayload(payload)
|
||||||
self.applyPayloadReply(res)
|
self.applyPayloadReply(res)
|
||||||
if self.mediaSyncPending:
|
|
||||||
bulkClient = BulkMediaSyncer(self.deck)
|
|
||||||
bulkServer = BulkMediaSyncer(self.server.deck)
|
|
||||||
bulkClient.server = bulkServer
|
|
||||||
bulkClient.sync()
|
|
||||||
|
|
||||||
def prepareSync(self):
|
def prepareSync(self):
|
||||||
"Sync setup. True if sync needed."
|
"Sync setup. True if sync needed."
|
||||||
|
|
@ -137,7 +132,7 @@ class SyncTools(object):
|
||||||
self.preSyncRefresh()
|
self.preSyncRefresh()
|
||||||
payload = {}
|
payload = {}
|
||||||
# first, handle models, facts and cards
|
# first, handle models, facts and cards
|
||||||
for key in self.keys():
|
for key in KEYS:
|
||||||
diff = self.diffSummary(lsum, rsum, key)
|
diff = self.diffSummary(lsum, rsum, key)
|
||||||
payload["added-" + key] = self.getObjsFromKey(diff[0], key)
|
payload["added-" + key] = self.getObjsFromKey(diff[0], key)
|
||||||
payload["deleted-" + key] = diff[1]
|
payload["deleted-" + key] = diff[1]
|
||||||
|
|
@ -156,10 +151,12 @@ class SyncTools(object):
|
||||||
reply = {}
|
reply = {}
|
||||||
self.preSyncRefresh()
|
self.preSyncRefresh()
|
||||||
# model, facts and cards
|
# model, facts and cards
|
||||||
for key in self.keys():
|
for key in KEYS:
|
||||||
|
k = 'added-' + key
|
||||||
# send back any requested
|
# send back any requested
|
||||||
reply['added-' + key] = self.getObjsFromKey(
|
reply[k] = self.getObjsFromKey(
|
||||||
payload['missing-' + key], key)
|
payload['missing-' + key], key)
|
||||||
|
if k in payload:
|
||||||
self.updateObjsFromKey(payload['added-' + key], key)
|
self.updateObjsFromKey(payload['added-' + key], key)
|
||||||
self.deleteObjsFromKey(payload['deleted-' + key], key)
|
self.deleteObjsFromKey(payload['deleted-' + key], key)
|
||||||
# send back deck-related stuff if it wasn't sent to us
|
# send back deck-related stuff if it wasn't sent to us
|
||||||
|
|
@ -186,7 +183,10 @@ class SyncTools(object):
|
||||||
|
|
||||||
def applyPayloadReply(self, reply):
|
def applyPayloadReply(self, reply):
|
||||||
# model, facts and cards
|
# model, facts and cards
|
||||||
for key in self.keys():
|
for key in KEYS:
|
||||||
|
k = 'added-' + key
|
||||||
|
# old version may not send media
|
||||||
|
if k in reply:
|
||||||
self.updateObjsFromKey(reply['added-' + key], key)
|
self.updateObjsFromKey(reply['added-' + key], key)
|
||||||
# deck
|
# deck
|
||||||
if 'deck' in reply:
|
if 'deck' in reply:
|
||||||
|
|
@ -226,12 +226,6 @@ class SyncTools(object):
|
||||||
'lm': len(payload['added-models']),
|
'lm': len(payload['added-models']),
|
||||||
'rm': len(payload['missing-models']),
|
'rm': len(payload['missing-models']),
|
||||||
}
|
}
|
||||||
if self.mediaSupported():
|
|
||||||
h['lM'] = len(payload['added-media'])
|
|
||||||
h['rM'] = len(payload['missing-media'])
|
|
||||||
else:
|
|
||||||
h['lM'] = _("off")
|
|
||||||
h['rM'] = _("off")
|
|
||||||
if self.localTime > self.remoteTime:
|
if self.localTime > self.remoteTime:
|
||||||
h['ls'] = _('all')
|
h['ls'] = _('all')
|
||||||
h['rs'] = 0
|
h['rs'] = 0
|
||||||
|
|
@ -249,7 +243,6 @@ class SyncTools(object):
|
||||||
<tr><td>Cards</td><td>%(lc)d</td><td>%(rc)d</td></tr>
|
<tr><td>Cards</td><td>%(lc)d</td><td>%(rc)d</td></tr>
|
||||||
<tr><td>Facts</td><td>%(lf)d</td><td>%(rf)d</td></tr>
|
<tr><td>Facts</td><td>%(lf)d</td><td>%(rf)d</td></tr>
|
||||||
<tr><td>Models</td><td>%(lm)d</td><td>%(rm)d</td></tr>
|
<tr><td>Models</td><td>%(lm)d</td><td>%(rm)d</td></tr>
|
||||||
<tr><td>Media</td><td>%(lM)s</td><td>%(rM)s</td></tr>
|
|
||||||
<tr><td>Stats</td><td>%(ls)s</td><td>%(rs)s</td></tr>
|
<tr><td>Stats</td><td>%(ls)s</td><td>%(rs)s</td></tr>
|
||||||
</table>""") % p
|
</table>""") % p
|
||||||
|
|
||||||
|
|
@ -709,22 +702,13 @@ insert or replace into sources values
|
||||||
lastSync=s[3],
|
lastSync=s[3],
|
||||||
syncPeriod=s[4])
|
syncPeriod=s[4])
|
||||||
|
|
||||||
# Media
|
# Media metadata
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def getMedia(self, ids, updateCreated=False):
|
def getMedia(self, ids):
|
||||||
size = self.deck.s.scalar(
|
|
||||||
"select sum(size) from media where id in %s" %
|
|
||||||
ids2str(ids))
|
|
||||||
if ids:
|
|
||||||
self.mediaSyncPending = True
|
|
||||||
if updateCreated:
|
|
||||||
created = time.time()
|
|
||||||
else:
|
|
||||||
created = "created"
|
|
||||||
return [tuple(row) for row in self.deck.s.all("""
|
return [tuple(row) for row in self.deck.s.all("""
|
||||||
select id, filename, size, %s, originalPath, description
|
select id, filename, size, created, originalPath, description
|
||||||
from media where id in %s""" % (created, ids2str(ids)))]
|
from media where id in %s""" % ids2str(ids))]
|
||||||
|
|
||||||
def updateMedia(self, media):
|
def updateMedia(self, media):
|
||||||
meta = []
|
meta = []
|
||||||
|
|
@ -739,7 +723,6 @@ from media where id in %s""" % (created, ids2str(ids)))]
|
||||||
'description': m[5]})
|
'description': m[5]})
|
||||||
# apply metadata
|
# apply metadata
|
||||||
if meta:
|
if meta:
|
||||||
self.mediaSyncPending = True
|
|
||||||
self.deck.s.statements("""
|
self.deck.s.statements("""
|
||||||
insert or replace into media (id, filename, size, created,
|
insert or replace into media (id, filename, size, created,
|
||||||
originalPath, description)
|
originalPath, description)
|
||||||
|
|
@ -759,21 +742,6 @@ select id, :now from media
|
||||||
where media.id in %s""" % sids, now=time.time())
|
where media.id in %s""" % sids, now=time.time())
|
||||||
self.deck.s.execute(
|
self.deck.s.execute(
|
||||||
"delete from media where id in %s" % sids)
|
"delete from media where id in %s" % sids)
|
||||||
for file in files:
|
|
||||||
self.deleteMediaFile(file)
|
|
||||||
|
|
||||||
# the following routines are reimplemented by the anki server so that
|
|
||||||
# media can be shared and accounted
|
|
||||||
|
|
||||||
def deleteMediaFile(self, file):
|
|
||||||
try:
|
|
||||||
os.unlink(self.mediaPath(file))
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def mediaPath(self, path):
|
|
||||||
"Return the path to store media in. Defaults to the deck media dir."
|
|
||||||
return os.path.join(self.deck.mediaDir(create=True), path)
|
|
||||||
|
|
||||||
# One-way syncing (sharing)
|
# One-way syncing (sharing)
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
@ -810,12 +778,9 @@ where media.id in %s""" % sids, now=time.time())
|
||||||
"select id from models where modified > :l", l=lastSync)
|
"select id from models where modified > :l", l=lastSync)
|
||||||
p['models'] = self.getModels(modelIds, updateModified=True)
|
p['models'] = self.getModels(modelIds, updateModified=True)
|
||||||
# media
|
# media
|
||||||
if self.mediaSupported():
|
|
||||||
mediaIds = self.deck.s.column0(
|
mediaIds = self.deck.s.column0(
|
||||||
"select id from media where created > :l", l=lastSync)
|
"select id from media where created > :l", l=lastSync)
|
||||||
p['media'] = self.getMedia(mediaIds, updateCreated=True)
|
p['media'] = self.getMedia(mediaIds)
|
||||||
if p['media']:
|
|
||||||
self.mediaSyncPending = True
|
|
||||||
# cards
|
# cards
|
||||||
cardIds = self.deck.s.column0(
|
cardIds = self.deck.s.column0(
|
||||||
"select id from cards where modified > :l", l=lastSync)
|
"select id from cards where modified > :l", l=lastSync)
|
||||||
|
|
@ -823,7 +788,7 @@ where media.id in %s""" % sids, now=time.time())
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def applyOneWayPayload(self, payload):
|
def applyOneWayPayload(self, payload):
|
||||||
keys = [k for k in self.keys() if k != "cards"]
|
keys = [k for k in KEYS if k != "cards"]
|
||||||
# model, facts, media
|
# model, facts, media
|
||||||
for key in keys:
|
for key in keys:
|
||||||
self.updateObjsFromKey(payload[key], key)
|
self.updateObjsFromKey(payload[key], key)
|
||||||
|
|
@ -833,9 +798,6 @@ where media.id in %s""" % sids, now=time.time())
|
||||||
"where id = :id",
|
"where id = :id",
|
||||||
s=self.server.deckName,
|
s=self.server.deckName,
|
||||||
id=m['id'])
|
id=m['id'])
|
||||||
# if media arrived, we'll need to download the data
|
|
||||||
self.mediaSyncPending = (self.mediaSyncPending or
|
|
||||||
self.mediaSupported() and payload['media'])
|
|
||||||
# cards last, handled differently
|
# cards last, handled differently
|
||||||
t = time.time()
|
t = time.time()
|
||||||
try:
|
try:
|
||||||
|
|
@ -936,11 +898,6 @@ and cards.id in %s""" % ids2str([c[0] for c in cards])))
|
||||||
def updateObjsFromKey(self, ids, key):
|
def updateObjsFromKey(self, ids, key):
|
||||||
return getattr(self, "update" + key.capitalize())(ids)
|
return getattr(self, "update" + key.capitalize())(ids)
|
||||||
|
|
||||||
def keys(self):
|
|
||||||
if self.mediaSupported():
|
|
||||||
return standardKeys + ("media",)
|
|
||||||
return standardKeys
|
|
||||||
|
|
||||||
# Full sync
|
# Full sync
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
@ -1055,7 +1012,6 @@ and cards.id in %s""" % ids2str([c[0] for c in cards])))
|
||||||
# Local syncing
|
# Local syncing
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
standardKeys = ("models", "facts", "cards")
|
|
||||||
|
|
||||||
class SyncServer(SyncTools):
|
class SyncServer(SyncTools):
|
||||||
|
|
||||||
|
|
@ -1188,110 +1144,20 @@ class HttpSyncServer(SyncServer):
|
||||||
"Create a deck on the server. Not implemented."
|
"Create a deck on the server. Not implemented."
|
||||||
return self.stuff("OK")
|
return self.stuff("OK")
|
||||||
|
|
||||||
# Bulk uploader/downloader
|
# Local media copying
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
class BulkMediaSyncer(SyncTools):
|
def copyLocalMedia(src, dst):
|
||||||
|
src = src.mediaDir()
|
||||||
def __init__(self, deck):
|
if not src:
|
||||||
self.deck = deck
|
|
||||||
self.server = None
|
|
||||||
self.oneWay = False
|
|
||||||
|
|
||||||
def missingMedia(self):
|
|
||||||
fnames = self.deck.s.column0(
|
|
||||||
"select filename from media")
|
|
||||||
return [f for f in fnames if
|
|
||||||
not os.path.exists(self.mediaPath(f))]
|
|
||||||
|
|
||||||
def progressCallback(self, type, count, total, fname):
|
|
||||||
return
|
return
|
||||||
|
dst = dst.mediaDir(create=True)
|
||||||
def sync(self):
|
files = os.listdir(src)
|
||||||
# upload to server
|
for file in files:
|
||||||
if not self.oneWay:
|
srcfile = os.path.join(src, file)
|
||||||
missing = self.server.missingMedia()
|
dstfile = os.path.join(dst, file)
|
||||||
total = len(missing)
|
if not os.path.exists(dstfile):
|
||||||
for n in range(total):
|
|
||||||
fname = missing[n]
|
|
||||||
self.progressCallback('up', n, total, fname)
|
|
||||||
data = self.getFile(fname)
|
|
||||||
if data:
|
|
||||||
self.server.addFile(fname, data)
|
|
||||||
n += 1
|
|
||||||
if total > 0:
|
|
||||||
self.progressCallback('up', total, total, missing[total-1])
|
|
||||||
|
|
||||||
# download from server
|
|
||||||
missing = self.missingMedia()
|
|
||||||
total = len(missing)
|
|
||||||
for n in range(total):
|
|
||||||
fname = missing[n]
|
|
||||||
self.progressCallback('down', n, total, fname)
|
|
||||||
data = self.server.getFile(fname)
|
|
||||||
if data:
|
|
||||||
self.addFile(fname, data)
|
|
||||||
n += 1
|
|
||||||
if total > 0:
|
|
||||||
self.progressCallback('down', total, total, missing[total-1])
|
|
||||||
|
|
||||||
def getFile(self, fname):
|
|
||||||
try:
|
try:
|
||||||
return open(self.mediaPath(fname), "rb").read()
|
shutil.copy2(srcfile, dstfile)
|
||||||
except (IOError, OSError):
|
except IOError, OSError:
|
||||||
return None
|
pass
|
||||||
|
|
||||||
def addFile(self, fname, data):
|
|
||||||
path = self.mediaPath(fname)
|
|
||||||
assert not os.path.exists(path)
|
|
||||||
size = self.deck.s.scalar(
|
|
||||||
"select size from media where filename = :f",
|
|
||||||
f=fname)
|
|
||||||
# don't bother checksumming locally
|
|
||||||
#assert size
|
|
||||||
#assert size == len(data)
|
|
||||||
#assert checksum(data) == os.path.splitext(fname)[0]
|
|
||||||
open(path, "wb").write(data)
|
|
||||||
|
|
||||||
class BulkMediaSyncerProxy(HttpSyncServerProxy):
|
|
||||||
|
|
||||||
def missingMedia(self):
|
|
||||||
return self.unstuff(self.runCmd("missingMedia"))
|
|
||||||
|
|
||||||
def _splitMedia(self, fname):
|
|
||||||
return "%s/%s/%s" % (fname[0:1], fname[0:2], fname)
|
|
||||||
|
|
||||||
def _relativeMediaPath(self, fname):
|
|
||||||
return "http://ankimedia.ichi2.net/%s" % (
|
|
||||||
self._splitMedia(fname))
|
|
||||||
|
|
||||||
def getFile(self, fname):
|
|
||||||
try:
|
|
||||||
f = urllib2.urlopen(self._relativeMediaPath(fname))
|
|
||||||
except (urllib2.URLError, socket.error, socket.timeout):
|
|
||||||
return ""
|
|
||||||
ret = f.read()
|
|
||||||
if not ret:
|
|
||||||
return ""
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def addFile(self, fname, data):
|
|
||||||
oldsum = os.path.splitext(fname)[0]
|
|
||||||
assert oldsum == checksum(data)
|
|
||||||
return self.runCmd("addFile", fname=fname, data=data)
|
|
||||||
|
|
||||||
def runCmd(self, action, **args):
|
|
||||||
data = {"p": self.password,
|
|
||||||
"u": self.username,
|
|
||||||
"d": self.deckName.encode("utf-8")}
|
|
||||||
data.update(args)
|
|
||||||
data = urllib.urlencode(data)
|
|
||||||
try:
|
|
||||||
f = urllib2.urlopen(SYNC_URL + action, data)
|
|
||||||
except (urllib2.URLError, socket.error, socket.timeout,
|
|
||||||
httplib.BadStatusLine):
|
|
||||||
raise SyncError(type="noResponse")
|
|
||||||
ret = f.read()
|
|
||||||
if not ret:
|
|
||||||
raise SyncError(type="noResponse")
|
|
||||||
return ret
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from anki import DeckStorage
|
||||||
from anki.db import *
|
from anki.db import *
|
||||||
from anki.stdmodels import BasicModel
|
from anki.stdmodels import BasicModel
|
||||||
from anki.sync import SyncClient, SyncServer, HttpSyncServer, HttpSyncServerProxy
|
from anki.sync import SyncClient, SyncServer, HttpSyncServer, HttpSyncServerProxy
|
||||||
|
from anki.sync import copyLocalMedia
|
||||||
from anki.stats import dailyStats, globalStats
|
from anki.stats import dailyStats, globalStats
|
||||||
from anki.facts import Fact
|
from anki.facts import Fact
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
|
|
@ -237,13 +238,20 @@ def test_localsync_media():
|
||||||
assert len(os.listdir(deck1media)) == 2
|
assert len(os.listdir(deck1media)) == 2
|
||||||
assert len(os.listdir(deck2media)) == 1
|
assert len(os.listdir(deck2media)) == 1
|
||||||
client.sync()
|
client.sync()
|
||||||
assert len(os.listdir(deck1media)) == 3
|
# metadata should have been copied
|
||||||
assert len(os.listdir(deck2media)) == 3
|
|
||||||
assert deck1.s.scalar("select count(1) from media") == 3
|
assert deck1.s.scalar("select count(1) from media") == 3
|
||||||
assert deck2.s.scalar("select count(1) from media") == 3
|
assert deck2.s.scalar("select count(1) from media") == 3
|
||||||
|
# copy local files
|
||||||
|
copyLocalMedia(deck1, deck2)
|
||||||
|
assert len(os.listdir(deck1media)) == 2
|
||||||
|
assert len(os.listdir(deck2media)) == 3
|
||||||
|
copyLocalMedia(deck2, deck1)
|
||||||
|
assert len(os.listdir(deck1media)) == 3
|
||||||
|
assert len(os.listdir(deck2media)) == 3
|
||||||
# check delete
|
# check delete
|
||||||
os.unlink(os.path.join(deck1media, "22161b29b0c18e068038021f54eee1ee.png"))
|
os.unlink(os.path.join(deck1media, "22161b29b0c18e068038021f54eee1ee.png"))
|
||||||
time.sleep(0.1)
|
os.system("sync")
|
||||||
|
time.sleep(0.2)
|
||||||
rebuildMediaDir(deck1)
|
rebuildMediaDir(deck1)
|
||||||
client.sync()
|
client.sync()
|
||||||
assert deck1.s.scalar("select count(1) from media") == 2
|
assert deck1.s.scalar("select count(1) from media") == 2
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue