désactivation async
This commit is contained in:
109
src/download.py
109
src/download.py
@@ -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é")
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user