You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
6.4 KiB
Python
154 lines
6.4 KiB
Python
import os
|
|
import shutil
|
|
import time
|
|
import datetime
|
|
import sqlite3
|
|
import threading
|
|
|
|
DB_NAME = 'my_data.db'
|
|
|
|
class FolderSynchronizer:
|
|
def __init__(self, name, folder1, folder2, interval):
|
|
self.name = name
|
|
self.folder1 = folder1
|
|
self.folder2 = folder2
|
|
self.interval = interval
|
|
self.local = threading.local()
|
|
self.create_table()
|
|
|
|
def get_connection(self):
|
|
if not hasattr(self.local, 'conn'):
|
|
self.local.conn = sqlite3.connect(DB_NAME)
|
|
return self.local.conn
|
|
|
|
def get_cursor(self):
|
|
return self.get_connection().cursor()
|
|
|
|
def create_table(self):
|
|
conn = self.get_connection()
|
|
cursor = conn.cursor()
|
|
cursor.execute(f'''
|
|
CREATE TABLE IF NOT EXISTS {self.name} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
rel_dir TEXT,
|
|
file_name TEXT,
|
|
modified1 REAL,
|
|
exist1 INTEGER,
|
|
modified2 REAL,
|
|
exist2 INTEGER,
|
|
UNIQUE(rel_dir, file_name)
|
|
)
|
|
''')
|
|
conn.commit()
|
|
|
|
# def delete_job(self):
|
|
# conn = self.get_connection()
|
|
# cursor = conn.cursor()
|
|
# cursor.execute(f"DROP TABLE IF EXISTS {self.name}")
|
|
# conn.commit()
|
|
# print(f"Job '{self.name}' has been deleted.")
|
|
|
|
def update_db(self, rel_dir, file_name, modified1, exist1, modified2, exist2):
|
|
conn = self.get_connection()
|
|
cursor = conn.cursor()
|
|
cursor.execute(f'''
|
|
INSERT OR REPLACE INTO {self.name}
|
|
(rel_dir, file_name, modified1, exist1, modified2, exist2)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
''', (rel_dir, file_name, modified1, exist1, modified2, exist2))
|
|
conn.commit()
|
|
|
|
def get_file_info(self, rel_dir, file_name):
|
|
conn = self.get_connection()
|
|
cursor = conn.cursor()
|
|
cursor.execute(f'''
|
|
SELECT * FROM {self.name}
|
|
WHERE rel_dir = ? AND file_name = ?
|
|
''', (rel_dir, file_name))
|
|
return cursor.fetchone()
|
|
|
|
def sync_folders(self):
|
|
def sync_files(src_folder, dest_folder):
|
|
current_time = time.time()
|
|
total_sync = 0
|
|
count_sync = 0
|
|
for root, _, files in os.walk(src_folder):
|
|
for filename in files:
|
|
try:
|
|
src_path = os.path.join(root, filename)
|
|
rel_dir = os.path.relpath(root, src_folder)
|
|
dest_path = os.path.join(dest_folder, rel_dir, filename)
|
|
total_sync += 1
|
|
|
|
src_modified = os.path.getmtime(src_path)
|
|
file_info = self.get_file_info(rel_dir, filename)
|
|
|
|
if not file_info:
|
|
# New file
|
|
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
shutil.copy2(src_path, dest_path)
|
|
count_sync += 1
|
|
dest_modified = current_time
|
|
modified1, exist1, modified2, exist2 = src_modified, 1, dest_modified, 1
|
|
else:
|
|
# Existing file
|
|
if src_modified > file_info[3] or not file_info[4]:
|
|
shutil.copy2(src_path, dest_path)
|
|
count_sync += 1
|
|
dest_modified = current_time
|
|
modified1, exist1, modified2, exist2 = src_modified, 1, dest_modified, 1
|
|
else:
|
|
modified1, exist1, modified2, exist2 = src_modified, 1, file_info[5], file_info[6]
|
|
except:
|
|
pass
|
|
finally:
|
|
self.update_db(rel_dir, filename, modified1, exist1, modified2, exist2)
|
|
|
|
return total_sync, count_sync
|
|
|
|
def check_deleted_files():
|
|
conn = self.get_connection()
|
|
cursor = conn.cursor()
|
|
current_time = time.time()
|
|
count_sync = 0
|
|
cursor.execute(f"SELECT rel_dir, file_name, modified1, exist1, modified2, exist2 FROM {self.name}")
|
|
for row in cursor.fetchall():
|
|
rel_dir, file_name, modified1, exist1, modified2, exist2 = row
|
|
path1 = os.path.join(self.folder1, rel_dir, file_name)
|
|
path2 = os.path.join(self.folder2, rel_dir, file_name)
|
|
|
|
if not os.path.exists(path1) and not os.path.exists(path2):
|
|
count_sync += 1
|
|
cursor.execute(f"DELETE FROM {self.name} WHERE rel_dir = ? AND file_name = ?", (rel_dir, file_name))
|
|
elif not os.path.exists(path1) and exist1:
|
|
modified1 = current_time
|
|
if modified1 > modified2:
|
|
count_sync += 1
|
|
os.remove(path2)
|
|
cursor.execute(f"DELETE FROM {self.name} WHERE rel_dir = ? AND file_name = ?", (rel_dir, file_name))
|
|
else:
|
|
self.update_db(rel_dir, file_name, modified1, 0, modified2, exist2)
|
|
elif not os.path.exists(path2) and exist2:
|
|
modified2 = current_time
|
|
if modified2 > modified1:
|
|
count_sync += 1
|
|
os.remove(path1)
|
|
cursor.execute(f"DELETE FROM {self.name} WHERE rel_dir = ? AND file_name = ?", (rel_dir, file_name))
|
|
else:
|
|
self.update_db(rel_dir, file_name, modified1, exist1, modified2, 0)
|
|
|
|
conn.commit()
|
|
return count_sync
|
|
|
|
total_sync1, count_sync1 = sync_files(self.folder1, self.folder2)
|
|
total_sync2, count_sync2 = sync_files(self.folder2, self.folder1)
|
|
count_sync3 = check_deleted_files()
|
|
|
|
total_sync = max(total_sync1, total_sync2)
|
|
count_sync = count_sync1 + count_sync2 + count_sync3
|
|
|
|
# now = datetime.datetime.now()
|
|
# formatted_datetime = now.strftime("[%Y-%m-%d %H:%M:%S]")
|
|
|
|
# return f"[{self.name}] \'{self.folder1} <-> {self.folder2}\' Sync completed! (sync count: {count_sync}, total files: {total_sync})"
|
|
return f"[{self.name}] Sync completed! (sync count: {count_sync}, total files: {total_sync})" |