Files
LorenEteval_Furious/Furious/Backends/Xray/AssetDownloadManager.py
T
2026-08-19 15:03:55 +08:00

237 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (C) 2024–present Loren Eteval & contributors <loren.eteval@proton.me>
#
# This file is part of Furious.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Provide Qt support for Xray asset download manager."""
from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Qt import *
from PySide6 import QtCore
from typing import AnyStr, Union, Callable
import os
import logging
import hashlib
import functools
__all__ = ['XrayAssetDownloadManager']
logger = logging.getLogger(__name__)
class SHA256Worker(QtCore.QObject, QtCore.QRunnable):
"""Run SHA-256 work in the background."""
finished = QtCore.Signal(str)
def __init__(self, string=b''):
# Explictly called __init__
"""Initialize the SHA256Worker."""
QtCore.QObject.__init__(self)
QtCore.QRunnable.__init__(self)
self.string = string
def run(self):
"""Run the SHA-256 worker task."""
self.finished.emit(hashlib.sha256(self.string).hexdigest())
class XrayAssetSHA256DownloadManager(HttpGetManager):
"""Coordinate Xray asset SHA-256 download operations."""
def __init__(self, parent=None, **kwargs):
"""Initialize the XrayAssetSHA256DownloadManager."""
actionMessage = kwargs.pop('actionMessage', 'download sha256')
super().__init__(parent, actionMessage=actionMessage)
@staticmethod
def fileContent(filepath, mode='rb') -> AnyStr:
"""Return the file content value used by the Xray asset SHA-256 download manager."""
try:
with open(filepath, mode) as file:
return file.read()
except Exception:
# Any non-exit exceptions
return b''
def successCallback(self, networkReply, **kwargs):
"""Handle a successful network operation."""
filepath = kwargs.pop('filepath', '')
downloadCallback = kwargs.pop('downloadCallback', None)
data = networkReply.readAll().data()
if isinstance(data, bytes):
datastr = data.decode('utf-8', 'replace')
else:
datastr = str(data)
logger.debug(f'data: {data}')
# 6068a73edfa08b63080b8d362bd4c5b069689a3548f413f43cfa58227a201571 geosite.dat
# 3a12beaa33c81b6751b45833a0a942b9462420d91182e7fa1d768b418e604049 geoip.dat
value = datastr.split()[0]
basename = os.path.basename(filepath)
def handleFinished(_digest, _value=''):
"""Handle finished."""
logger.debug(
f'computed digest is \'{_digest}\' while repo digest is \'{_value}\''
)
if _digest != _value:
logger.info(f'digest not equal for {basename}. Start downloading asset')
if callable(downloadCallback):
downloadCallback()
else:
logger.info(f'digest equal for {basename}. Nothing to do')
worker = SHA256Worker(self.fileContent(filepath))
worker.setAutoDelete(True)
worker.finished.connect(functools.partial(handleFinished, _value=value))
AppThreadPool().start(worker)
def download(self, url, filepath, downloadCallback: Callable[[], None]):
"""Download the Xray asset SHA-256 download manager."""
self.webGET(
url,
filepath=str(filepath),
downloadCallback=downloadCallback,
)
class XrayAssetAssetsDownloadManager(HttpGetManager):
"""Coordinate Xray asset assets download operations."""
def __init__(self, parent=None, **kwargs):
"""Initialize the XrayAssetAssetsDownloadManager."""
actionMessage = kwargs.pop('actionMessage', 'download assets')
super().__init__(parent, actionMessage=actionMessage)
def successCallback(self, networkReply, **kwargs):
"""Handle a successful network operation."""
filepath = kwargs.pop('filepath', '')
saveFile = QtCore.QSaveFile(filepath)
data = networkReply.readAll().data()
if not saveFile.open(QtCore.QSaveFile.OpenModeFlag.WriteOnly):
logger.error(
f'failed to open \'{filepath}\' for writing. {saveFile.errorString()}'
)
else:
saveFile.write(data)
if not saveFile.commit():
logger.error(
f'save file to \'{filepath}\' failed. {saveFile.errorString()}'
)
else:
logger.info(f'save file to \'{filepath}\' success')
def download(self, url, filepath):
"""Download the Xray asset assets download manager."""
self.webGET(url, filepath=str(filepath))
class XrayAssetPairDownloadHelper:
"""Represent Xray asset pair download helper."""
def __init__(self, *args, **kwargs):
"""Initialize the XrayAssetPairDownloadHelper."""
sha256ActionMessage = kwargs.pop('sha256ActionMessage', 'download sha256')
assetsActionMessage = kwargs.pop('assetsActionMessage', 'download assets')
super().__init__(*args, **kwargs)
self.sha256Downloader = XrayAssetSHA256DownloadManager(
actionMessage=sha256ActionMessage
)
self.assetsDownloader = XrayAssetAssetsDownloadManager(
actionMessage=assetsActionMessage
)
def configureHttpProxy(self, httpProxy: Union[str, None]) -> bool:
"""Configure HTTP proxy."""
return all(
[
self.sha256Downloader.configureHttpProxy(httpProxy),
self.assetsDownloader.configureHttpProxy(httpProxy),
]
)
def download(self, sha256URL, assetsURL, filepath):
"""Download the Xray asset pair download helper."""
self.sha256Downloader.download(
url=sha256URL,
filepath=filepath,
downloadCallback=functools.partial(
self.assetsDownloader.download, assetsURL, filepath
),
)
class XrayAssetDownloadManager:
"""Coordinate Xray asset download operations."""
def __init__(self, *args, **kwargs):
"""Initialize the XrayAssetDownloadManager."""
super().__init__(*args, **kwargs)
self.downloadHelperGeosite = XrayAssetPairDownloadHelper(
sha256ActionMessage='download geosite sha256',
assetsActionMessage='download geosite assets',
)
self.downloadHelperGeoip = XrayAssetPairDownloadHelper(
sha256ActionMessage='download geoip sha256',
assetsActionMessage='download geoip assets',
)
def configureHttpProxy(self, httpProxy: Union[str, None]) -> bool:
"""Configure HTTP proxy."""
return all(
[
self.downloadHelperGeosite.configureHttpProxy(httpProxy),
self.downloadHelperGeoip.configureHttpProxy(httpProxy),
]
)
def download(self):
"""Download the Xray asset download manager."""
self.downloadHelperGeosite.download(
sha256URL=URL_GEOSITE_SHA256,
assetsURL=URL_GEOSITE,
filepath=XRAY_ASSET_PATH_GEOSITE,
)
self.downloadHelperGeoip.download(
sha256URL=URL_GEOIP_SHA256,
assetsURL=URL_GEOIP,
filepath=XRAY_ASSET_PATH_GEOIP,
)