désactivation async

This commit is contained in:
2025-05-26 00:31:51 +02:00
parent f442e334d0
commit 01d598684c
3 changed files with 70 additions and 70 deletions

View File

@@ -1,5 +1,3 @@
import threading
from PySide6.QtCore import QObject, Signal, QTimer, QUrl, Slot, QThread from PySide6.QtCore import QObject, Signal, QTimer, QUrl, Slot, QThread
from PySide6.QtNetwork import QNetworkCookie, QNetworkAccessManager, QNetworkRequest, QNetworkReply, QNetworkCookieJar from PySide6.QtNetwork import QNetworkCookie, QNetworkAccessManager, QNetworkRequest, QNetworkReply, QNetworkCookieJar
@@ -36,7 +34,6 @@ class DownloadManager(QObject):
self.files: dict[str, FileType] = self.conf.get_value("files", {}) self.files: dict[str, FileType] = self.conf.get_value("files", {})
self.tasks: dict[FileType, DownloaderWorker] = {} self.tasks: dict[FileType, DownloaderWorker] = {}
self.task_stats: dict[str, FileStatsType] = {} self.task_stats: dict[str, FileStatsType] = {}
self.waiter = threading.Event()
self.cookies = {} self.cookies = {}
# slots # slots
@@ -56,21 +53,25 @@ class DownloadManager(QObject):
self.status = {} self.status = {}
self.update_status() self.update_status()
def loop_queue(self): self.queue_timer = QTimer(self)
self.logger.info("Démarrage de la boucle de téléchargement") self.queue_timer.timeout.connect(self.process_queue)
while not self.stop: self.queue_timer.start(1)
def process_queue(self):
if self.pause:
return
available_worker = next((worker for worker in self.worker_pool if worker.available), None) available_worker = next((worker for worker in self.worker_pool if worker.available), None)
file = next((file for file in self.files.values() if not file.downloaded and file not in self.tasks), None) file = next((file for file in self.files.values() if not file.downloaded and file not in self.tasks), None)
# wait if pause is true if available_worker and file:
if not available_worker or not file or self.pause:
self.logger.info("loop queue paused, waiting for tasks to finish...")
self.waiter.clear()
self.waiter.wait()
self.logger.info("loop queue resumed")
else:
self.logger.info(f"Assignation du fichier {file.id} à un worker disponible") self.logger.info(f"Assignation du fichier {file.id} à un worker disponible")
self.tasks[file] = available_worker self.tasks[file] = available_worker
available_worker.start_download(file) available_worker.start_download(file)
else:
self.queue_timer.stop()
if all(file.downloaded for file in self.files.values()):
self.logger.info("Tous les fichiers ont été téléchargés")
self.set_pause(True)
self.logger.info("Arrêt de la boucle de téléchargement") self.logger.info("Arrêt de la boucle de téléchargement")
@@ -82,11 +83,11 @@ class DownloadManager(QObject):
self.logger.info(f"État de pause modifié à: {value}") self.logger.info(f"État de pause modifié à: {value}")
if self.pause: if self.pause:
self.logger.info("Arrêt de tous les téléchargements actifs") self.logger.info("Arrêt de tous les téléchargements actifs")
for worker in self.tasks.values(): for worker in self.tasks.copy().values():
worker.stop_download() worker.stop_download()
else: else:
self.logger.info("Reprise des téléchargements") self.logger.info("Reprise des téléchargements")
self.waiter.set() self.queue_timer.start(1)
QTimer.singleShot(100, self.update_status) QTimer.singleShot(100, self.update_status)
def task_ended(self, file): def task_ended(self, file):
@@ -99,7 +100,7 @@ class DownloadManager(QObject):
self.task_stats.pop(file.id) self.task_stats.pop(file.id)
self.logger.debug("Notification du waiter pour traiter le prochain fichier") self.logger.debug("Notification du waiter pour traiter le prochain fichier")
self.waiter.set() self.queue_timer.start(1)
self.files_updated.emit(self.files) self.files_updated.emit(self.files)
@@ -151,6 +152,7 @@ class DownloadManager(QObject):
"downloader_stats": {key: dl_stat.to_dict() for key, dl_stat in self.task_stats.items()} "downloader_stats": {key: dl_stat.to_dict() for key, dl_stat in self.task_stats.items()}
} }
if self.status != new_status: if self.status != new_status:
print(new_status["downloader_stats"])
self.status = new_status self.status = new_status
self.status_updated.emit(self.status) self.status_updated.emit(self.status)
self.logger.debug(f"Mise à jour du statut: {len(self.files)} fichiers, {new_status['downloaded_files']} téléchargés, Vitesse: {new_status['speed']/1024:.2f} Ko/s") self.logger.debug(f"Mise à jour du statut: {len(self.files)} fichiers, {new_status['downloaded_files']} téléchargés, Vitesse: {new_status['speed']/1024:.2f} Ko/s")
@@ -184,12 +186,13 @@ class DownloaderWorker(QObject):
self.logger = logging.getLogger('DownloaderWorker') self.logger = logging.getLogger('DownloaderWorker')
self.logger.info("Initialisation d'un nouveau worker de téléchargement") self.logger.info("Initialisation d'un nouveau worker de téléchargement")
self.dl_thread = QThread() self.dl_thread = QThread(self)
self.dl_thread.start()
self.manager = QNetworkAccessManager(self) self.manager = QNetworkAccessManager(self)
self.manager.moveToThread(self.dl_thread)
if not self.manager.cookieJar(): if not self.manager.cookieJar():
self.manager.setCookieJar(QNetworkCookieJar()) self.manager.setCookieJar(QNetworkCookieJar())
# self.manager.moveToThread(self.dl_thread)
self.manager.finished.connect(self._on_finished) self.manager.finished.connect(self._on_finished)
self.available = True self.available = True
@@ -198,8 +201,10 @@ class DownloaderWorker(QObject):
self.file: Optional[FileType] = None self.file: Optional[FileType] = None
self.stats: Optional[FileStatsType] = None self.stats: Optional[FileStatsType] = None
self.last_update_time = time.monotonic() self.last_update_time = 0
self.last_downloaded_size = 0 self.accumulated_bytes = 0
self.dl_thread.start()
self.logger.debug("Worker de téléchargement initialisé et prêt") self.logger.debug("Worker de téléchargement initialisé et prêt")
@Slot(FileType) @Slot(FileType)
@@ -214,6 +219,7 @@ class DownloaderWorker(QObject):
# construction des stats + vérification si le téléchargement est déjà terminé # construction des stats + vérification si le téléchargement est déjà terminé
self.file = file self.file = file
self.stats = FileStatsType() self.stats = FileStatsType()
self.stats.total_size = file.total_size
self.stats.downloaded_size = file.size_downloaded self.stats.downloaded_size = file.size_downloaded
if self.stats.downloaded_size >= file.total_size: if self.stats.downloaded_size >= file.total_size:
self.logger.info(f"Le fichier {file.id} est déjà téléchargé") self.logger.info(f"Le fichier {file.id} est déjà téléchargé")
@@ -243,29 +249,35 @@ class DownloaderWorker(QObject):
self.logger.debug(f"Fichier ouvert en mode {mode}: {file.target}") self.logger.debug(f"Fichier ouvert en mode {mode}: {file.target}")
self.last_update_time = time.monotonic() self.last_update_time = time.monotonic()
self.last_downloaded_size = self.stats.downloaded_size self.accumulated_bytes = 0
def _on_data_ready(self): def _on_data_ready(self):
chunk = self.reply.readAll() chunk_size = 0
while self.reply.bytesAvailable():
chunk = self.reply.read(64 * 1024)
self.file_io.write(chunk.data()) self.file_io.write(chunk.data())
chunk_size += chunk.size()
# stats calculation
chunk_size = chunk.size()
self.stats.downloaded_size += chunk_size self.stats.downloaded_size += chunk_size
current_time = time.monotonic() self.accumulated_bytes += chunk_size
elapsed_time = max(current_time - self.last_update_time, 0.001) now = time.monotonic()
current_speed = chunk_size / elapsed_time elapsed = now - self.last_update_time
if self.file.speed > 0:
self.file.speed = round(0.7 * current_speed + 0.3 * self.file.speed)
else:
self.file.speed = round(current_speed)
self.last_update_time = current_time if elapsed >= 1.0:
self.last_downloaded_size = chunk_size # EMA lissage court terme (optionnel : 0.7 nouveau / 0.3 ancien)
self.logger.debug(f"Données reçues: {chunk_size} octets, vitesse: {self.file.speed/1024:.2f} Ko/s") new_speed = self.accumulated_bytes / elapsed
if self.stats.speed > 0:
self.stats.speed = round(0.7 * new_speed + 0.3 * self.stats.speed)
else:
self.stats.speed = round(new_speed)
self.accumulated_bytes = 0
self.last_update_time = now
# self.logger.info(f"Données reçues: {chunk_size} octets, vitesse: {self.stats.speed/1024:.2f} Ko/s")
def _on_progress(self, bytes_received, bytes_total): def _on_progress(self, bytes_received, bytes_total):
self.logger.debug(f"Progression du téléchargement pour {self.file.id}: {bytes_received}/{bytes_total} octets ({(bytes_received/max(bytes_total, 1))*100:.1f}%)") pass
# self.logger.debug(f"Progression du téléchargement pour {self.file.id}: {bytes_received}/{bytes_total} octets ({(bytes_received/max(bytes_total, 1))*100:.1f}%)")
def _on_finished(self, reply: QNetworkReply): def _on_finished(self, reply: QNetworkReply):
if not self.file: if not self.file:
@@ -280,8 +292,9 @@ class DownloaderWorker(QObject):
self.logger.error(f"Échec du téléchargement pour {self.file.id}: {error_msg}") self.logger.error(f"Échec du téléchargement pour {self.file.id}: {error_msg}")
self.file.error = error_msg self.file.error = error_msg
self.clear()
self.download_manager.task_ended(self.file) self.download_manager.task_ended(self.file)
self.clear()
def stop_download(self): def stop_download(self):
if self.file: if self.file:
@@ -301,17 +314,18 @@ class DownloaderWorker(QObject):
if not self.reply.isFinished(): if not self.reply.isFinished():
self.logger.debug("Annulation de la requête réseau") self.logger.debug("Annulation de la requête réseau")
self.reply.abort() self.reply.abort()
if self.reply:
self.reply.deleteLater() self.reply.deleteLater()
self.reply = None self.reply = None
self.logger.debug("Requête réseau nettoyée") self.logger.debug("Requête réseau nettoyée")
if self.stats: # if self.stats:
self.stats = None # self.stats = None
self.logger.debug("Statistiques réinitialisées") # self.logger.debug("Statistiques réinitialisées")
#
if self.file: # if self.file:
self.file = None # self.file = None
self.logger.debug("Référence au fichier supprimée") # self.logger.debug("Référence au fichier supprimée")
self.available = True self.available = True
self.logger.debug("Worker marqué comme disponible") self.logger.debug("Worker marqué comme disponible")
@@ -330,9 +344,8 @@ class DownloaderWorker(QObject):
self.reply.abort() self.reply.abort()
self.reply.deleteLater() self.reply.deleteLater()
self.manager.deleteLater() self.manager.deleteLater()
self.logger.debug("Arrêt du thread de téléchargement")
self.dl_thread.quit() self.dl_thread.quit()
if not self.dl_thread.wait(3000): self.dl_thread.wait()
self.logger.warning("Le thread ne s'est pas arrêté proprement, terminaison forcée") self.logger.debug("Arrêt du thread de téléchargement")
self.dl_thread.terminate()
self.logger.info("Worker fermé") self.logger.info("Worker fermé")

