195 |
199 |
196 self.rsyncProgressMessage.emit( |
200 self.rsyncProgressMessage.emit( |
197 self.tr("Synchronizing <b>{0}</b>.").format(deviceDirectory) |
201 self.tr("Synchronizing <b>{0}</b>.").format(deviceDirectory) |
198 ) |
202 ) |
199 |
203 |
|
204 doneMessage = self.tr("Done synchronizing <b>{0}</b>.").format( |
|
205 deviceDirectory) |
|
206 |
200 sourceDict = {} |
207 sourceDict = {} |
201 sourceFiles = listdirStat(hostDirectory) |
208 sourceFiles = listdirStat(hostDirectory) |
202 for name, nstat in sourceFiles: |
209 for name, nstat in sourceFiles: |
203 sourceDict[name] = nstat |
210 sourceDict[name] = nstat |
204 |
211 |
205 destinationDict = {} |
212 destinationDict = {} |
206 try: |
213 if localDevice: |
207 destinationFiles = self.__commandsInterface.lls(deviceDirectory, |
214 if not os.path.isdir(deviceDirectory): |
208 fullstat=True) |
215 # simply copy destination to source |
209 except Exception as exc: |
216 shutil.copytree(hostDirectory, deviceDirectory) |
210 return [str(exc)] |
217 self.rsyncProgressMessage.emit(doneMessage) |
211 if destinationFiles is None: |
218 return errors |
212 # the destination directory does not exist |
219 else: |
|
220 destinationFiles = listdirStat(deviceDirectory) |
|
221 for name, nstat in destinationFiles: |
|
222 destinationDict[name] = nstat |
|
223 else: |
213 try: |
224 try: |
214 self.__commandsInterface.mkdir(deviceDirectory) |
225 destinationFiles = self.__commandsInterface.lls(deviceDirectory, |
|
226 fullstat=True) |
215 except Exception as exc: |
227 except Exception as exc: |
216 return [str(exc)] |
228 return [str(exc)] |
217 else: |
229 if destinationFiles is None: |
218 for name, nstat in destinationFiles: |
230 # the destination directory does not exist |
219 destinationDict[name] = nstat |
231 try: |
|
232 self.__commandsInterface.mkdir(deviceDirectory) |
|
233 except Exception as exc: |
|
234 return [str(exc)] |
|
235 else: |
|
236 for name, nstat in destinationFiles: |
|
237 destinationDict[name] = nstat |
220 |
238 |
221 destinationSet = set(destinationDict.keys()) |
239 destinationSet = set(destinationDict.keys()) |
222 sourceSet = set(sourceDict.keys()) |
240 sourceSet = set(sourceDict.keys()) |
223 toAdd = sourceSet - destinationSet # add to dev |
241 toAdd = sourceSet - destinationSet # add to dev |
224 toDelete = destinationSet - sourceSet # delete from dev |
242 toDelete = destinationSet - sourceSet # delete from dev |
225 toUpdate = destinationSet.intersection(sourceSet) # update files |
243 toUpdate = destinationSet.intersection(sourceSet) # update files |
226 |
244 |
227 for sourceBasename in toAdd: |
245 if localDevice: |
228 # name exists in source but not in device |
246 for sourceBasename in toAdd: |
229 sourceFilename = os.path.join(hostDirectory, sourceBasename) |
247 # name exists in source but not in device |
230 destFilename = deviceDirectory + "/" + sourceBasename |
248 sourceFilename = os.path.join(hostDirectory, sourceBasename) |
231 self.rsyncProgressMessage.emit( |
249 destFilename = os.path.join(deviceDirectory, sourceBasename) |
232 self.tr("Adding <b>{0}</b>...").format(destFilename)) |
250 self.rsyncProgressMessage.emit( |
233 if os.path.isfile(sourceFilename): |
251 self.tr("Adding <b>{0}</b>...").format(destFilename)) |
234 try: |
252 if os.path.isfile(sourceFilename): |
235 self.__commandsInterface.put(sourceFilename, destFilename) |
253 shutil.copy2(sourceFilename, destFilename) |
236 except Exception as exc: |
254 elif os.path.isdir(sourceFilename): |
|
255 # recurse |
|
256 errs = self.__rsync(sourceFilename, destFilename, |
|
257 mirror=mirror, localDevice=localDevice) |
237 # just note issues but ignore them otherwise |
258 # just note issues but ignore them otherwise |
238 errors.append(str(exc)) |
259 errors.extend(errs) |
239 if os.path.isdir(sourceFilename): |
260 |
240 # recurse |
261 if mirror: |
241 errs = self.__rsync(sourceFilename, destFilename, |
262 for destBasename in toDelete: |
242 mirror=mirror) |
263 # name exists in device but not local, delete |
243 # just note issues but ignore them otherwise |
264 destFilename = os.path.join(deviceDirectory, destBasename) |
244 errors.extend(errs) |
265 if os.path.isdir(destFilename): |
245 |
266 shutil.rmtree(destFilename, ignore_errors=True) |
246 if mirror: |
267 elif os.path.isfile(destFilename): |
247 for destBasename in toDelete: |
268 os.remove(destFilename) |
248 # name exists in device but not local, delete |
269 |
249 destFilename = deviceDirectory + "/" + destBasename |
270 for sourceBasename in toUpdate: |
|
271 # names exist in both; do an update |
|
272 sourceStat = sourceDict[sourceBasename] |
|
273 destStat = destinationDict[sourceBasename] |
|
274 sourceFilename = os.path.join(hostDirectory, sourceBasename) |
|
275 destFilename = os.path.join(deviceDirectory, sourceBasename) |
|
276 destMode = destStat[0] |
|
277 if os.path.isdir(sourceFilename): |
|
278 if os.path.isdir(destFilename): |
|
279 # both are directories => recurs |
|
280 errs = self.__rsync(sourceFilename, destFilename, |
|
281 mirror=mirror, |
|
282 localDevice=localDevice) |
|
283 # just note issues but ignore them otherwise |
|
284 errors.extend(errs) |
|
285 else: |
|
286 self.rsyncProgressMessage.emit( |
|
287 self.tr("Source <b>{0}</b> is a directory and" |
|
288 " destination <b>{1}</b> is a file." |
|
289 " Ignoring it.") |
|
290 .format(sourceFilename, destFilename) |
|
291 ) |
|
292 else: |
|
293 if os.path.isdir(destFilename): |
|
294 self.rsyncProgressMessage.emit( |
|
295 self.tr("Source <b>{0}</b> is a file and" |
|
296 " destination <b>{1}</b> is a directory." |
|
297 " Ignoring it.") |
|
298 .format(sourceFilename, destFilename) |
|
299 ) |
|
300 else: |
|
301 if sourceStat[8] > destStat[8]: # mtime |
|
302 self.rsyncProgressMessage.emit( |
|
303 self.tr("Updating <b>{0}</b>...") |
|
304 .format(destFilename) |
|
305 ) |
|
306 shutil.copy2(sourceFilename, destFilename) |
|
307 else: |
|
308 for sourceBasename in toAdd: |
|
309 # name exists in source but not in device |
|
310 sourceFilename = os.path.join(hostDirectory, sourceBasename) |
|
311 destFilename = deviceDirectory + "/" + sourceBasename |
250 self.rsyncProgressMessage.emit( |
312 self.rsyncProgressMessage.emit( |
251 self.tr("Removing <b>{0}</b>...").format(destFilename)) |
313 self.tr("Adding <b>{0}</b>...").format(destFilename)) |
252 try: |
314 if os.path.isfile(sourceFilename): |
253 self.__commandsInterface.rmrf(destFilename, recursive=True, |
315 try: |
254 force=True) |
316 self.__commandsInterface.put(sourceFilename, |
255 except Exception as exc: |
317 destFilename) |
256 # just note issues but ignore them otherwise |
318 except Exception as exc: |
257 errors.append(str(exc)) |
319 # just note issues but ignore them otherwise |
258 |
320 errors.append(str(exc)) |
259 for sourceBasename in toUpdate: |
321 elif os.path.isdir(sourceFilename): |
260 # names exist in both; do an update |
322 # recurse |
261 sourceStat = sourceDict[sourceBasename] |
|
262 destStat = destinationDict[sourceBasename] |
|
263 sourceFilename = os.path.join(hostDirectory, sourceBasename) |
|
264 destFilename = deviceDirectory + "/" + sourceBasename |
|
265 destMode = destStat[0] |
|
266 if os.path.isdir(sourceFilename): |
|
267 if stat.S_ISDIR(destMode): |
|
268 # both are directories => recurs |
|
269 errs = self.__rsync(sourceFilename, destFilename, |
323 errs = self.__rsync(sourceFilename, destFilename, |
270 mirror=mirror) |
324 mirror=mirror) |
271 # just note issues but ignore them otherwise |
325 # just note issues but ignore them otherwise |
272 errors.extend(errs) |
326 errors.extend(errs) |
|
327 |
|
328 if mirror: |
|
329 for destBasename in toDelete: |
|
330 # name exists in device but not local, delete |
|
331 destFilename = deviceDirectory + "/" + destBasename |
|
332 self.rsyncProgressMessage.emit( |
|
333 self.tr("Removing <b>{0}</b>...").format(destFilename)) |
|
334 try: |
|
335 self.__commandsInterface.rmrf(destFilename, |
|
336 recursive=True, |
|
337 force=True) |
|
338 except Exception as exc: |
|
339 # just note issues but ignore them otherwise |
|
340 errors.append(str(exc)) |
|
341 |
|
342 for sourceBasename in toUpdate: |
|
343 # names exist in both; do an update |
|
344 sourceStat = sourceDict[sourceBasename] |
|
345 destStat = destinationDict[sourceBasename] |
|
346 sourceFilename = os.path.join(hostDirectory, sourceBasename) |
|
347 destFilename = deviceDirectory + "/" + sourceBasename |
|
348 destMode = destStat[0] |
|
349 if os.path.isdir(sourceFilename): |
|
350 if stat.S_ISDIR(destMode): |
|
351 # both are directories => recurs |
|
352 errs = self.__rsync(sourceFilename, destFilename, |
|
353 mirror=mirror) |
|
354 # just note issues but ignore them otherwise |
|
355 errors.extend(errs) |
|
356 else: |
|
357 self.rsyncProgressMessage.emit( |
|
358 self.tr("Source <b>{0}</b> is a directory and" |
|
359 " destination <b>{1}</b> is a file." |
|
360 " Ignoring it.") |
|
361 .format(sourceFilename, destFilename) |
|
362 ) |
273 else: |
363 else: |
274 self.rsyncProgressMessage.emit( |
364 if stat.S_ISDIR(destMode): |
275 self.tr("Source <b>{0}</b> is a directory and" |
|
276 " destination <b>{1}</b> is a file. Ignoring" |
|
277 " it.") |
|
278 .format(sourceFilename, destFilename) |
|
279 ) |
|
280 else: |
|
281 if stat.S_ISDIR(destMode): |
|
282 self.rsyncProgressMessage.emit( |
|
283 self.tr("Source <b>{0}</b> is a file and destination" |
|
284 " <b>{1}</b> is a directory. Ignoring it.") |
|
285 .format(sourceFilename, destFilename) |
|
286 ) |
|
287 else: |
|
288 if sourceStat[8] > destStat[8]: # mtime |
|
289 self.rsyncProgressMessage.emit( |
365 self.rsyncProgressMessage.emit( |
290 self.tr("Updating <b>{0}</b>...") |
366 self.tr("Source <b>{0}</b> is a file and" |
291 .format(destFilename) |
367 " destination <b>{1}</b> is a directory." |
|
368 " Ignoring it.") |
|
369 .format(sourceFilename, destFilename) |
292 ) |
370 ) |
293 try: |
371 else: |
294 self.__commandsInterface.put(sourceFilename, |
372 if sourceStat[8] > destStat[8]: # mtime |
295 destFilename) |
373 self.rsyncProgressMessage.emit( |
296 except Exception as exc: |
374 self.tr("Updating <b>{0}</b>...") |
297 errors.append(str(exc)) |
375 .format(destFilename) |
298 |
376 ) |
299 self.rsyncProgressMessage.emit( |
377 try: |
300 self.tr("Done synchronizing <b>{0}</b>.").format(deviceDirectory) |
378 self.__commandsInterface.put(sourceFilename, |
301 ) |
379 destFilename) |
|
380 except Exception as exc: |
|
381 errors.append(str(exc)) |
|
382 |
|
383 self.rsyncProgressMessage.emit(doneMessage) |
302 |
384 |
303 return errors |
385 return errors |
304 |
386 |
305 @pyqtSlot(str, str) |
387 @pyqtSlot(str, str) |
306 @pyqtSlot(str, str, bool) |
388 @pyqtSlot(str, str, bool) |
307 def rsync(self, hostDirectory, deviceDirectory, mirror=True): |
389 @pyqtSlot(str, str, bool, bool) |
|
390 def rsync(self, hostDirectory, deviceDirectory, mirror=True, |
|
391 localDevice=False): |
308 """ |
392 """ |
309 Public slot to synchronize a local directory to the device. |
393 Public slot to synchronize a local directory to the device. |
310 |
394 |
311 @param hostDirectory name of the local directory |
395 @param hostDirectory name of the local directory |
312 @type str |
396 @type str |
313 @param deviceDirectory name of the directory on the device |
397 @param deviceDirectory name of the directory on the device |
314 @type str |
398 @type str |
315 @param mirror flag indicating to mirror the local directory to |
399 @param mirror flag indicating to mirror the local directory to |
316 the device directory |
400 the device directory |
317 @type bool |
401 @type bool |
318 """ |
402 @param localDevice flag indicating device access via local file system |
319 errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror) |
403 @type bool |
|
404 """ |
|
405 errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror, |
|
406 localDevice=localDevice) |
320 if errors: |
407 if errors: |
321 self.error.emit("rsync", "\n".join(errors)) |
408 self.error.emit("rsync", "\n".join(errors)) |
322 |
409 |
323 self.rsyncDone.emit(hostDirectory, deviceDirectory) |
410 self.rsyncDone.emit(hostDirectory, deviceDirectory) |
324 |
411 |