Files
LorenEteval_Furious/Furious/QtFramework/XrayAssetDownloadManager.py
T
2025-06-22 11:07:16 +08:00

215 lines
6.8 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/>.
from __future__ import annotations
from Furious.QtFramework.QtNetwork import *
from Furious.QtFramework.QtWidgets import *
from Furious.QtFramework.DynamicTranslate import gettext as _
from Furious.QtFramework.WebGETManager import *
from Furious.Utility import *
from Furious.Library import *
from PySide6 import QtCore
from PySide6.QtGui import *
from PySide6.QtNetwork import *
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):
finished = QtCore.Signal(str)
def __init__(self, string=b''):
# Explictly called __init__
QtCore.QObject.__init__(self)
QtCore.QRunnable.__init__(self)
self.string = string
def run(self):
self.finished.emit(hashlib.sha256(self.string).hexdigest())
class XrayAssetSHA256DownloadManager(WebGETManager):
def __init__(self, parent=None, **kwargs):
actionMessage = kwargs.pop('actionMessage', 'download sha256')
super().__init__(parent, actionMessage=actionMessage)
@staticmethod
def fileContent(filepath, mode='rb') -> AnyStr:
try:
with open(filepath, mode) as file:
return file.read()
except Exception:
# Any non-exit exceptions
return b''
def successCallback(self, networkReply, **kwargs):
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=''):
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]):
self.webGET(
url,
filepath=str(filepath),
downloadCallback=downloadCallback,
)
class XrayAssetAssetsDownloadManager(WebGETManager):
def __init__(self, parent=None, **kwargs):
actionMessage = kwargs.pop('actionMessage', 'download assets')
super().__init__(parent, actionMessage=actionMessage)
def successCallback(self, networkReply, **kwargs):
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):
self.webGET(url, filepath=str(filepath))
class XrayAssetPairDownloadHelper:
def __init__(self, *args, **kwargs):
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:
return all(
[
self.sha256Downloader.configureHttpProxy(httpProxy),
self.assetsDownloader.configureHttpProxy(httpProxy),
]
)
def download(self, sha256URL, assetsURL, filepath):
self.sha256Downloader.download(
url=sha256URL,
filepath=filepath,
downloadCallback=functools.partial(
self.assetsDownloader.download, assetsURL, filepath
),
)
class XrayAssetDownloadManager:
def __init__(self, *args, **kwargs):
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:
return all(
[
self.downloadHelperGeosite.configureHttpProxy(httpProxy),
self.downloadHelperGeoip.configureHttpProxy(httpProxy),
]
)
def download(self):
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,
)