View File

@@ -20,7 +20,7 @@ def configure_logging(debug_mode=False):
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler = logging.StreamHandler(sys.stdout) handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG) handler.setLevel(logging.INFO)
handler.setFormatter(formatter) handler.setFormatter(formatter)
root.addHandler(handler) root.addHandler(handler)

View File

@@ -46,18 +46,8 @@ class MainWindow(QMainWindow):
self.site_window.on_cookie_added.connect(self.download_manager.add_cookie) self.site_window.on_cookie_added.connect(self.download_manager.add_cookie)
self.logger.debug("Signaux connectés") self.logger.debug("Signaux connectés")
# initialisation du gestionnaire de téléchargement
self.dm_loop = None
QTimer.singleShot(0, self.setup_tasks)
self.logger.info("Fenêtre principale initialisée avec succès") self.logger.info("Fenêtre principale initialisée avec succès")
def setup_tasks(self):
# Lancer les tâches une fois que l'application est prête
self.logger.debug("Configuration des tâches")
self.dm_loop = threading.Thread(target=self.download_manager.loop_queue, daemon=True)
self.dm_loop.start()
self.logger.debug("File d'attente de téléchargement démarrée")
def handle_files(self, file_paths): def handle_files(self, file_paths):
""" """
@@ -95,10 +85,7 @@ class MainWindow(QMainWindow):
def closeEvent(self, event, /): def closeEvent(self, event, /):
self.download_manager.pause = True self.download_manager.pause = True
self.download_manager.stop = True self.download_manager.stop = True
self.download_manager.waiter.set()
self.download_manager.close_thread_workers() self.download_manager.close_thread_workers()
self.dm_loop.join(timeout=1)
event.accept() event.accept()