mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-09-29 18:38:02 +03:00
@@ -17,7 +17,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
+245
-233
@@ -17,13 +17,19 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Core import *
|
||||
from Furious.Core.CoreProcessWorker import *
|
||||
from Furious.Core.XrayCore import *
|
||||
from Furious.Core.Hysteria1 import *
|
||||
from Furious.Core.Hysteria2 import *
|
||||
from Furious.Core.Tun2socks import *
|
||||
|
||||
from typing import Tuple, Union
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
@@ -35,7 +41,7 @@ __all__ = ['CoreManager']
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def fixLogObjectPath(config: ConfigurationFactory, attr: str, value: str, log=True):
|
||||
def fixLogObjectPath(config: ConfigFactory, attr: str, value: str, log=True):
|
||||
try:
|
||||
path = config['log'][attr]
|
||||
except Exception:
|
||||
@@ -47,15 +53,15 @@ def fixLogObjectPath(config: ConfigurationFactory, attr: str, value: str, log=Tr
|
||||
config['log'][attr] = path = ''
|
||||
|
||||
if path == '':
|
||||
if isPythonw() and StdoutRedirectHelper.TemporaryDir.isValid():
|
||||
if SystemRuntime.isPythonw() and ProcessOutputRedirector.TemporaryDir.isValid():
|
||||
# Redirect implementation for pythonw environment
|
||||
config['log'][attr] = StdoutRedirectHelper.TemporaryDir.filePath(value)
|
||||
config['log'][attr] = ProcessOutputRedirector.TemporaryDir.filePath(value)
|
||||
else:
|
||||
# Relative path fails if booting on start up
|
||||
# on Windows, when packed using nuitka...
|
||||
|
||||
# Fix relative path if needed. User cannot feel this operation.
|
||||
config['log'][attr] = getAbsolutePath(path)
|
||||
config['log'][attr] = absolutePath(path)
|
||||
|
||||
result = config['log'][attr]
|
||||
|
||||
@@ -79,44 +85,222 @@ def fixLogObjectPath(config: ConfigurationFactory, attr: str, value: str, log=Tr
|
||||
|
||||
|
||||
def getUserTUNSettings(*args, **kwargs):
|
||||
return AS_UserTUNSettings().get(*args, **kwargs)
|
||||
return Storage.UserTUNSettings().get(*args, **kwargs)
|
||||
|
||||
|
||||
userPrimaryAdapterInterfaceName = functools.partial(
|
||||
getUserTUNSettings, 'primaryAdapterInterfaceName', ''
|
||||
)
|
||||
userPrimaryAdapterInterfaceIP = functools.partial(
|
||||
getUserTUNSettings, 'primaryAdapterInterfaceIP', ''
|
||||
)
|
||||
userDefaultPrimaryGatewayIP = functools.partial(
|
||||
getUserTUNSettings, 'defaultPrimaryGatewayIP', ''
|
||||
)
|
||||
userTunAdapterInterfaceDNS = functools.partial(
|
||||
getUserTUNSettings, 'tunAdapterInterfaceDNS', ''
|
||||
)
|
||||
userBypassTUNAdapterInterfaceIP = functools.partial(
|
||||
getUserTUNSettings, 'bypassTUNAdapterInterfaceIP', ''
|
||||
)
|
||||
userDisablePrimaryAdapterInterfaceDNS = functools.partial(
|
||||
getUserTUNSettings, 'disablePrimaryAdapterInterfaceDNS', 'True'
|
||||
(
|
||||
userPrimaryAdapterInterfaceName,
|
||||
userPrimaryAdapterInterfaceIP,
|
||||
userDefaultPrimaryGatewayIP,
|
||||
userTunAdapterInterfaceDNS,
|
||||
userBypassTUNAdapterInterfaceIP,
|
||||
userDisablePrimaryAdapterInterfaceDNS,
|
||||
) = (
|
||||
functools.partial(getUserTUNSettings, 'primaryAdapterInterfaceName', ''),
|
||||
functools.partial(getUserTUNSettings, 'primaryAdapterInterfaceIP', ''),
|
||||
functools.partial(getUserTUNSettings, 'defaultPrimaryGatewayIP', ''),
|
||||
functools.partial(getUserTUNSettings, 'tunAdapterInterfaceDNS', ''),
|
||||
functools.partial(getUserTUNSettings, 'bypassTUNAdapterInterfaceIP', ''),
|
||||
functools.partial(getUserTUNSettings, 'disablePrimaryAdapterInterfaceDNS', 'True'),
|
||||
)
|
||||
|
||||
|
||||
class CoreManager(SupportExitCleanup):
|
||||
class CoreManager(Mixins.CleanupOnExit):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.uniqueCleanup = False
|
||||
self.processesPool = list()
|
||||
|
||||
self.coresPool = []
|
||||
@functools.singledispatchmethod
|
||||
def _startCore(
|
||||
self,
|
||||
config,
|
||||
routing,
|
||||
exitCallback=None,
|
||||
msgCallback=None,
|
||||
proxyModeOnly=False,
|
||||
log=True,
|
||||
**kwargs,
|
||||
) -> Tuple[Union[CoreProcessWorker, None], bool]:
|
||||
return None, False
|
||||
|
||||
@_startCore.register(ConfigXray)
|
||||
def _(
|
||||
self,
|
||||
config,
|
||||
routing,
|
||||
exitCallback=None,
|
||||
msgCallback=None,
|
||||
proxyModeOnly=False,
|
||||
log=True,
|
||||
**kwargs,
|
||||
):
|
||||
if config.get('log') is None or not isinstance(config['log'], dict):
|
||||
config['log'] = {
|
||||
'access': '',
|
||||
'error': '',
|
||||
'loglevel': 'warning',
|
||||
}
|
||||
|
||||
logRedirectValue = str(uuid.uuid4())
|
||||
|
||||
# Fix logObject
|
||||
for attr in ['access', 'error']:
|
||||
fixLogObjectPath(config, attr, logRedirectValue, log)
|
||||
|
||||
if routing == 'Bypass Mainland China':
|
||||
# TUN Mode handling
|
||||
if not proxyModeOnly and SystemRuntime.isTUNMode():
|
||||
mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
mbox.setWindowTitle(_('Unable to connect'))
|
||||
mbox.setText(
|
||||
_('Routing option with direct rules is not allowed in TUN mode')
|
||||
)
|
||||
|
||||
# Show the MessageBox asynchronously
|
||||
mbox.open()
|
||||
|
||||
return False
|
||||
|
||||
routingObject = {
|
||||
'domainStrategy': 'IPIfNonMatch',
|
||||
'domainMatcher': 'hybrid',
|
||||
'rules': [
|
||||
{
|
||||
'type': 'field',
|
||||
'domain': [
|
||||
'geosite:category-ads-all',
|
||||
],
|
||||
'outboundTag': 'block',
|
||||
},
|
||||
{
|
||||
'type': 'field',
|
||||
'domain': [
|
||||
'geosite:cn',
|
||||
],
|
||||
'outboundTag': 'direct',
|
||||
},
|
||||
{
|
||||
'type': 'field',
|
||||
'ip': [
|
||||
'geoip:private',
|
||||
'geoip:cn',
|
||||
],
|
||||
'outboundTag': 'direct',
|
||||
},
|
||||
{
|
||||
'type': 'field',
|
||||
'port': '0-65535',
|
||||
'outboundTag': 'proxy',
|
||||
},
|
||||
],
|
||||
}
|
||||
elif routing == 'Global':
|
||||
routingObject = {}
|
||||
elif routing == 'Custom':
|
||||
routingObject = config.get('routing', {})
|
||||
else:
|
||||
routingObject = {}
|
||||
|
||||
if log:
|
||||
logger.info(f'core {XrayCore.name()} configured')
|
||||
logger.info(f'routing is {routing}')
|
||||
logger.info(f'RoutingObject: {routingObject}')
|
||||
|
||||
config['routing'] = routingObject
|
||||
|
||||
process = XrayCore(exitCallback=exitCallback, msgCallback=msgCallback)
|
||||
success = process.start(config, **kwargs)
|
||||
|
||||
return process, success
|
||||
|
||||
@_startCore.register(ConfigHysteria1)
|
||||
def _(
|
||||
self,
|
||||
config,
|
||||
routing,
|
||||
exitCallback=None,
|
||||
msgCallback=None,
|
||||
proxyModeOnly=False,
|
||||
log=True,
|
||||
**kwargs,
|
||||
):
|
||||
if routing == 'Bypass Mainland China':
|
||||
# TUN Mode handling
|
||||
if not proxyModeOnly and SystemRuntime.isTUNMode():
|
||||
mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
mbox.setWindowTitle(_('Unable to connect'))
|
||||
mbox.setText(
|
||||
_('Routing option with direct rules is not allowed in TUN mode')
|
||||
)
|
||||
|
||||
# Show the MessageBox asynchronously
|
||||
mbox.open()
|
||||
|
||||
return False
|
||||
|
||||
routingObject = {
|
||||
'rule': DATA_DIR / 'hysteria' / 'bypass-mainland-China.acl',
|
||||
'mmdb': DATA_DIR / 'hysteria' / 'country.mmdb',
|
||||
}
|
||||
elif routing == 'Global':
|
||||
routingObject = {
|
||||
'rule': '',
|
||||
'mmdb': '',
|
||||
}
|
||||
elif routing == 'Custom':
|
||||
routingObject = {
|
||||
'rule': config.get('acl', ''),
|
||||
'mmdb': config.get('mmdb', ''),
|
||||
}
|
||||
else:
|
||||
routingObject = {
|
||||
'rule': '',
|
||||
'mmdb': '',
|
||||
}
|
||||
|
||||
if log:
|
||||
logger.info(f'core {Hysteria1.name()} configured')
|
||||
logger.info(f'routing is {routing}')
|
||||
logger.info(f'RoutingObject: {routingObject}')
|
||||
|
||||
process = Hysteria1(exitCallback=exitCallback, msgCallback=msgCallback)
|
||||
success = process.start(
|
||||
config,
|
||||
Hysteria1.rule(routingObject.get('rule', '')),
|
||||
Hysteria1.mmdb(routingObject.get('mmdb', '')),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return process, success
|
||||
|
||||
@_startCore.register(ConfigHysteria2)
|
||||
def _(
|
||||
self,
|
||||
config,
|
||||
routing,
|
||||
exitCallback=None,
|
||||
msgCallback=None,
|
||||
proxyModeOnly=False,
|
||||
log=True,
|
||||
**kwargs,
|
||||
):
|
||||
if log:
|
||||
logger.info(f'core {Hysteria2.name()} configured')
|
||||
|
||||
process = Hysteria2(exitCallback=exitCallback, msgCallback=msgCallback)
|
||||
success = process.start(config, **kwargs)
|
||||
|
||||
return process, success
|
||||
|
||||
def start(
|
||||
self,
|
||||
config: ConfigurationFactory,
|
||||
config: ConfigFactory,
|
||||
routing: str,
|
||||
exitCallback=None,
|
||||
msgCallbackCore=None,
|
||||
msgCallbackTun_=None,
|
||||
msgCallbackTUN_=None,
|
||||
deepcopy=True,
|
||||
proxyModeOnly=False,
|
||||
log=True,
|
||||
@@ -127,197 +311,27 @@ class CoreManager(SupportExitCleanup):
|
||||
else:
|
||||
configcopy = config
|
||||
|
||||
if isinstance(configcopy, ConfigurationXray):
|
||||
if configcopy.get('log') is None or not isinstance(configcopy['log'], dict):
|
||||
configcopy['log'] = {
|
||||
'access': '',
|
||||
'error': '',
|
||||
'loglevel': 'warning',
|
||||
}
|
||||
process, success = self._startCore(
|
||||
configcopy,
|
||||
routing,
|
||||
exitCallback,
|
||||
msgCallbackCore,
|
||||
proxyModeOnly,
|
||||
log,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
logRedirectValue = str(uuid.uuid4())
|
||||
|
||||
# Fix logObject
|
||||
for attr in ['access', 'error']:
|
||||
fixLogObjectPath(configcopy, attr, logRedirectValue, log)
|
||||
|
||||
if routing == 'Bypass Mainland China':
|
||||
# TUN Mode handling
|
||||
if not proxyModeOnly and isTUNMode():
|
||||
mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
mbox.setWindowTitle(_('Unable to connect'))
|
||||
mbox.setText(
|
||||
_('Routing option with direct rules is not allowed in TUN mode')
|
||||
)
|
||||
|
||||
# Show the MessageBox asynchronously
|
||||
mbox.open()
|
||||
|
||||
return False
|
||||
|
||||
routingObject = {
|
||||
'domainStrategy': 'IPIfNonMatch',
|
||||
'domainMatcher': 'hybrid',
|
||||
'rules': [
|
||||
{
|
||||
'type': 'field',
|
||||
'domain': [
|
||||
'geosite:category-ads-all',
|
||||
],
|
||||
'outboundTag': 'block',
|
||||
},
|
||||
{
|
||||
'type': 'field',
|
||||
'domain': [
|
||||
'geosite:cn',
|
||||
],
|
||||
'outboundTag': 'direct',
|
||||
},
|
||||
{
|
||||
'type': 'field',
|
||||
'ip': [
|
||||
'geoip:private',
|
||||
'geoip:cn',
|
||||
],
|
||||
'outboundTag': 'direct',
|
||||
},
|
||||
{
|
||||
'type': 'field',
|
||||
'port': '0-65535',
|
||||
'outboundTag': 'proxy',
|
||||
},
|
||||
],
|
||||
}
|
||||
# elif routing == 'Bypass Iran':
|
||||
# routingObject = {
|
||||
# 'domainStrategy': 'IPIfNonMatch',
|
||||
# 'domainMatcher': 'hybrid',
|
||||
# 'rules': [
|
||||
# {
|
||||
# 'type': 'field',
|
||||
# 'domain': [
|
||||
# 'geosite:category-ads-all',
|
||||
# 'iran:ads',
|
||||
# ],
|
||||
# 'outboundTag': 'block',
|
||||
# },
|
||||
# {
|
||||
# 'type': 'field',
|
||||
# 'domain': [
|
||||
# 'iran:ir',
|
||||
# 'iran:other',
|
||||
# ],
|
||||
# 'outboundTag': 'direct',
|
||||
# },
|
||||
# {
|
||||
# 'type': 'field',
|
||||
# 'ip': [
|
||||
# 'geoip:private',
|
||||
# 'geoip:ir',
|
||||
# ],
|
||||
# 'outboundTag': 'direct',
|
||||
# },
|
||||
# {
|
||||
# 'type': 'field',
|
||||
# 'port': '0-65535',
|
||||
# 'outboundTag': 'proxy',
|
||||
# },
|
||||
# ],
|
||||
# }
|
||||
elif routing == 'Global':
|
||||
routingObject = {}
|
||||
elif routing == 'Custom':
|
||||
routingObject = configcopy.get('routing', {})
|
||||
else:
|
||||
routingObject = {}
|
||||
|
||||
if log:
|
||||
logger.info(f'core {XrayCore.name()} configured')
|
||||
logger.info(f'routing is {routing}')
|
||||
logger.info(f'RoutingObject: {routingObject}')
|
||||
|
||||
configcopy['routing'] = routingObject
|
||||
|
||||
coreProcess = XrayCore(
|
||||
exitCallback=exitCallback, msgCallback=msgCallbackCore
|
||||
)
|
||||
success = coreProcess.start(configcopy, **kwargs)
|
||||
elif isinstance(configcopy, ConfigurationHysteria1):
|
||||
if routing == 'Bypass Mainland China':
|
||||
# TUN Mode handling
|
||||
if not proxyModeOnly and isTUNMode():
|
||||
mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
mbox.setWindowTitle(_('Unable to connect'))
|
||||
mbox.setText(
|
||||
_('Routing option with direct rules is not allowed in TUN mode')
|
||||
)
|
||||
|
||||
# Show the MessageBox asynchronously
|
||||
mbox.open()
|
||||
|
||||
return False
|
||||
|
||||
routingObject = {
|
||||
'rule': DATA_DIR / 'hysteria' / 'bypass-mainland-China.acl',
|
||||
'mmdb': DATA_DIR / 'hysteria' / 'country.mmdb',
|
||||
}
|
||||
# elif routing == 'Bypass Iran':
|
||||
# routingObject = {
|
||||
# 'rule': DATA_DIR / 'hysteria' / 'bypass-Iran.acl',
|
||||
# 'mmdb': DATA_DIR / 'hysteria' / 'country.mmdb',
|
||||
# }
|
||||
elif routing == 'Global':
|
||||
routingObject = {
|
||||
'rule': '',
|
||||
'mmdb': '',
|
||||
}
|
||||
elif routing == 'Custom':
|
||||
routingObject = {
|
||||
'rule': configcopy.get('acl', ''),
|
||||
'mmdb': configcopy.get('mmdb', ''),
|
||||
}
|
||||
else:
|
||||
routingObject = {
|
||||
'rule': '',
|
||||
'mmdb': '',
|
||||
}
|
||||
|
||||
if log:
|
||||
logger.info(f'core {Hysteria1.name()} configured')
|
||||
logger.info(f'routing is {routing}')
|
||||
logger.info(f'RoutingObject: {routingObject}')
|
||||
|
||||
coreProcess = Hysteria1(
|
||||
exitCallback=exitCallback, msgCallback=msgCallbackCore
|
||||
)
|
||||
success = coreProcess.start(
|
||||
configcopy,
|
||||
Hysteria1.rule(routingObject.get('rule', '')),
|
||||
Hysteria1.mmdb(routingObject.get('mmdb', '')),
|
||||
**kwargs,
|
||||
)
|
||||
elif isinstance(configcopy, ConfigurationHysteria2):
|
||||
if log:
|
||||
logger.info(f'core {Hysteria2.name()} configured')
|
||||
|
||||
coreProcess = Hysteria2(
|
||||
exitCallback=exitCallback, msgCallback=msgCallbackCore
|
||||
)
|
||||
success = coreProcess.start(configcopy, **kwargs)
|
||||
else:
|
||||
coreProcess = None
|
||||
success = False
|
||||
|
||||
if coreProcess is not None:
|
||||
self.coresPool.append(coreProcess)
|
||||
if process is not None:
|
||||
self.processesPool.append(process)
|
||||
|
||||
if not success:
|
||||
logger.error(f'core {coreProcess.name()} start failed')
|
||||
if isinstance(process, CoreProcessWorker):
|
||||
logger.error(f'core {process.name()} start failed')
|
||||
|
||||
return success
|
||||
|
||||
# TUN Mode handling
|
||||
if not proxyModeOnly and isTUNMode():
|
||||
if not proxyModeOnly and SystemRuntime.isTUNMode():
|
||||
# Currently TUN Mode is only supported on Windows and macOS
|
||||
if PLATFORM == 'Windows' or PLATFORM == 'Darwin':
|
||||
if PLATFORM == 'Windows':
|
||||
@@ -368,16 +382,14 @@ class CoreManager(SupportExitCleanup):
|
||||
else:
|
||||
gateway, interfaceIP = defaultGateway[0], None
|
||||
|
||||
tunProcess = Tun2socks(
|
||||
exitCallback=exitCallback, msgCallback=msgCallbackTun_
|
||||
)
|
||||
self.coresPool.append(tunProcess)
|
||||
tun = Tun2socks(exitCallback=exitCallback, msgCallback=msgCallbackTUN_)
|
||||
self.processesPool.append(tun)
|
||||
|
||||
if not tunProcess.start(
|
||||
if not tun.start(
|
||||
APPLICATION_TUN_DEVICE_NAME,
|
||||
APPLICATION_TUN_NETWORK_INTERFACE_NAME,
|
||||
'error',
|
||||
f'socks5://{configcopy.socksProxyEndpoint()}',
|
||||
f'socks5://{configcopy.socksProxy()}',
|
||||
'',
|
||||
):
|
||||
return False
|
||||
@@ -392,7 +404,7 @@ class CoreManager(SupportExitCleanup):
|
||||
# Any non-exit exceptions
|
||||
|
||||
logger.error(
|
||||
f'error when processing ' f'user TUN bypass Settings: {ex}'
|
||||
f'error when processing user TUN bypass settings: {ex}'
|
||||
)
|
||||
|
||||
SystemRoutingTable.Relations.clear()
|
||||
@@ -407,7 +419,7 @@ class CoreManager(SupportExitCleanup):
|
||||
else:
|
||||
logger.error(
|
||||
f'invalid IP address when processing '
|
||||
f'user TUN bypass Settings: {bypass}'
|
||||
f'user TUN bypass settings: {bypass}'
|
||||
)
|
||||
|
||||
SystemRoutingTable.Relations.clear()
|
||||
@@ -422,7 +434,7 @@ class CoreManager(SupportExitCleanup):
|
||||
address = configcopy.itemAddress
|
||||
|
||||
if not isValidIPAddress(address):
|
||||
DNSResolver.configureHttpProxy(configcopy.httpProxyEndpoint())
|
||||
DNSResolver.configureHttpProxy(configcopy.httpProxy())
|
||||
|
||||
error, resolved = DNSResolver.resolve(address)
|
||||
|
||||
@@ -466,7 +478,7 @@ class CoreManager(SupportExitCleanup):
|
||||
|
||||
break
|
||||
|
||||
PySide6LegacyEventLoopWait(100)
|
||||
PySide6Legacy.eventLoopWait(100)
|
||||
|
||||
if not foundDevice:
|
||||
logger.error(
|
||||
@@ -501,7 +513,7 @@ class CoreManager(SupportExitCleanup):
|
||||
SystemRoutingTable.WIN32SetInterfaceDNS(_alias)
|
||||
SystemRoutingTable.WIN32FlushDNSCache()
|
||||
|
||||
tunProcess.cleanup = functools.partial(_windowsCleanup, alias)
|
||||
tun.cleanup = functools.partial(_windowsCleanup, alias)
|
||||
|
||||
# Handle user defined settings
|
||||
userDisableInterfaceDNS = (
|
||||
@@ -556,7 +568,7 @@ class CoreManager(SupportExitCleanup):
|
||||
for _service, _dnsserver in _servers:
|
||||
SystemRoutingTable.DarwinSetDNSServers(_service, _dnsserver)
|
||||
|
||||
tunProcess.cleanup = functools.partial(_darwinCleanup, servers)
|
||||
tun.cleanup = functools.partial(_darwinCleanup, servers)
|
||||
|
||||
# Handle user defined settings
|
||||
userTunInterfaceDNS = userTunAdapterInterfaceDNS()
|
||||
@@ -585,18 +597,18 @@ class CoreManager(SupportExitCleanup):
|
||||
return True
|
||||
|
||||
def allRunning(self) -> bool:
|
||||
return all(coreProcess.isAlive() for coreProcess in self.coresPool)
|
||||
return all(process.isAlive() for process in self.processesPool)
|
||||
|
||||
def anyRunning(self) -> bool:
|
||||
return any(coreProcess.isAlive() for coreProcess in self.coresPool)
|
||||
return any(process.isAlive() for process in self.processesPool)
|
||||
|
||||
def stopAll(self):
|
||||
if self.coresPool:
|
||||
for coreProcess in self.coresPool:
|
||||
if isinstance(coreProcess, CoreFactory):
|
||||
coreProcess.stop()
|
||||
if self.processesPool:
|
||||
for process in self.processesPool:
|
||||
if isinstance(process, CoreProcessFactory):
|
||||
process.stop()
|
||||
|
||||
self.coresPool.clear()
|
||||
self.processesPool.clear()
|
||||
|
||||
def cleanup(self):
|
||||
self.stopAll()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.Utility import *
|
||||
|
||||
@@ -34,55 +35,11 @@ import threading
|
||||
import multiprocessing
|
||||
import multiprocessing.queues
|
||||
|
||||
__all__ = ['CoreProcess', 'MsgQueue', 'StdoutRedirectHelper']
|
||||
__all__ = ['MsgQueue', 'CoreProcessWorker', 'ProcessOutputRedirector']
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CoreProcessDaemon(CoreFactory, ABC):
|
||||
def __init__(self, **kwargs):
|
||||
exitCallback = kwargs.pop('exitCallback', None)
|
||||
|
||||
super().__init__(exitCallback)
|
||||
|
||||
self._process = None
|
||||
|
||||
self._daemon = QtCore.QTimer()
|
||||
self._daemon.timeout.connect(self.queryIsAlive)
|
||||
|
||||
@property
|
||||
def process(self) -> Union[multiprocessing.Process, None]:
|
||||
return self._process
|
||||
|
||||
@process.setter
|
||||
def process(self, process: Union[multiprocessing.Process, None]):
|
||||
self._process = process
|
||||
|
||||
@property
|
||||
def daemon(self) -> QtCore.QTimer:
|
||||
return self._daemon
|
||||
|
||||
def isAlive(self) -> bool:
|
||||
if isinstance(self.process, multiprocessing.Process):
|
||||
return self.process.is_alive()
|
||||
else:
|
||||
return False
|
||||
|
||||
def queryIsAlive(self):
|
||||
if isinstance(self.process, multiprocessing.Process):
|
||||
if self.process.is_alive():
|
||||
return True
|
||||
else:
|
||||
self.handleInteralProcessStopped()
|
||||
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def handleInteralProcessStopped(self, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class MsgQueue(multiprocessing.queues.Queue):
|
||||
MSG_PRODUCE_THRESHOLD = 1024
|
||||
OPTIMIZER_MIN_FREQ = 2
|
||||
@@ -165,11 +122,55 @@ class MsgQueue(multiprocessing.queues.Queue):
|
||||
self.startTimer()
|
||||
|
||||
|
||||
class CoreProcess(CoreProcessDaemon):
|
||||
class CoreProcessMonitor(CoreProcessFactory, ABC):
|
||||
def __init__(self, **kwargs):
|
||||
exitCallback = kwargs.pop('exitCallback', None)
|
||||
|
||||
super().__init__(exitCallback)
|
||||
|
||||
self._process = None
|
||||
|
||||
self._daemon = QtCore.QTimer()
|
||||
self._daemon.timeout.connect(self.queryIsAlive)
|
||||
|
||||
@property
|
||||
def process(self) -> Union[multiprocessing.Process, None]:
|
||||
return self._process
|
||||
|
||||
@process.setter
|
||||
def process(self, process: Union[multiprocessing.Process, None]):
|
||||
self._process = process
|
||||
|
||||
@property
|
||||
def daemon(self) -> QtCore.QTimer:
|
||||
return self._daemon
|
||||
|
||||
def isAlive(self) -> bool:
|
||||
if isinstance(self.process, multiprocessing.Process):
|
||||
return self.process.is_alive()
|
||||
else:
|
||||
return False
|
||||
|
||||
def queryIsAlive(self):
|
||||
if isinstance(self.process, multiprocessing.Process):
|
||||
if self.process.is_alive():
|
||||
return True
|
||||
else:
|
||||
self.handleInteralProcessStopped()
|
||||
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def handleInteralProcessStopped(self, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CoreProcessWorker(CoreProcessMonitor, ABC):
|
||||
def __init__(self, **kwargs):
|
||||
msgCallback = kwargs.pop('msgCallback', None)
|
||||
# Optimizer is loggerCore window
|
||||
backgroundOptimizer = kwargs.pop('backgroundOptimizer', loggerCore)
|
||||
# Optimizer is AppLoggerWindow.Core window
|
||||
backgroundOptimizer = kwargs.pop('backgroundOptimizer', AppLoggerWindow.Core)
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@@ -205,7 +206,7 @@ class CoreProcess(CoreProcessDaemon):
|
||||
|
||||
if waitCore:
|
||||
# Wait for the core to start up completely
|
||||
PySide6LegacyEventLoopWait(waitTime)
|
||||
PySide6Legacy.eventLoopWait(waitTime)
|
||||
|
||||
if self.queryIsAlive():
|
||||
# Start core daemon
|
||||
@@ -231,7 +232,7 @@ class CoreProcess(CoreProcessDaemon):
|
||||
pass
|
||||
|
||||
|
||||
class StdoutRedirectHelper:
|
||||
class ProcessOutputRedirector:
|
||||
TemporaryDir = QtCore.QTemporaryDir()
|
||||
|
||||
@staticmethod
|
||||
@@ -242,17 +243,17 @@ class StdoutRedirectHelper:
|
||||
return
|
||||
|
||||
if (
|
||||
not StdoutRedirectHelper.TemporaryDir.isValid()
|
||||
not ProcessOutputRedirector.TemporaryDir.isValid()
|
||||
or not redirect
|
||||
# pythonw.exe
|
||||
or isPythonw()
|
||||
or SystemRuntime.isPythonw()
|
||||
):
|
||||
# Call entrypoint directly
|
||||
entrypoint()
|
||||
|
||||
return
|
||||
|
||||
temporaryFile = StdoutRedirectHelper.TemporaryDir.filePath(str(uuid.uuid4()))
|
||||
temporaryFile = ProcessOutputRedirector.TemporaryDir.filePath(str(uuid.uuid4()))
|
||||
tmpFileStream = open(temporaryFile, 'w+b')
|
||||
stdoutFileno_ = sys.stdout.fileno()
|
||||
stderrFileno_ = sys.stderr.fileno()
|
||||
+21
-31
@@ -17,11 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Core.CoreProcessWorker import *
|
||||
|
||||
from enum import Enum
|
||||
from typing import Union
|
||||
|
||||
import time
|
||||
@@ -40,22 +39,22 @@ def startHysteria1(jsonString, rule, mmdb, msgQueue: multiprocessing.Queue):
|
||||
except ImportError:
|
||||
# Fake running process
|
||||
while True:
|
||||
time.sleep(1)
|
||||
time.sleep(3600)
|
||||
else:
|
||||
if versionToValue(hysteria.__version__) <= versionToValue('1.3.5'):
|
||||
redirect = False
|
||||
else:
|
||||
redirect = True
|
||||
|
||||
StdoutRedirectHelper.launch(
|
||||
ProcessOutputRedirector.launch(
|
||||
msgQueue,
|
||||
functools.partial(hysteria.startFromJSON, jsonString, rule, mmdb),
|
||||
redirect,
|
||||
)
|
||||
|
||||
|
||||
class Hysteria1(CoreProcess):
|
||||
class ExitCode:
|
||||
class Hysteria1(CoreProcessWorker):
|
||||
class ExitCode(Enum):
|
||||
ConfigurationError = 23
|
||||
RemoteNetworkError = 3
|
||||
# Windows shutting down
|
||||
@@ -70,7 +69,7 @@ class Hysteria1(CoreProcess):
|
||||
return ''
|
||||
|
||||
try:
|
||||
path = getAbsolutePath(str(rulePath))
|
||||
path = absolutePath(str(rulePath))
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -100,7 +99,7 @@ class Hysteria1(CoreProcess):
|
||||
return ''
|
||||
|
||||
try:
|
||||
path = getAbsolutePath(str(mmdbPath))
|
||||
path = absolutePath(str(mmdbPath))
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -139,28 +138,19 @@ class Hysteria1(CoreProcess):
|
||||
|
||||
return '0.0.0'
|
||||
|
||||
def startFromArgs(self, jsonString: str, rule, mmdb, **kwargs) -> bool:
|
||||
self.registerCurrentJSONConfig(jsonString)
|
||||
|
||||
return super().start(
|
||||
target=startHysteria1,
|
||||
args=(jsonString, rule, mmdb, self.msgQueue),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def start(self, config: Union[str, dict], rule, mmdb, **kwargs) -> bool:
|
||||
if isinstance(config, str):
|
||||
return self.startFromArgs(config, rule, mmdb, **kwargs)
|
||||
elif isinstance(config, ConfigurationFactory):
|
||||
return self.startFromArgs(config.toJSONString(), rule, mmdb, **kwargs)
|
||||
elif isinstance(config, dict):
|
||||
try:
|
||||
jsonString = UJSONEncoder.encode(config)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
param = self.toJSONString(config)
|
||||
|
||||
return False
|
||||
else:
|
||||
return self.startFromArgs(jsonString, rule, mmdb, **kwargs)
|
||||
if param:
|
||||
return super().start(
|
||||
target=startHysteria1,
|
||||
args=(
|
||||
param,
|
||||
rule,
|
||||
mmdb,
|
||||
self.msgQueue,
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
+17
-27
@@ -17,11 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Core.CoreProcessWorker import *
|
||||
|
||||
from enum import Enum
|
||||
from typing import Union
|
||||
|
||||
import time
|
||||
@@ -40,22 +39,22 @@ def startHysteria2(jsonString: str, msgQueue: multiprocessing.Queue):
|
||||
except ImportError:
|
||||
# Fake running process
|
||||
while True:
|
||||
time.sleep(1)
|
||||
time.sleep(3600)
|
||||
else:
|
||||
if versionToValue(hysteria2.__version__) <= versionToValue('2.0.0.1'):
|
||||
redirect = False
|
||||
else:
|
||||
redirect = True
|
||||
|
||||
StdoutRedirectHelper.launch(
|
||||
ProcessOutputRedirector.launch(
|
||||
msgQueue,
|
||||
functools.partial(hysteria2.startFromJSON, jsonString),
|
||||
redirect,
|
||||
)
|
||||
|
||||
|
||||
class Hysteria2(CoreProcess):
|
||||
class ExitCode:
|
||||
class Hysteria2(CoreProcessWorker):
|
||||
class ExitCode(Enum):
|
||||
ConfigurationError = 23
|
||||
# Windows: 4294967295. Darwin, Linux: 255 (-1)
|
||||
ServerStartFailure = 4294967295 if PLATFORM == 'Windows' else 255
|
||||
@@ -80,26 +79,17 @@ class Hysteria2(CoreProcess):
|
||||
|
||||
return '0.0.0'
|
||||
|
||||
def startFromArgs(self, jsonString: str, **kwargs) -> bool:
|
||||
self.registerCurrentJSONConfig(jsonString)
|
||||
|
||||
return super().start(
|
||||
target=startHysteria2, args=(jsonString, self.msgQueue), **kwargs
|
||||
)
|
||||
|
||||
def start(self, config: Union[str, dict], **kwargs) -> bool:
|
||||
if isinstance(config, str):
|
||||
return self.startFromArgs(config, **kwargs)
|
||||
elif isinstance(config, ConfigurationFactory):
|
||||
return self.startFromArgs(config.toJSONString(), **kwargs)
|
||||
elif isinstance(config, dict):
|
||||
try:
|
||||
jsonString = UJSONEncoder.encode(config)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
param = self.toJSONString(config)
|
||||
|
||||
return False
|
||||
else:
|
||||
return self.startFromArgs(jsonString, **kwargs)
|
||||
if param:
|
||||
return super().start(
|
||||
target=startHysteria2,
|
||||
args=(
|
||||
param,
|
||||
self.msgQueue,
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
+15
-10
@@ -17,10 +17,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Core.CoreProcessWorker import *
|
||||
|
||||
import time
|
||||
import functools
|
||||
@@ -35,28 +33,28 @@ def startTun2socks(msgQueue: multiprocessing.Queue, *args):
|
||||
except ImportError:
|
||||
# Fake running process
|
||||
while True:
|
||||
time.sleep(1)
|
||||
time.sleep(3600)
|
||||
else:
|
||||
if versionToValue(tun2socks.__version__) <= versionToValue('2.5.1.1'):
|
||||
redirect = False
|
||||
else:
|
||||
redirect = True
|
||||
|
||||
StdoutRedirectHelper.launch(
|
||||
ProcessOutputRedirector.launch(
|
||||
msgQueue,
|
||||
functools.partial(tun2socks.startFromArgs, *args),
|
||||
redirect,
|
||||
)
|
||||
|
||||
|
||||
class Tun2socks(CoreProcess):
|
||||
class Tun2socks(CoreProcessWorker):
|
||||
class ExitCode:
|
||||
# Windows shutting down
|
||||
SystemShuttingDown = 0x40010004
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Optimizer is loggerTun_ window
|
||||
backgroundOptimizer = kwargs.pop('backgroundOptimizer', loggerTun_)
|
||||
# Optimizer is AppLoggerWindow.TUN_ window
|
||||
backgroundOptimizer = kwargs.pop('backgroundOptimizer', AppLoggerWindow.TUN_)
|
||||
|
||||
super().__init__(**kwargs, backgroundOptimizer=backgroundOptimizer)
|
||||
|
||||
@@ -88,7 +86,14 @@ class Tun2socks(CoreProcess):
|
||||
) -> bool:
|
||||
return super().start(
|
||||
target=startTun2socks,
|
||||
args=(self.msgQueue, device, networkInterface, logLevel, proxy, restAPI),
|
||||
args=(
|
||||
self.msgQueue,
|
||||
device,
|
||||
networkInterface,
|
||||
logLevel,
|
||||
proxy,
|
||||
restAPI,
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
+62
-74
@@ -17,11 +17,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Core.CoreProcessWorker import *
|
||||
|
||||
from enum import Enum
|
||||
from typing import Union
|
||||
|
||||
import io
|
||||
@@ -39,7 +41,7 @@ def startXrayCore(jsonString: str, msgQueue: multiprocessing.Queue):
|
||||
except ImportError:
|
||||
# Fake running process
|
||||
while True:
|
||||
time.sleep(1)
|
||||
time.sleep(3600)
|
||||
else:
|
||||
if versionToValue(xray.__version__) <= versionToValue('1.8.4'):
|
||||
redirect = False
|
||||
@@ -47,8 +49,8 @@ def startXrayCore(jsonString: str, msgQueue: multiprocessing.Queue):
|
||||
# Can be redirected
|
||||
redirect = True
|
||||
|
||||
if not isPythonw():
|
||||
return StdoutRedirectHelper.launch(
|
||||
if not SystemRuntime.isPythonw():
|
||||
return ProcessOutputRedirector.launch(
|
||||
msgQueue,
|
||||
functools.partial(xray.startFromJSON, jsonString),
|
||||
redirect,
|
||||
@@ -57,70 +59,65 @@ def startXrayCore(jsonString: str, msgQueue: multiprocessing.Queue):
|
||||
if not redirect:
|
||||
return xray.startFromJSON(jsonString)
|
||||
|
||||
def xrayPythonwProduceMsg():
|
||||
try:
|
||||
jsonObject = UJSONEncoder.decode(jsonString)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
try:
|
||||
jsonObject = UJSONEncoder.decode(jsonString)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
xray.startFromJSON(jsonString)
|
||||
else:
|
||||
loggingPath = []
|
||||
fileStreams = []
|
||||
xray.startFromJSON(jsonString)
|
||||
else:
|
||||
loggingPath = list()
|
||||
fileStreams = list()
|
||||
|
||||
for loggingAttr in ['access', 'error']:
|
||||
try:
|
||||
path = jsonObject['log'][loggingAttr]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
continue
|
||||
|
||||
if path not in loggingPath:
|
||||
loggingPath.append(path)
|
||||
|
||||
for loggingAttr in ['access', 'error']:
|
||||
try:
|
||||
path = jsonObject['log'][loggingAttr]
|
||||
stream = open(path, 'rb')
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
continue
|
||||
pass
|
||||
else:
|
||||
stream.seek(0, io.SEEK_END)
|
||||
|
||||
if path not in loggingPath:
|
||||
loggingPath.append(path)
|
||||
fileStreams.append(stream)
|
||||
|
||||
try:
|
||||
stream = open(path, 'rb')
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
def produceMsg():
|
||||
while True:
|
||||
for file in fileStreams:
|
||||
for line in iter(file.readline, b''):
|
||||
if line and not line.isspace():
|
||||
try:
|
||||
msgQueue.put_nowait(line.decode('utf-8', 'replace'))
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
pass
|
||||
else:
|
||||
stream.seek(0, io.SEEK_END)
|
||||
pass
|
||||
|
||||
fileStreams.append(stream)
|
||||
time.sleep(MsgQueue.MSG_PRODUCE_THRESHOLD / 1000)
|
||||
|
||||
def produceMsg():
|
||||
while True:
|
||||
for file in fileStreams:
|
||||
for line in iter(file.readline, b''):
|
||||
if line and not line.isspace():
|
||||
try:
|
||||
msgQueue.put_nowait(
|
||||
line.decode('utf-8', 'replace')
|
||||
)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
try:
|
||||
if fileStreams:
|
||||
msgThread = threading.Thread(target=produceMsg, daemon=True)
|
||||
msgThread.start()
|
||||
|
||||
pass
|
||||
|
||||
time.sleep(MsgQueue.MSG_PRODUCE_THRESHOLD / 1000)
|
||||
|
||||
try:
|
||||
if fileStreams:
|
||||
msgThread = threading.Thread(target=produceMsg, daemon=True)
|
||||
msgThread.start()
|
||||
|
||||
xray.startFromJSON(jsonString)
|
||||
finally:
|
||||
for stream in fileStreams:
|
||||
stream.close()
|
||||
|
||||
xrayPythonwProduceMsg()
|
||||
xray.startFromJSON(jsonString)
|
||||
finally:
|
||||
for stream in fileStreams:
|
||||
stream.close()
|
||||
|
||||
|
||||
class XrayCore(CoreProcess):
|
||||
class ExitCode:
|
||||
class XrayCore(CoreProcessWorker):
|
||||
class ExitCode(Enum):
|
||||
ConfigurationError = 23
|
||||
# Windows: 4294967295. Darwin, Linux: 255 (-1)
|
||||
ServerStartFailure = 4294967295 if PLATFORM == 'Windows' else 255
|
||||
@@ -145,26 +142,17 @@ class XrayCore(CoreProcess):
|
||||
|
||||
return '0.0.0'
|
||||
|
||||
def startFromArgs(self, jsonString: str, **kwargs) -> bool:
|
||||
self.registerCurrentJSONConfig(jsonString)
|
||||
def start(self, config: Union[str, ConfigFactory, dict], **kwargs) -> bool:
|
||||
param = self.toJSONString(config)
|
||||
|
||||
return super().start(
|
||||
target=startXrayCore, args=(jsonString, self.msgQueue), **kwargs
|
||||
)
|
||||
|
||||
def start(self, config: Union[str, dict], **kwargs) -> bool:
|
||||
if isinstance(config, str):
|
||||
return self.startFromArgs(config, **kwargs)
|
||||
elif isinstance(config, ConfigurationFactory):
|
||||
return self.startFromArgs(config.toJSONString(), **kwargs)
|
||||
elif isinstance(config, dict):
|
||||
try:
|
||||
jsonString = UJSONEncoder.encode(config)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return False
|
||||
else:
|
||||
return self.startFromArgs(jsonString, **kwargs)
|
||||
if param:
|
||||
return super().start(
|
||||
target=startXrayCore,
|
||||
args=(
|
||||
param,
|
||||
self.msgQueue,
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
Binary file not shown.
+68073
-73426
File diff suppressed because one or more lines are too long
Vendored
+14
-11
@@ -1,7 +1,7 @@
|
||||
TRANSLATION = {
|
||||
"Delete": {
|
||||
"source": [
|
||||
"Furious.QtFramework.QtWidgets",
|
||||
"Furious.Qt.QtWidgets",
|
||||
"Furious.Widget.UserServersQTableWidget",
|
||||
"Furious.Widget.UserSubsQTableWidget",
|
||||
"Furious.Widget.XrayAssetViewerQListWidget",
|
||||
@@ -13,7 +13,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Delete these items?": {
|
||||
"source": [
|
||||
"Furious.QtFramework.QtWidgets"
|
||||
"Furious.Qt.QtWidgets"
|
||||
],
|
||||
"RU": "Удалить эти элементы?",
|
||||
"ZH": "删除这些项目?",
|
||||
@@ -21,7 +21,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Delete this item?": {
|
||||
"source": [
|
||||
"Furious.QtFramework.QtWidgets"
|
||||
"Furious.Qt.QtWidgets"
|
||||
],
|
||||
"RU": "Удалить этот элемент?",
|
||||
"ZH": "删除此项目?",
|
||||
@@ -29,7 +29,7 @@ TRANSLATION = {
|
||||
},
|
||||
"New changes will take effect next time": {
|
||||
"source": [
|
||||
"Furious.QtFramework.QtWidgets"
|
||||
"Furious.Qt.QtWidgets"
|
||||
],
|
||||
"RU": "Новые изменения вступят в силу при следующем подключении",
|
||||
"ZH": "新的更改将于下次生效",
|
||||
@@ -37,7 +37,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Unrecognized Configuration. Please modify it in the editor": {
|
||||
"source": [
|
||||
"Furious.QtFramework.QtWidgets"
|
||||
"Furious.Qt.QtWidgets"
|
||||
],
|
||||
"RU": "Нераспознанная конфигурация. Пожалуйста, измените его в редакторе",
|
||||
"ZH": "无法识别的配置。请在编辑器中修改",
|
||||
@@ -45,7 +45,7 @@ TRANSLATION = {
|
||||
},
|
||||
"New version available": {
|
||||
"source": [
|
||||
"Furious.QtFramework.UpdatesManager"
|
||||
"Furious.Qt.UpdatesManager"
|
||||
],
|
||||
"RU": "Доступна новая версия",
|
||||
"ZH": "新版本可用",
|
||||
@@ -53,7 +53,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Check for updates failed": {
|
||||
"source": [
|
||||
"Furious.QtFramework.UpdatesManager"
|
||||
"Furious.Qt.UpdatesManager"
|
||||
],
|
||||
"RU": "Не удалось проверить наличие обновлений",
|
||||
"ZH": "检查更新失败",
|
||||
@@ -61,7 +61,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Go to download page?": {
|
||||
"source": [
|
||||
"Furious.QtFramework.UpdatesManager"
|
||||
"Furious.Qt.UpdatesManager"
|
||||
],
|
||||
"RU": "Перейти на страницу загрузки?",
|
||||
"ZH": "前往下载页面?",
|
||||
@@ -69,7 +69,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Furious is already the latest version": {
|
||||
"source": [
|
||||
"Furious.QtFramework.UpdatesManager"
|
||||
"Furious.Qt.UpdatesManager"
|
||||
],
|
||||
"RU": "Используется последняя версия Furious",
|
||||
"ZH": "Furious已是最新版本",
|
||||
@@ -201,7 +201,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Cancel": {
|
||||
"source": [
|
||||
"Furious.QtFramework.GuiEditorXXX",
|
||||
"Furious.Qt.GuiEditorXXX",
|
||||
"Furious.Widget.IndentSpinBox",
|
||||
"Furious.Window.TextEditorWindow",
|
||||
"Furious.Window.UserSubsWindow"
|
||||
@@ -801,6 +801,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Show Furious Log...": {
|
||||
"source": [
|
||||
"Furious.Widget.UserServersQTableWidget",
|
||||
"Furious.Window.AppMainWindow"
|
||||
],
|
||||
"RU": "Показать журнал Furious...",
|
||||
@@ -809,6 +810,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Show Core Log...": {
|
||||
"source": [
|
||||
"Furious.Widget.UserServersQTableWidget",
|
||||
"Furious.Window.AppMainWindow"
|
||||
],
|
||||
"RU": "Показать журнал ядра...",
|
||||
@@ -817,6 +819,7 @@ TRANSLATION = {
|
||||
},
|
||||
"Show Tun2socks Log...": {
|
||||
"source": [
|
||||
"Furious.Widget.UserServersQTableWidget",
|
||||
"Furious.Window.AppMainWindow"
|
||||
],
|
||||
"RU": "Показать журнал Tun2socks...",
|
||||
@@ -1386,7 +1389,7 @@ TRANSLATION = {
|
||||
"OK": {
|
||||
"source": [
|
||||
"Furious.__main__",
|
||||
"Furious.QtFramework.GuiEditorXXX",
|
||||
"Furious.Qt.GuiEditorXXX",
|
||||
"Furious.Widget.IndentSpinBox",
|
||||
"Furious.Window.UserSubsWindow"
|
||||
],
|
||||
|
||||
@@ -17,17 +17,26 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Utility import BinarySettings
|
||||
|
||||
from PySide6 import QtCore
|
||||
|
||||
import logging
|
||||
|
||||
__all__ = ['AppSettings', 'registerAppSettings']
|
||||
__all__ = [
|
||||
'AppBinarySettings',
|
||||
'AppSettings',
|
||||
'registerAppSettings',
|
||||
]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AppBinarySettings:
|
||||
OFF = '0'
|
||||
ON_ = '1'
|
||||
|
||||
RANGE = [OFF, ON_]
|
||||
|
||||
|
||||
class AppSettings:
|
||||
SettingsPool: dict[str, AppSettings] = dict()
|
||||
|
||||
@@ -42,8 +51,8 @@ class AppSettings:
|
||||
self.isBinary = isBinary
|
||||
|
||||
if isBinary:
|
||||
self.validRange = BinarySettings.RANGE
|
||||
self.default = BinarySettings.OFF if default is None else default
|
||||
self.validRange = AppBinarySettings.RANGE
|
||||
self.default = AppBinarySettings.OFF if default is None else default
|
||||
else:
|
||||
self.validRange = validRange
|
||||
|
||||
@@ -90,7 +99,7 @@ class AppSettings:
|
||||
def isStateON_(key: str) -> bool:
|
||||
value = AppSettings.get(key)
|
||||
|
||||
if value == BinarySettings.ON_:
|
||||
if value == AppBinarySettings.ON_:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -99,7 +108,7 @@ class AppSettings:
|
||||
def isStateOFF(key: str) -> bool:
|
||||
value = AppSettings.get(key)
|
||||
|
||||
if value == BinarySettings.OFF:
|
||||
if value == AppBinarySettings.OFF:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -121,11 +130,11 @@ class AppSettings:
|
||||
|
||||
@staticmethod
|
||||
def turnON_(key: str):
|
||||
AppSettings.set(key, BinarySettings.ON_)
|
||||
AppSettings.set(key, AppBinarySettings.ON_)
|
||||
|
||||
@staticmethod
|
||||
def turnOFF(key: str):
|
||||
AppSettings.set(key, BinarySettings.OFF)
|
||||
AppSettings.set(key, AppBinarySettings.OFF)
|
||||
|
||||
|
||||
def registerAppSettings(name: str, *args, **kwargs):
|
||||
@@ -18,15 +18,11 @@
|
||||
from Furious.Version import __version__
|
||||
|
||||
from PySide6 import QtCore, __version__ as _pyside6_version
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
import os
|
||||
import math
|
||||
import pathlib
|
||||
import platform
|
||||
import functools
|
||||
|
||||
APP = functools.partial(QApplication.instance)
|
||||
|
||||
APPLICATION_NAME = 'Furious'
|
||||
APPLICATION_VERSION = __version__
|
||||
@@ -46,6 +42,7 @@ PYSIDE6_VERSION = _pyside6_version
|
||||
OS_CPU_COUNT = os.cpu_count()
|
||||
|
||||
PLATFORM = platform.system()
|
||||
PLATFORM_PYTHON_VERSION = platform.python_version()
|
||||
PLATFORM_RELEASE = platform.release()
|
||||
PLATFORM_MACHINE = platform.machine()
|
||||
|
||||
@@ -15,4 +15,36 @@
|
||||
# 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 .Ancestors import *
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = [
|
||||
'APP',
|
||||
'AppFontName',
|
||||
'AppThreadPool',
|
||||
'AppLoggerWindow',
|
||||
]
|
||||
|
||||
APP = functools.partial(QApplication.instance)
|
||||
|
||||
|
||||
def getAppAttributes(name: str):
|
||||
return getattr(APP(), name)
|
||||
|
||||
|
||||
(
|
||||
AppFontName,
|
||||
AppThreadPool,
|
||||
) = (
|
||||
functools.partial(getAppAttributes, 'customFontName'),
|
||||
functools.partial(getAppAttributes, 'threadPool'),
|
||||
)
|
||||
|
||||
|
||||
class AppLoggerWindow:
|
||||
Self = functools.partial(getAppAttributes, 'logViewerWindowSelf')
|
||||
Core = functools.partial(getAppAttributes, 'logViewerWindowCore')
|
||||
TUN_ = functools.partial(getAppAttributes, 'logViewerWindowTun_')
|
||||
@@ -0,0 +1,178 @@
|
||||
# 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 PySide6 import QtCore
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ['Mixins']
|
||||
|
||||
|
||||
class Mixins:
|
||||
class ConnectionAware:
|
||||
ObjectsPool = list()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
Mixins.ConnectionAware.ObjectsPool.append(self)
|
||||
|
||||
def disconnectedCallback(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def connectedCallback(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def callConnectedCallback():
|
||||
for ob in Mixins.ConnectionAware.ObjectsPool:
|
||||
assert isinstance(ob, Mixins.ConnectionAware)
|
||||
|
||||
ob.connectedCallback()
|
||||
|
||||
@staticmethod
|
||||
def callDisconnectedCallback():
|
||||
for ob in Mixins.ConnectionAware.ObjectsPool:
|
||||
assert isinstance(ob, Mixins.ConnectionAware)
|
||||
|
||||
ob.disconnectedCallback()
|
||||
|
||||
class ThemeAware:
|
||||
ObjectsPool = list()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
Mixins.ThemeAware.ObjectsPool.append(self)
|
||||
|
||||
def themeChangedCallback(self, theme: str):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def callThemeChangedCallbackUnchecked(theme: str):
|
||||
for ob in Mixins.ThemeAware.ObjectsPool:
|
||||
assert isinstance(ob, Mixins.ThemeAware)
|
||||
|
||||
ob.themeChangedCallback(theme)
|
||||
|
||||
@staticmethod
|
||||
def callThemeChangedCallback(theme: str):
|
||||
try:
|
||||
app = QApplication.instance()
|
||||
|
||||
if app is not None and app.isDarkModeEnabled():
|
||||
# Ignore application dark detect system
|
||||
logger.info(f'ignore system theme \'{theme}\' changes in dark mode')
|
||||
|
||||
return
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
pass
|
||||
|
||||
logger.info(f'system theme changed to \'{theme}\'')
|
||||
|
||||
Mixins.ThemeAware.callThemeChangedCallbackUnchecked(theme)
|
||||
|
||||
class CleanupOnExit:
|
||||
ObjectsPool = list()
|
||||
VisitedType = dict()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.uniqueCleanup = kwargs.pop('uniqueCleanup', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
Mixins.CleanupOnExit.ObjectsPool.append(self)
|
||||
|
||||
def cleanup(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def cleanupAll():
|
||||
for ob in Mixins.CleanupOnExit.ObjectsPool:
|
||||
assert isinstance(ob, Mixins.CleanupOnExit)
|
||||
|
||||
if ob.uniqueCleanup:
|
||||
obtype = str(type(ob))
|
||||
|
||||
if not Mixins.CleanupOnExit.VisitedType.get(obtype, False):
|
||||
ob.cleanup()
|
||||
|
||||
Mixins.CleanupOnExit.VisitedType[obtype] = True
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
ob.cleanup()
|
||||
|
||||
Mixins.CleanupOnExit.ObjectsPool.clear()
|
||||
Mixins.CleanupOnExit.VisitedType.clear()
|
||||
|
||||
class QSetDisabledContext:
|
||||
def __init__(self, qobject: QtCore.QObject):
|
||||
self.qobject = qobject
|
||||
|
||||
def __enter__(self):
|
||||
if hasattr(self.qobject, 'setDisabled'):
|
||||
self.qobject.setDisabled(True)
|
||||
|
||||
def __exit__(self, exceptionType, exceptionValue, tb):
|
||||
if hasattr(self.qobject, 'setDisabled'):
|
||||
self.qobject.setDisabled(False)
|
||||
|
||||
class QBlockSignalContext:
|
||||
def __init__(self, qobject: QtCore.QObject):
|
||||
self.qobject = qobject
|
||||
|
||||
def __enter__(self):
|
||||
if hasattr(self.qobject, 'blockSignals'):
|
||||
self.qobject.blockSignals(True)
|
||||
|
||||
def __exit__(self, exceptionType, exceptionValue, tb):
|
||||
if hasattr(self.qobject, 'blockSignals'):
|
||||
self.qobject.blockSignals(False)
|
||||
|
||||
class QTranslatable:
|
||||
ObjectsPool = list()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.translatable = kwargs.pop('translatable', True)
|
||||
self.useQSetDisabled = kwargs.pop('useQSetDisabled', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def retranslate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def retranslateAll():
|
||||
for ob in Mixins.QTranslatable.ObjectsPool:
|
||||
assert isinstance(ob, Mixins.QTranslatable)
|
||||
|
||||
if ob.translatable:
|
||||
if ob.useQSetDisabled:
|
||||
assert isinstance(ob, QtCore.QObject)
|
||||
|
||||
with Mixins.QSetDisabledContext(ob):
|
||||
ob.retranslate()
|
||||
else:
|
||||
ob.retranslate()
|
||||
@@ -0,0 +1,50 @@
|
||||
# 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 Furious.Frozenlib.Constants import *
|
||||
from Furious.Frozenlib.Globals import *
|
||||
from Furious.Frozenlib.Utility import *
|
||||
|
||||
import time
|
||||
|
||||
__all__ = ['PySide6Legacy']
|
||||
|
||||
|
||||
class PySide6Legacy:
|
||||
@staticmethod
|
||||
def enumValueWrapper(enum):
|
||||
# Protect PySide6 enum wrapper behavior changes
|
||||
if versionToValue(PYSIDE6_VERSION) < versionToValue('6.2.2'):
|
||||
return enum
|
||||
else:
|
||||
return enum.value
|
||||
|
||||
@staticmethod
|
||||
def eventLoopWait(ms):
|
||||
# Protect qWait method does not exist in some
|
||||
# old PySide6 version
|
||||
if versionToValue(PYSIDE6_VERSION) < versionToValue('6.3.1'):
|
||||
for counter in range(0, ms, 10):
|
||||
time.sleep(10 / 1000)
|
||||
|
||||
APP().processEvents()
|
||||
else:
|
||||
from PySide6.QtTest import QTest
|
||||
|
||||
QTest.qWait(ms)
|
||||
|
||||
APP().processEvents()
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Constants import *
|
||||
from Furious.Utility.Utility import runExternalCommand
|
||||
from Furious.Frozenlib.Constants import *
|
||||
from Furious.Frozenlib.Utility import *
|
||||
|
||||
import re
|
||||
import logging
|
||||
@@ -17,12 +17,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Constants import *
|
||||
from Furious.Frozenlib.Constants import *
|
||||
|
||||
from typing import AnyStr, Tuple
|
||||
|
||||
import os
|
||||
import ujson
|
||||
import enum
|
||||
import time
|
||||
import pathlib
|
||||
import operator
|
||||
import functools
|
||||
import ipaddress
|
||||
@@ -30,32 +32,19 @@ import subprocess
|
||||
import urllib.parse
|
||||
|
||||
__all__ = [
|
||||
'AppThreadPool',
|
||||
'Protocol',
|
||||
'BinarySettings',
|
||||
'callRateLimited',
|
||||
'callOnceOnly',
|
||||
'protocolRepr',
|
||||
'isValidIPAddress',
|
||||
'parseHostPort',
|
||||
'runExternalCommand',
|
||||
'getAbsolutePath',
|
||||
'absolutePath',
|
||||
'versionToValue',
|
||||
'getXrayProxyOutboundObject',
|
||||
'getXrayProxyOutboundStream',
|
||||
'loggerApp_',
|
||||
'loggerCore',
|
||||
'loggerTun_',
|
||||
]
|
||||
|
||||
|
||||
def _appThreadPool() -> QtCore.QThreadPool:
|
||||
return APP().threadPool
|
||||
|
||||
|
||||
AppThreadPool = functools.partial(_appThreadPool)
|
||||
|
||||
|
||||
class Protocol:
|
||||
class Protocol(enum.Enum):
|
||||
Unknown = 'Unknown'
|
||||
VMess = 'VMess'
|
||||
VLESS = 'VLESS'
|
||||
Shadowsocks = 'Shadowsocks'
|
||||
@@ -63,12 +52,53 @@ class Protocol:
|
||||
Hysteria1 = 'hysteria1'
|
||||
Hysteria2 = 'hysteria2'
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def toEnum(protocol: str):
|
||||
if not isinstance(protocol, str):
|
||||
return Protocol.Unknown
|
||||
|
||||
class BinarySettings:
|
||||
OFF = '0'
|
||||
ON_ = '1'
|
||||
if protocol.lower() == 'vmess':
|
||||
return Protocol.VMess
|
||||
|
||||
RANGE = [OFF, ON_]
|
||||
if protocol.lower() == 'vless':
|
||||
return Protocol.VLESS
|
||||
|
||||
if protocol.lower() == 'shadowsocks':
|
||||
return Protocol.Shadowsocks
|
||||
|
||||
if protocol.lower() == 'trojan':
|
||||
return Protocol.Trojan
|
||||
|
||||
return Protocol.Unknown
|
||||
|
||||
|
||||
def callRateLimited(maxCallPerSecond):
|
||||
"""
|
||||
Decorator function that limits the rate at which a function can be called.
|
||||
"""
|
||||
interval = 1.0 / float(maxCallPerSecond)
|
||||
called = time.monotonic()
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# Previously called
|
||||
nonlocal called
|
||||
|
||||
elapsed = time.monotonic() - called
|
||||
waitsec = interval - elapsed
|
||||
|
||||
if waitsec > 0:
|
||||
time.sleep(waitsec)
|
||||
|
||||
result = func(*args, **kwargs)
|
||||
called = time.monotonic()
|
||||
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def callOnceOnly(func):
|
||||
@@ -91,26 +121,6 @@ def callOnceOnly(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def protocolRepr(protocol: str) -> str:
|
||||
if not isinstance(protocol, str):
|
||||
return ''
|
||||
|
||||
if protocol.lower() == 'vmess':
|
||||
return Protocol.VMess
|
||||
|
||||
if protocol.lower() == 'vless':
|
||||
return Protocol.VLESS
|
||||
|
||||
if protocol.lower() == 'shadowsocks':
|
||||
return Protocol.Shadowsocks
|
||||
|
||||
if protocol.lower() == 'trojan':
|
||||
return Protocol.Trojan
|
||||
|
||||
return protocol
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def isValidIPAddress(address) -> bool:
|
||||
try:
|
||||
@@ -124,13 +134,24 @@ def isValidIPAddress(address) -> bool:
|
||||
|
||||
|
||||
# Can throw exceptions
|
||||
def parseHostPort(address) -> Tuple[AnyStr | None, str]:
|
||||
if address.count('//') == 0:
|
||||
@functools.lru_cache(None)
|
||||
def parseHostPort(address: str) -> Tuple[AnyStr | None, str | None]:
|
||||
if address.find('//') == -1:
|
||||
result = urllib.parse.urlsplit('//' + address)
|
||||
else:
|
||||
result = urllib.parse.urlsplit(address)
|
||||
|
||||
return result.hostname, str(result.port)
|
||||
if result.hostname is None:
|
||||
hostname = None
|
||||
else:
|
||||
hostname = result.hostname
|
||||
|
||||
if result.port is None:
|
||||
port = None
|
||||
else:
|
||||
port = str(result.port)
|
||||
|
||||
return hostname, port
|
||||
|
||||
|
||||
def runExternalCommand(*args, **kwargs):
|
||||
@@ -142,19 +163,18 @@ def runExternalCommand(*args, **kwargs):
|
||||
return subprocess.run(*args, **kwargs)
|
||||
|
||||
|
||||
def getAbsolutePath(path):
|
||||
return path if os.path.isabs(path) else str(ROOT_DIR / path)
|
||||
@functools.lru_cache(None)
|
||||
def absolutePath(path) -> pathlib.Path:
|
||||
return pathlib.Path(path) if os.path.isabs(path) else ROOT_DIR / path
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def versionToValue(version: str) -> int:
|
||||
def _split():
|
||||
# x.y or x.y.z or x.y.z.u
|
||||
def split():
|
||||
# x or x.y or x.y.z or x.y.z.u
|
||||
result = version.split('.')
|
||||
|
||||
if len(result) == 2:
|
||||
result.append('0')
|
||||
|
||||
if len(result) == 3:
|
||||
while len(result) < 4:
|
||||
result.append('0')
|
||||
|
||||
return result
|
||||
@@ -165,7 +185,7 @@ def versionToValue(version: str) -> int:
|
||||
u_weight = 1
|
||||
|
||||
try:
|
||||
x, y, z, u = _split()
|
||||
x, y, z, u = split()
|
||||
|
||||
return functools.reduce(
|
||||
operator.add,
|
||||
@@ -180,53 +200,3 @@ def versionToValue(version: str) -> int:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def getXrayProxyOutboundObject(config: dict) -> dict:
|
||||
if not isinstance(config.get('outbounds'), list):
|
||||
config['outbounds'] = []
|
||||
|
||||
for outbound in config['outbounds']:
|
||||
if isinstance(outbound, dict):
|
||||
tag = outbound.get('tag')
|
||||
|
||||
if isinstance(tag, str) and tag == 'proxy':
|
||||
return outbound
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def getXrayProxyOutboundStream(config: dict) -> dict:
|
||||
proxyOutboundObject = getXrayProxyOutboundObject(config)
|
||||
|
||||
if not isinstance(proxyOutboundObject.get('streamSettings'), dict):
|
||||
proxyOutboundObject['streamSettings'] = {}
|
||||
|
||||
return proxyOutboundObject['streamSettings']
|
||||
|
||||
|
||||
def loggerApp_():
|
||||
try:
|
||||
return APP().logViewerWindowApp_
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def loggerCore():
|
||||
try:
|
||||
return APP().logViewerWindowCore
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def loggerTun_():
|
||||
try:
|
||||
return APP().logViewerWindowTun_
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return None
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Constants import *
|
||||
from Furious.Frozenlib.Constants import *
|
||||
|
||||
import logging
|
||||
import threading
|
||||
@@ -15,6 +15,12 @@
|
||||
# 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 .UserServers import *
|
||||
from .UserSubs import *
|
||||
from .UserTUNSettings import *
|
||||
from .AppSettings import *
|
||||
from .Constants import *
|
||||
from .Globals import *
|
||||
from .Mixins import *
|
||||
from .PySide6Legacy import *
|
||||
from .SystemRoutingTable import *
|
||||
from .Tcping import *
|
||||
from .Utility import *
|
||||
from .Win32Session import *
|
||||
@@ -17,11 +17,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
__all__ = ['ApplicationFactory']
|
||||
|
||||
|
||||
class ApplicationFactory:
|
||||
class ExitCode:
|
||||
class ExitCode(Enum):
|
||||
ExitSuccess = 0
|
||||
UnknownException = 61
|
||||
PlatformNotSupported = 62
|
||||
@@ -19,19 +19,16 @@ from __future__ import annotations
|
||||
|
||||
from Furious.Interface.UserServersTableItem import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from abc import ABC
|
||||
from typing import Union
|
||||
|
||||
import copy
|
||||
import ujson
|
||||
import functools
|
||||
|
||||
__all__ = ['ConfigurationFactory']
|
||||
__all__ = ['ConfigFactory']
|
||||
|
||||
|
||||
class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
class ConfigFactory(UserServersTableItem, dict):
|
||||
"""
|
||||
ConfigurationFactory is how Furious sees the core config.
|
||||
|
||||
@@ -54,24 +51,31 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
# Extra attributes
|
||||
self.kwargs = kwargs
|
||||
|
||||
if isinstance(config, str):
|
||||
self._init_dispatch(config)
|
||||
|
||||
@functools.singledispatchmethod
|
||||
def _init_dispatch(self, config):
|
||||
super().__init__()
|
||||
|
||||
@_init_dispatch.register(str)
|
||||
def _(self, config):
|
||||
try:
|
||||
jsonObject = ujson.loads(config)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
try:
|
||||
jsonObject = ujson.loads(config)
|
||||
self.fromURI(config)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
try:
|
||||
self.fromURI(config)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
super().__init__()
|
||||
else:
|
||||
super().__init__(**jsonObject)
|
||||
elif isinstance(config, dict):
|
||||
super().__init__(**config)
|
||||
super().__init__()
|
||||
else:
|
||||
super().__init__()
|
||||
super().__init__(**jsonObject)
|
||||
|
||||
@_init_dispatch.register(dict)
|
||||
def _(self, config):
|
||||
super().__init__(**config)
|
||||
|
||||
def __getitem__(self, item: str):
|
||||
if not isinstance(item, str):
|
||||
@@ -85,7 +89,7 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
|
||||
return super().__setitem__(item, value)
|
||||
|
||||
def deepcopy(self) -> ConfigurationFactory:
|
||||
def deepcopy(self) -> ConfigFactory:
|
||||
return copy.deepcopy(self)
|
||||
|
||||
def coreName(self) -> str:
|
||||
@@ -126,20 +130,7 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
|
||||
@property
|
||||
def itemSubscription(self) -> str:
|
||||
try:
|
||||
app = QApplication.instance()
|
||||
|
||||
if app is None:
|
||||
return ''
|
||||
else:
|
||||
subsId = self.getExtras('subsId')
|
||||
subsOb = app.userSubs.data().get(subsId, {})
|
||||
|
||||
return subsOb.get('remark', '')
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
return ''
|
||||
|
||||
@property
|
||||
def itemLatency(self) -> str:
|
||||
@@ -203,7 +194,7 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
|
||||
return False
|
||||
|
||||
def httpProxyEndpoint(self) -> str:
|
||||
def httpProxy(self) -> str:
|
||||
"""
|
||||
Get current http proxy endpoint
|
||||
|
||||
@@ -212,7 +203,7 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
|
||||
return ''
|
||||
|
||||
def socksProxyEndpoint(self) -> str:
|
||||
def socksProxy(self) -> str:
|
||||
"""
|
||||
Get current socks proxy endpoint
|
||||
|
||||
@@ -221,7 +212,7 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
|
||||
return ''
|
||||
|
||||
def setHttpProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setHttpProxy(self, endpoint: str) -> bool:
|
||||
"""
|
||||
Set current http proxy endpoint
|
||||
|
||||
@@ -230,7 +221,7 @@ class ConfigurationFactory(UserServersTableItem, dict, ABC):
|
||||
|
||||
return False
|
||||
|
||||
def setSocksProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setSocksProxy(self, endpoint: str) -> bool:
|
||||
"""
|
||||
Set current socks proxy endpoint
|
||||
|
||||
@@ -17,18 +17,21 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Constants import PLATFORM
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface.ConfigFactory import *
|
||||
|
||||
from abc import ABC
|
||||
from abc import ABC, abstractmethod
|
||||
from enum import Enum
|
||||
from typing import Callable, Union
|
||||
|
||||
import ujson
|
||||
import functools
|
||||
|
||||
__all__ = ['CoreFactory']
|
||||
__all__ = ['CoreProcessFactory']
|
||||
|
||||
|
||||
class CoreFactory(ABC):
|
||||
class ExitCode:
|
||||
class CoreProcessFactory(ABC):
|
||||
class ExitCode(Enum):
|
||||
ConfigurationError = 23
|
||||
# Windows: 4294967295. Darwin, Linux: 255 (-1)
|
||||
ServerStartFailure = 4294967295 if PLATFORM == 'Windows' else 255
|
||||
@@ -36,62 +39,60 @@ class CoreFactory(ABC):
|
||||
SystemShuttingDown = 0x40010004
|
||||
|
||||
def __init__(
|
||||
self, exitCallback: Union[Callable[[CoreFactory, int], None], None] = None
|
||||
self,
|
||||
exitCallback: Union[Callable[[CoreProcessFactory, int], None], None] = None,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self._exitCallback = exitCallback
|
||||
self._jsonConfig = ''
|
||||
|
||||
def callExitCallback(self, exitcode: int):
|
||||
if callable(self._exitCallback):
|
||||
self._exitCallback(self, exitcode)
|
||||
|
||||
def registerCurrentJSONConfig(self, config: Union[str, dict]) -> CoreFactory:
|
||||
self._jsonConfig = config
|
||||
|
||||
return self
|
||||
|
||||
def configJSONString(self) -> str:
|
||||
if isinstance(self._jsonConfig, str):
|
||||
return self._jsonConfig
|
||||
if isinstance(self._jsonConfig, dict):
|
||||
try:
|
||||
return ujson.dumps(
|
||||
self._jsonConfig,
|
||||
ensure_ascii=False,
|
||||
escape_forward_slashes=False,
|
||||
)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
|
||||
@functools.singledispatchmethod
|
||||
def toJSONString(self, config, **kwargs) -> str:
|
||||
return ''
|
||||
|
||||
def configJSONObject(self) -> dict:
|
||||
if isinstance(self._jsonConfig, str):
|
||||
try:
|
||||
return ujson.loads(self._jsonConfig)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
@toJSONString.register(str)
|
||||
def _(self, config, **kwargs) -> str:
|
||||
return config
|
||||
|
||||
return {}
|
||||
if isinstance(self._jsonConfig, dict):
|
||||
return self._jsonConfig
|
||||
@toJSONString.register(ConfigFactory)
|
||||
def _(self, config, **kwargs) -> str:
|
||||
return config.toJSONString(**kwargs)
|
||||
|
||||
return {}
|
||||
@toJSONString.register(dict)
|
||||
def _(self, config, **kwargs) -> str:
|
||||
try:
|
||||
ensure_ascii = kwargs.pop('ensure_ascii', False)
|
||||
escape_forward_slashes = kwargs.pop('escape_forward_slashes', False)
|
||||
|
||||
return ujson.dumps(
|
||||
config,
|
||||
ensure_ascii=ensure_ascii,
|
||||
escape_forward_slashes=escape_forward_slashes,
|
||||
**kwargs,
|
||||
)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def name() -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def version() -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def start(self, *args, **kwargs) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def stop(self):
|
||||
raise NotImplementedError
|
||||
@@ -17,15 +17,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
__all__ = ['Encoder']
|
||||
__all__ = ['EncoderFactory']
|
||||
|
||||
|
||||
class Encoder(ABC):
|
||||
class EncoderFactory(ABC):
|
||||
@abstractmethod
|
||||
def encode(self, data: Any, **kwargs) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def decode(self, data: Any, **kwargs) -> Any:
|
||||
raise NotImplementedError
|
||||
@@ -17,15 +17,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
__all__ = ['StorageFactory']
|
||||
|
||||
|
||||
class StorageFactory(ABC):
|
||||
@abstractmethod
|
||||
def sync(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def data(self) -> Any:
|
||||
raise NotImplementedError
|
||||
@@ -17,12 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
|
||||
__all__ = ['UserServersTableItem']
|
||||
|
||||
|
||||
class UserServersTableItem(ABC):
|
||||
class UserServersTableItem:
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
# 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 .Encoder import *
|
||||
from .ApplicationFactory import *
|
||||
from .EncoderFactory import *
|
||||
from .GuiEditorItemFactory import *
|
||||
from .StorageFactory import *
|
||||
from .UserServersTableItem import *
|
||||
from .Storage import *
|
||||
from .Application import *
|
||||
from .ConfigurationFactory import *
|
||||
from .CoreFactory import *
|
||||
from .ConfigFactory import *
|
||||
from .CoreProcessFactory import *
|
||||
|
||||
+431
-149
@@ -17,12 +17,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Library.Encoder import *
|
||||
from Furious.Library.Storage import *
|
||||
|
||||
from typing import Union, Tuple
|
||||
|
||||
import copy
|
||||
import functools
|
||||
import urllib.parse
|
||||
|
||||
@@ -32,17 +34,21 @@ parse_qsl = functools.partial(urllib.parse.parse_qsl)
|
||||
urlparse = functools.partial(urllib.parse.urlparse)
|
||||
urlunparse = functools.partial(urllib.parse.urlunparse)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'ConfigurationXray',
|
||||
'ConfigurationHysteria1',
|
||||
'ConfigurationHysteria2',
|
||||
'constructFromDict',
|
||||
'constructFromAny',
|
||||
'BLANK_CONFIG_XRAY',
|
||||
'BLANK_CONFIG_HYSTERIA1',
|
||||
'BLANK_CONFIG_HYSTERIA2',
|
||||
'ConfigXray',
|
||||
'ConfigHysteria1',
|
||||
'ConfigHysteria2',
|
||||
'configXrayEmptyProxyOutboundObject',
|
||||
'configFactoryFromDict',
|
||||
'configFactoryFromAny',
|
||||
'configFactoryBlank',
|
||||
]
|
||||
|
||||
|
||||
class ConfigurationXrayProxyOutboundObjectV(dict):
|
||||
class ConfigXrayProxyOutboundObjectV(dict):
|
||||
def __init__(
|
||||
self,
|
||||
protocol,
|
||||
@@ -62,7 +68,7 @@ class ConfigurationXrayProxyOutboundObjectV(dict):
|
||||
networkKey = f'{type_}Settings'
|
||||
|
||||
networkObjectArgs[networkKey] = (
|
||||
ConfigurationXray.kwargs2ProxyStreamSettingsNetworkObject(
|
||||
ConfigXray.kwargs2ProxyStreamSettingsNetworkObject(
|
||||
type_, remote_host, kwargs
|
||||
)
|
||||
)
|
||||
@@ -74,7 +80,7 @@ class ConfigurationXrayProxyOutboundObjectV(dict):
|
||||
TLSObjectArgs[
|
||||
# tlsSettings, realitySettings
|
||||
f'{security}Settings'
|
||||
] = ConfigurationXray.kwargs2ProxyStreamSettingsTLSObject(
|
||||
] = ConfigXray.kwargs2ProxyStreamSettingsTLSObject(
|
||||
protocol, remote_host, security, kwargs
|
||||
)
|
||||
|
||||
@@ -88,7 +94,7 @@ class ConfigurationXrayProxyOutboundObjectV(dict):
|
||||
'address': remote_host,
|
||||
'port': int(remote_port),
|
||||
'users': [
|
||||
ConfigurationXray.kwargs2ProxyUserObject(
|
||||
ConfigXray.kwargs2ProxyUserObject(
|
||||
protocol, uuid_, encryption, kwargs
|
||||
),
|
||||
],
|
||||
@@ -109,7 +115,7 @@ class ConfigurationXrayProxyOutboundObjectV(dict):
|
||||
)
|
||||
|
||||
|
||||
class ConfigurationXrayProxyOutboundObjectSS(dict):
|
||||
class ConfigXrayProxyOutboundObjectSS(dict):
|
||||
def __init__(self, method, password, address, port):
|
||||
super().__init__(
|
||||
**{
|
||||
@@ -138,7 +144,7 @@ class ConfigurationXrayProxyOutboundObjectSS(dict):
|
||||
)
|
||||
|
||||
|
||||
class ConfigurationXrayProxyOutboundObjectTrojan(dict):
|
||||
class ConfigXrayProxyOutboundObjectTrojan(dict):
|
||||
def __init__(self, password, address, port, type_, security, **kwargs):
|
||||
networkObjectArgs, securityArgs, TLSObjectArgs = {}, {}, {}
|
||||
|
||||
@@ -148,9 +154,7 @@ class ConfigurationXrayProxyOutboundObjectTrojan(dict):
|
||||
networkKey = f'{type_}Settings'
|
||||
|
||||
networkObjectArgs[networkKey] = (
|
||||
ConfigurationXray.kwargs2ProxyStreamSettingsNetworkObject(
|
||||
type_, address, kwargs
|
||||
)
|
||||
ConfigXray.kwargs2ProxyStreamSettingsNetworkObject(type_, address, kwargs)
|
||||
)
|
||||
|
||||
if security:
|
||||
@@ -160,7 +164,7 @@ class ConfigurationXrayProxyOutboundObjectTrojan(dict):
|
||||
TLSObjectArgs[
|
||||
# tlsSettings, realitySettings
|
||||
f'{security}Settings'
|
||||
] = ConfigurationXray.kwargs2ProxyStreamSettingsTLSObject(
|
||||
] = ConfigXray.kwargs2ProxyStreamSettingsTLSObject(
|
||||
'trojan', address, security, kwargs
|
||||
)
|
||||
|
||||
@@ -192,21 +196,169 @@ class ConfigurationXrayProxyOutboundObjectTrojan(dict):
|
||||
)
|
||||
|
||||
|
||||
class ConfigurationXray(ConfigurationFactory):
|
||||
BLANK_CONFIG_XRAY = {
|
||||
# log
|
||||
'log': {
|
||||
'access': '',
|
||||
'error': '',
|
||||
'loglevel': 'warning',
|
||||
},
|
||||
# inbounds
|
||||
'inbounds': [
|
||||
{
|
||||
'tag': 'socks',
|
||||
'port': 10808,
|
||||
'listen': '127.0.0.1',
|
||||
'protocol': 'socks',
|
||||
'sniffing': {
|
||||
'enabled': True,
|
||||
'destOverride': [
|
||||
'http',
|
||||
'tls',
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'auth': 'noauth',
|
||||
'udp': True,
|
||||
'allowTransparent': False,
|
||||
},
|
||||
},
|
||||
{
|
||||
'tag': 'http',
|
||||
'port': 10809,
|
||||
'listen': '127.0.0.1',
|
||||
'protocol': 'http',
|
||||
'sniffing': {
|
||||
'enabled': True,
|
||||
'destOverride': [
|
||||
'http',
|
||||
'tls',
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'auth': 'noauth',
|
||||
'udp': True,
|
||||
'allowTransparent': False,
|
||||
},
|
||||
},
|
||||
],
|
||||
# outbounds
|
||||
'outbounds': [
|
||||
# proxy
|
||||
{
|
||||
'tag': 'proxy',
|
||||
'protocol': '',
|
||||
'settings': {},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
},
|
||||
# direct
|
||||
{
|
||||
'tag': 'direct',
|
||||
'protocol': 'freedom',
|
||||
'settings': {},
|
||||
},
|
||||
# block
|
||||
{
|
||||
'tag': 'block',
|
||||
'protocol': 'blackhole',
|
||||
'settings': {
|
||||
'response': {
|
||||
'type': 'http',
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
# routing
|
||||
'routing': {},
|
||||
}
|
||||
|
||||
|
||||
class ConfigXray(ConfigFactory):
|
||||
def __init__(self, config: Union[str, dict] = '', **kwargs):
|
||||
super().__init__(config, **kwargs)
|
||||
|
||||
def coreName(self):
|
||||
return 'Xray-core'
|
||||
|
||||
@staticmethod
|
||||
def getProxyOutboundObject(config: dict, default=None) -> dict:
|
||||
if not isinstance(config.get('outbounds'), list):
|
||||
config['outbounds'] = []
|
||||
|
||||
for outbound in config['outbounds']:
|
||||
if isinstance(outbound, dict):
|
||||
tag = outbound.get('tag')
|
||||
|
||||
if isinstance(tag, str) and tag == 'proxy':
|
||||
return outbound
|
||||
|
||||
if isinstance(default, dict):
|
||||
# Proxy outbound not found. Add it
|
||||
config['outbounds'].append(default)
|
||||
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def getProxyOutboundStream(config: dict, **kwargs) -> dict:
|
||||
proxyOutbound = ConfigXray.getProxyOutboundObject(config, **kwargs)
|
||||
|
||||
if not isinstance(proxyOutbound.get('streamSettings'), dict):
|
||||
proxyOutbound['streamSettings'] = {}
|
||||
|
||||
return proxyOutbound['streamSettings']
|
||||
|
||||
@staticmethod
|
||||
def getProxyOutboundServer(config: dict, protocol: Protocol, **kwargs) -> dict:
|
||||
value, proxyOutbound = (
|
||||
protocol.value.lower(),
|
||||
ConfigXray.getProxyOutboundObject(config, **kwargs),
|
||||
)
|
||||
|
||||
try:
|
||||
if value == 'vless' or value == 'vmess':
|
||||
server = proxyOutbound['settings']['vnext'][0]
|
||||
elif value == 'shadowsocks' or value == 'trojan':
|
||||
server = proxyOutbound['settings']['servers'][0]
|
||||
else:
|
||||
server = {}
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
server = {}
|
||||
|
||||
if not isinstance(server, dict):
|
||||
server = {}
|
||||
|
||||
return server
|
||||
|
||||
@staticmethod
|
||||
def getProxyOutboundUser(config: dict, protocol: Protocol, **kwargs) -> dict:
|
||||
proxyOutboundServer = ConfigXray.getProxyOutboundServer(
|
||||
config, protocol, **kwargs
|
||||
)
|
||||
|
||||
try:
|
||||
user = proxyOutboundServer['users'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
user = {}
|
||||
|
||||
if not isinstance(user, dict):
|
||||
user = {}
|
||||
|
||||
return user
|
||||
|
||||
@property
|
||||
def proxyOutboundObject(self) -> dict:
|
||||
try:
|
||||
for outboundObject in self['outbounds']:
|
||||
if outboundObject['tag'] == 'proxy':
|
||||
return outboundObject
|
||||
|
||||
return {}
|
||||
return ConfigXray.getProxyOutboundObject(self)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -224,16 +376,12 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
@property
|
||||
def proxyServerObject(self) -> dict:
|
||||
try:
|
||||
if (
|
||||
self.proxyProtocol.lower() == 'vmess'
|
||||
or self.proxyProtocol.lower() == 'vless'
|
||||
):
|
||||
proxyProtocol = self.proxyProtocol.lower()
|
||||
|
||||
if proxyProtocol == 'vmess' or proxyProtocol == 'vless':
|
||||
return self.proxyOutboundObject['settings']['vnext'][0]
|
||||
|
||||
if (
|
||||
self.proxyProtocol.lower() == 'shadowsocks'
|
||||
or self.proxyProtocol.lower() == 'trojan'
|
||||
):
|
||||
if proxyProtocol == 'shadowsocks' or proxyProtocol == 'trojan':
|
||||
return self.proxyOutboundObject['settings']['servers'][0]
|
||||
|
||||
return {}
|
||||
@@ -254,7 +402,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
@property
|
||||
def proxyStreamSettingsObject(self) -> dict:
|
||||
try:
|
||||
return self.proxyOutboundObject['streamSettings']
|
||||
return ConfigXray.getProxyOutboundStream(self)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -732,6 +880,9 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return xhttpObject
|
||||
|
||||
else:
|
||||
return {}
|
||||
|
||||
@property
|
||||
def kwargsFromProxyStreamSettingsTLSObject(self) -> dict:
|
||||
kwargs = {}
|
||||
@@ -864,7 +1015,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ConfigurationXray.URI2ProxyOutboundObjectVLESS(URI, protocol='vmess')
|
||||
return ConfigXray.URI2ProxyOutboundObjectVLESS(URI, protocol='vmess')
|
||||
else:
|
||||
|
||||
def getOrDefault(key, default=''):
|
||||
@@ -875,7 +1026,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
return (
|
||||
remark,
|
||||
# Ignore: v
|
||||
ConfigurationXrayProxyOutboundObjectV(
|
||||
ConfigXrayProxyOutboundObjectV(
|
||||
'vmess',
|
||||
getOrDefault('add'),
|
||||
int(getOrDefault('port', '0')),
|
||||
@@ -916,7 +1067,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return (
|
||||
remark,
|
||||
ConfigurationXrayProxyOutboundObjectV(
|
||||
ConfigXrayProxyOutboundObjectV(
|
||||
kwargs.pop('protocol', 'vless'),
|
||||
remote_host,
|
||||
int(remote_port),
|
||||
@@ -982,7 +1133,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return (
|
||||
remark,
|
||||
ConfigurationXrayProxyOutboundObjectSS(*getSSParams()),
|
||||
ConfigXrayProxyOutboundObjectSS(*getSSParams()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1001,7 +1152,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return (
|
||||
remark,
|
||||
ConfigurationXrayProxyOutboundObjectTrojan(
|
||||
ConfigXrayProxyOutboundObjectTrojan(
|
||||
password, address, port, type_, security, **queryObject
|
||||
),
|
||||
)
|
||||
@@ -1009,16 +1160,16 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
@staticmethod
|
||||
def URI2ProxyOutboundObject(URI: str) -> Tuple[str, dict]:
|
||||
if URI.startswith('vmess://'):
|
||||
return ConfigurationXray.URI2ProxyOutboundObjectVMess(URI)
|
||||
return ConfigXray.URI2ProxyOutboundObjectVMess(URI)
|
||||
|
||||
if URI.startswith('vless://'):
|
||||
return ConfigurationXray.URI2ProxyOutboundObjectVLESS(URI)
|
||||
return ConfigXray.URI2ProxyOutboundObjectVLESS(URI)
|
||||
|
||||
if URI.startswith('ss://'):
|
||||
return ConfigurationXray.URI2ProxyOutboundObjectSS(URI)
|
||||
return ConfigXray.URI2ProxyOutboundObjectSS(URI)
|
||||
|
||||
if URI.startswith('trojan://'):
|
||||
return ConfigurationXray.URI2ProxyOutboundObjectTrojan(URI)
|
||||
return ConfigXray.URI2ProxyOutboundObjectTrojan(URI)
|
||||
|
||||
raise ValueError(f'Unrecognized URI scheme {URI}')
|
||||
|
||||
@@ -1026,9 +1177,21 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
def itemRemark(self) -> str:
|
||||
return self.getExtras('remark')
|
||||
|
||||
@property
|
||||
def itemSubscription(self) -> str:
|
||||
try:
|
||||
subsId = self.getExtras('subsId')
|
||||
subsOb = Storage.UserSubs().get(subsId, {})
|
||||
|
||||
return subsOb.get('remark', '')
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
|
||||
@property
|
||||
def itemProtocol(self) -> str:
|
||||
return protocolRepr(self.proxyProtocol)
|
||||
return Protocol.toEnum(self.proxyProtocol).value
|
||||
|
||||
@property
|
||||
def itemAddress(self) -> str:
|
||||
@@ -1071,7 +1234,9 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
else:
|
||||
override = remark
|
||||
|
||||
if self.proxyProtocol.lower() == 'vmess':
|
||||
proxyProtocol = self.proxyProtocol.lower()
|
||||
|
||||
if proxyProtocol == 'vmess':
|
||||
netloc = PyBase64Encoder.encode(
|
||||
UJSONEncoder.encode(
|
||||
{
|
||||
@@ -1103,7 +1268,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return urlunparse(['vmess', netloc, '', '', {}, ''])
|
||||
|
||||
if self.proxyProtocol.lower() == 'vless':
|
||||
if proxyProtocol == 'vless':
|
||||
flowArg = {}
|
||||
|
||||
if self.proxyUserObject.get('flow'):
|
||||
@@ -1132,7 +1297,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return urlunparse(['vless', netloc, '', '', query, quote(override)])
|
||||
|
||||
if self.proxyProtocol.lower() == 'shadowsocks':
|
||||
if proxyProtocol == 'shadowsocks':
|
||||
method, password, address, port = list(
|
||||
self.proxyServerObject[value]
|
||||
for value in ['method', 'password', 'address', 'port']
|
||||
@@ -1142,7 +1307,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return urlunparse(['ss', netloc, '', '', '', quote(override)])
|
||||
|
||||
if self.proxyProtocol.lower() == 'trojan':
|
||||
if proxyProtocol == 'trojan':
|
||||
password, address, port = list(
|
||||
self.proxyServerObject[value]
|
||||
for value in ['password', 'address', 'port']
|
||||
@@ -1168,81 +1333,31 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
def fromURI(self, URI: str) -> bool:
|
||||
try:
|
||||
remark, proxyOutboundObject = ConfigurationXray.URI2ProxyOutboundObject(URI)
|
||||
remark, proxyOutboundObject = ConfigXray.URI2ProxyOutboundObject(URI)
|
||||
|
||||
dict.__init__(
|
||||
self,
|
||||
**{
|
||||
# log
|
||||
'log': {
|
||||
'access': '',
|
||||
'error': '',
|
||||
'loglevel': 'warning',
|
||||
},
|
||||
# inbounds
|
||||
'inbounds': [
|
||||
{
|
||||
'tag': 'socks',
|
||||
'port': 10808,
|
||||
'listen': '127.0.0.1',
|
||||
'protocol': 'socks',
|
||||
'sniffing': {
|
||||
'enabled': True,
|
||||
'destOverride': [
|
||||
'http',
|
||||
'tls',
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'auth': 'noauth',
|
||||
'udp': True,
|
||||
'allowTransparent': False,
|
||||
},
|
||||
},
|
||||
{
|
||||
'tag': 'http',
|
||||
'port': 10809,
|
||||
'listen': '127.0.0.1',
|
||||
'protocol': 'http',
|
||||
'sniffing': {
|
||||
'enabled': True,
|
||||
'destOverride': [
|
||||
'http',
|
||||
'tls',
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'auth': 'noauth',
|
||||
'udp': True,
|
||||
'allowTransparent': False,
|
||||
},
|
||||
},
|
||||
],
|
||||
# outbounds
|
||||
'outbounds': [
|
||||
# proxy
|
||||
proxyOutboundObject,
|
||||
# direct
|
||||
{
|
||||
'tag': 'direct',
|
||||
'protocol': 'freedom',
|
||||
'settings': {},
|
||||
},
|
||||
# block
|
||||
{
|
||||
'tag': 'block',
|
||||
'protocol': 'blackhole',
|
||||
'settings': {
|
||||
'response': {
|
||||
'type': 'http',
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
# routing
|
||||
'routing': {},
|
||||
factory = copy.deepcopy(BLANK_CONFIG_XRAY)
|
||||
factory['outbounds'] = [
|
||||
# proxy
|
||||
proxyOutboundObject,
|
||||
# direct
|
||||
{
|
||||
'tag': 'direct',
|
||||
'protocol': 'freedom',
|
||||
'settings': {},
|
||||
},
|
||||
)
|
||||
# block
|
||||
{
|
||||
'tag': 'block',
|
||||
'protocol': 'blackhole',
|
||||
'settings': {
|
||||
'response': {
|
||||
'type': 'http',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
dict.__init__(self, **factory)
|
||||
|
||||
self.setExtras('remark', remark)
|
||||
|
||||
@@ -1252,7 +1367,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return False
|
||||
|
||||
def httpProxyEndpoint(self) -> str:
|
||||
def httpProxy(self) -> str:
|
||||
try:
|
||||
for inbound in self['inbounds']:
|
||||
if inbound['protocol'] == 'http':
|
||||
@@ -1266,7 +1381,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return ''
|
||||
|
||||
def socksProxyEndpoint(self) -> str:
|
||||
def socksProxy(self) -> str:
|
||||
try:
|
||||
for inbound in self['inbounds']:
|
||||
if inbound['protocol'] == 'socks':
|
||||
@@ -1280,7 +1395,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return ''
|
||||
|
||||
def setHttpProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setHttpProxy(self, endpoint: str) -> bool:
|
||||
try:
|
||||
if self.get('inbounds') is None:
|
||||
self['inbounds'] = []
|
||||
@@ -1337,7 +1452,7 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
|
||||
return False
|
||||
|
||||
def setSocksProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setSocksProxy(self, endpoint: str) -> bool:
|
||||
try:
|
||||
if self.get('inbounds') is None:
|
||||
self['inbounds'] = []
|
||||
@@ -1395,7 +1510,19 @@ class ConfigurationXray(ConfigurationFactory):
|
||||
return False
|
||||
|
||||
|
||||
class ConfigurationHysteria1(ConfigurationFactory):
|
||||
BLANK_CONFIG_HYSTERIA1 = {
|
||||
'server': '',
|
||||
'protocol': 'udp',
|
||||
'socks5': {
|
||||
'listen': '127.0.0.1:10808',
|
||||
},
|
||||
'http': {
|
||||
'listen': '127.0.0.1:10809',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ConfigHysteria1(ConfigFactory):
|
||||
def __init__(self, config: Union[str, dict] = '', **kwargs):
|
||||
super().__init__(config, **kwargs)
|
||||
|
||||
@@ -1406,9 +1533,21 @@ class ConfigurationHysteria1(ConfigurationFactory):
|
||||
def itemRemark(self) -> str:
|
||||
return self.getExtras('remark')
|
||||
|
||||
@property
|
||||
def itemSubscription(self) -> str:
|
||||
try:
|
||||
subsId = self.getExtras('subsId')
|
||||
subsOb = Storage.UserSubs().get(subsId, {})
|
||||
|
||||
return subsOb.get('remark', '')
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
|
||||
@property
|
||||
def itemProtocol(self) -> str:
|
||||
return Protocol.Hysteria1
|
||||
return Protocol.Hysteria1.value
|
||||
|
||||
@property
|
||||
def itemAddress(self) -> str:
|
||||
@@ -1601,7 +1740,7 @@ class ConfigurationHysteria1(ConfigurationFactory):
|
||||
|
||||
return False
|
||||
|
||||
def httpProxyEndpoint(self) -> str:
|
||||
def httpProxy(self) -> str:
|
||||
try:
|
||||
return self['http']['listen']
|
||||
except Exception:
|
||||
@@ -1609,7 +1748,7 @@ class ConfigurationHysteria1(ConfigurationFactory):
|
||||
|
||||
return ''
|
||||
|
||||
def socksProxyEndpoint(self) -> str:
|
||||
def socksProxy(self) -> str:
|
||||
try:
|
||||
return self['socks5']['listen']
|
||||
except Exception:
|
||||
@@ -1617,7 +1756,7 @@ class ConfigurationHysteria1(ConfigurationFactory):
|
||||
|
||||
return ''
|
||||
|
||||
def setHttpProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setHttpProxy(self, endpoint: str) -> bool:
|
||||
try:
|
||||
if endpoint == '':
|
||||
self.pop('http', None)
|
||||
@@ -1635,7 +1774,7 @@ class ConfigurationHysteria1(ConfigurationFactory):
|
||||
|
||||
return False
|
||||
|
||||
def setSocksProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setSocksProxy(self, endpoint: str) -> bool:
|
||||
try:
|
||||
if endpoint == '':
|
||||
self.pop('socks5', None)
|
||||
@@ -1654,7 +1793,21 @@ class ConfigurationHysteria1(ConfigurationFactory):
|
||||
return False
|
||||
|
||||
|
||||
class ConfigurationHysteria2(ConfigurationFactory):
|
||||
BLANK_CONFIG_HYSTERIA2 = {
|
||||
'server': '',
|
||||
'tls': {
|
||||
'insecure': False,
|
||||
},
|
||||
'socks5': {
|
||||
'listen': '127.0.0.1:10808',
|
||||
},
|
||||
'http': {
|
||||
'listen': '127.0.0.1:10809',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ConfigHysteria2(ConfigFactory):
|
||||
def __init__(self, config: Union[str, dict] = '', **kwargs):
|
||||
super().__init__(config, **kwargs)
|
||||
|
||||
@@ -1665,9 +1818,21 @@ class ConfigurationHysteria2(ConfigurationFactory):
|
||||
def itemRemark(self) -> str:
|
||||
return self.getExtras('remark')
|
||||
|
||||
@property
|
||||
def itemSubscription(self) -> str:
|
||||
try:
|
||||
subsId = self.getExtras('subsId')
|
||||
subsOb = Storage.UserSubs().get(subsId, {})
|
||||
|
||||
return subsOb.get('remark', '')
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
|
||||
@property
|
||||
def itemProtocol(self) -> str:
|
||||
return Protocol.Hysteria2
|
||||
return Protocol.Hysteria2.value
|
||||
|
||||
@property
|
||||
def itemAddress(self) -> str:
|
||||
@@ -1818,7 +1983,7 @@ class ConfigurationHysteria2(ConfigurationFactory):
|
||||
|
||||
return False
|
||||
|
||||
def httpProxyEndpoint(self) -> str:
|
||||
def httpProxy(self) -> str:
|
||||
try:
|
||||
return self['http']['listen']
|
||||
except Exception:
|
||||
@@ -1826,7 +1991,7 @@ class ConfigurationHysteria2(ConfigurationFactory):
|
||||
|
||||
return ''
|
||||
|
||||
def socksProxyEndpoint(self) -> str:
|
||||
def socksProxy(self) -> str:
|
||||
try:
|
||||
return self['socks5']['listen']
|
||||
except Exception:
|
||||
@@ -1834,7 +1999,7 @@ class ConfigurationHysteria2(ConfigurationFactory):
|
||||
|
||||
return ''
|
||||
|
||||
def setHttpProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setHttpProxy(self, endpoint: str) -> bool:
|
||||
try:
|
||||
if endpoint == '':
|
||||
self.pop('http', None)
|
||||
@@ -1852,7 +2017,7 @@ class ConfigurationHysteria2(ConfigurationFactory):
|
||||
|
||||
return False
|
||||
|
||||
def setSocksProxyEndpoint(self, endpoint: str) -> bool:
|
||||
def setSocksProxy(self, endpoint: str) -> bool:
|
||||
try:
|
||||
if endpoint == '':
|
||||
self.pop('socks5', None)
|
||||
@@ -1871,16 +2036,94 @@ class ConfigurationHysteria2(ConfigurationFactory):
|
||||
return False
|
||||
|
||||
|
||||
def constructFromDict(config: dict, **kwargs) -> ConfigurationFactory:
|
||||
def configXrayEmptyProxyOutboundObject(protocol: Protocol) -> dict:
|
||||
value = protocol.value.lower()
|
||||
|
||||
if value == 'vless' or value == 'vmess':
|
||||
return {
|
||||
'tag': 'proxy',
|
||||
'protocol': protocol,
|
||||
'settings': {
|
||||
'vnext': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'users': [
|
||||
{
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
elif value == 'shadowsocks':
|
||||
return {
|
||||
'tag': 'proxy',
|
||||
'protocol': 'shadowsocks',
|
||||
'settings': {
|
||||
'servers': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'method': '',
|
||||
'password': '',
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
'ota': False,
|
||||
}
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
elif value == 'trojan':
|
||||
return {
|
||||
'tag': 'proxy',
|
||||
'protocol': 'trojan',
|
||||
'settings': {
|
||||
'servers': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'password': '',
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
}
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def configFactoryFromDict(config: dict, **kwargs) -> ConfigFactory:
|
||||
if not isinstance(config, dict):
|
||||
return ConfigurationFactory()
|
||||
return ConfigFactory()
|
||||
|
||||
def hasField(field):
|
||||
return config.get(field) is not None
|
||||
|
||||
if hasField('inbounds') or hasField('outbounds'):
|
||||
# Assuming is Xray-Core
|
||||
return ConfigurationXray(config, **kwargs)
|
||||
return ConfigXray(config, **kwargs)
|
||||
|
||||
if hasField('server'):
|
||||
if (
|
||||
@@ -1897,7 +2140,7 @@ def constructFromDict(config: dict, **kwargs) -> ConfigurationFactory:
|
||||
or hasField('fast_open')
|
||||
or hasField('lazy_start')
|
||||
):
|
||||
return ConfigurationHysteria1(config, **kwargs)
|
||||
return ConfigHysteria1(config, **kwargs)
|
||||
if (
|
||||
hasField('tls')
|
||||
or hasField('transport')
|
||||
@@ -1911,13 +2154,13 @@ def constructFromDict(config: dict, **kwargs) -> ConfigurationFactory:
|
||||
or hasField('fastOpen')
|
||||
or hasField('lazy')
|
||||
):
|
||||
return ConfigurationHysteria2(config, **kwargs)
|
||||
return ConfigHysteria2(config, **kwargs)
|
||||
|
||||
# Copied to factory unrecognized
|
||||
return ConfigurationFactory(config, **kwargs)
|
||||
return ConfigFactory(config, **kwargs)
|
||||
|
||||
|
||||
def constructFromAny(config: Union[str, dict], **kwargs) -> ConfigurationFactory:
|
||||
def configFactoryFromAny(config: Union[str, dict], **kwargs) -> ConfigFactory:
|
||||
if isinstance(config, str):
|
||||
if (
|
||||
config.startswith('vmess://')
|
||||
@@ -1925,21 +2168,60 @@ def constructFromAny(config: Union[str, dict], **kwargs) -> ConfigurationFactory
|
||||
or config.startswith('ss://')
|
||||
or config.startswith('trojan://')
|
||||
):
|
||||
return ConfigurationXray(config, **kwargs)
|
||||
return ConfigXray(config, **kwargs)
|
||||
if config.startswith('hysteria://'):
|
||||
return ConfigurationHysteria1(config, **kwargs)
|
||||
return ConfigHysteria1(config, **kwargs)
|
||||
if config.startswith('hy2://') or config.startswith('hysteria2://'):
|
||||
return ConfigurationHysteria2(config, **kwargs)
|
||||
return ConfigHysteria2(config, **kwargs)
|
||||
|
||||
try:
|
||||
# Try to construct from JSON string
|
||||
return constructFromDict(UJSONEncoder.decode(config), **kwargs)
|
||||
return configFactoryFromDict(UJSONEncoder.decode(config), **kwargs)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ConfigurationFactory(**kwargs)
|
||||
return ConfigFactory(**kwargs)
|
||||
|
||||
if isinstance(config, dict):
|
||||
return constructFromDict(config, **kwargs)
|
||||
return configFactoryFromDict(config, **kwargs)
|
||||
|
||||
return ConfigurationFactory(**kwargs)
|
||||
return ConfigFactory(**kwargs)
|
||||
|
||||
|
||||
def configFactoryBlank(protocol: Protocol) -> ConfigFactory:
|
||||
if protocol == Protocol.VMess or protocol == Protocol.VLESS:
|
||||
factory = configFactoryFromDict(BLANK_CONFIG_XRAY)
|
||||
factory['outbounds'][0]['protocol'] = protocol.value.lower()
|
||||
factory['outbounds'][0]['settings']['vnext'] = [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'users': [{'email': PROXY_OUTBOUND_USER_EMAIL}],
|
||||
},
|
||||
]
|
||||
|
||||
return factory
|
||||
|
||||
if protocol == Protocol.Shadowsocks or protocol == Protocol.Trojan:
|
||||
factory = configFactoryFromDict(BLANK_CONFIG_XRAY)
|
||||
factory['outbounds'][0]['protocol'] = protocol.value.lower()
|
||||
factory['outbounds'][0]['settings']['servers'] = [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
},
|
||||
]
|
||||
|
||||
return factory
|
||||
|
||||
if protocol == Protocol.Hysteria1:
|
||||
factory = configFactoryFromDict(BLANK_CONFIG_HYSTERIA1)
|
||||
|
||||
return factory
|
||||
if protocol == Protocol.Hysteria2:
|
||||
factory = configFactoryFromDict(BLANK_CONFIG_HYSTERIA2)
|
||||
|
||||
return factory
|
||||
|
||||
return ConfigFactory()
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
# 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.Interface import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Library.Configuration import *
|
||||
|
||||
__all__ = ['getEmptyFactory']
|
||||
|
||||
RAY_TEMPLATE = constructFromDict(
|
||||
{
|
||||
'log': {
|
||||
'access': '',
|
||||
'error': '',
|
||||
'loglevel': 'warning',
|
||||
},
|
||||
'inbounds': [
|
||||
{
|
||||
'tag': 'socks',
|
||||
'port': 10808,
|
||||
'listen': '127.0.0.1',
|
||||
'protocol': 'socks',
|
||||
'sniffing': {
|
||||
'enabled': True,
|
||||
'destOverride': [
|
||||
'http',
|
||||
'tls',
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'auth': 'noauth',
|
||||
'udp': True,
|
||||
'allowTransparent': False,
|
||||
},
|
||||
},
|
||||
{
|
||||
'tag': 'http',
|
||||
'port': 10809,
|
||||
'listen': '127.0.0.1',
|
||||
'protocol': 'http',
|
||||
'sniffing': {
|
||||
'enabled': True,
|
||||
'destOverride': [
|
||||
'http',
|
||||
'tls',
|
||||
],
|
||||
},
|
||||
'settings': {
|
||||
'auth': 'noauth',
|
||||
'udp': True,
|
||||
'allowTransparent': False,
|
||||
},
|
||||
},
|
||||
],
|
||||
'outbounds': [
|
||||
{
|
||||
'tag': 'proxy',
|
||||
'protocol': '',
|
||||
'settings': {},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
'tag': 'direct',
|
||||
'protocol': 'freedom',
|
||||
'settings': {},
|
||||
},
|
||||
{
|
||||
'tag': 'block',
|
||||
'protocol': 'blackhole',
|
||||
'settings': {
|
||||
'response': {
|
||||
'type': 'http',
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
'routing': {},
|
||||
}
|
||||
)
|
||||
|
||||
HY1_TEMPLATE = constructFromDict(
|
||||
{
|
||||
'server': '',
|
||||
'protocol': 'udp',
|
||||
'socks5': {
|
||||
'listen': '127.0.0.1:10808',
|
||||
},
|
||||
'http': {
|
||||
'listen': '127.0.0.1:10809',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
HY2_TEMPLATE = constructFromDict(
|
||||
{
|
||||
'server': '',
|
||||
'tls': {
|
||||
'insecure': False,
|
||||
},
|
||||
'socks5': {
|
||||
'listen': '127.0.0.1:10808',
|
||||
},
|
||||
'http': {
|
||||
'listen': '127.0.0.1:10809',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def getEmptyFactory(protocol: str) -> ConfigurationFactory:
|
||||
if (
|
||||
protocolRepr(protocol) == Protocol.VMess
|
||||
or protocolRepr(protocol) == Protocol.VLESS
|
||||
):
|
||||
factory = RAY_TEMPLATE.deepcopy()
|
||||
factory['outbounds'][0]['protocol'] = protocol.lower()
|
||||
factory['outbounds'][0]['settings']['vnext'] = [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'users': [{'email': PROXY_OUTBOUND_USER_EMAIL}],
|
||||
},
|
||||
]
|
||||
|
||||
return factory
|
||||
|
||||
if (
|
||||
protocolRepr(protocol) == Protocol.Shadowsocks
|
||||
or protocolRepr(protocol) == Protocol.Trojan
|
||||
):
|
||||
factory = RAY_TEMPLATE.deepcopy()
|
||||
factory['outbounds'][0]['protocol'] = protocol.lower()
|
||||
factory['outbounds'][0]['settings']['servers'] = [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
},
|
||||
]
|
||||
|
||||
return factory
|
||||
|
||||
if protocolRepr(protocol) == Protocol.Hysteria1:
|
||||
factory = HY1_TEMPLATE.deepcopy()
|
||||
|
||||
return factory
|
||||
if protocolRepr(protocol) == Protocol.Hysteria2:
|
||||
factory = HY2_TEMPLATE.deepcopy()
|
||||
|
||||
return factory
|
||||
|
||||
return ConfigurationFactory()
|
||||
+26
-19
@@ -26,21 +26,29 @@ import ujson
|
||||
import base64
|
||||
import pybase64
|
||||
|
||||
__all__ = ['JSONEncoder', 'UJSONEncoder', 'Base64Encoder', 'PyBase64Encoder']
|
||||
__all__ = [
|
||||
'JSONEncoder',
|
||||
'UJSONEncoder',
|
||||
'Base64Encoder',
|
||||
'PyBase64Encoder',
|
||||
]
|
||||
|
||||
|
||||
class _JSONEncoder(Encoder):
|
||||
def encode(self, data: Any, **kwargs) -> str:
|
||||
class JSONEncoder(EncoderFactory):
|
||||
@staticmethod
|
||||
def encode(data: Any, **kwargs) -> str:
|
||||
ensure_ascii = kwargs.pop('ensure_ascii', False)
|
||||
|
||||
return json.dumps(data, ensure_ascii=ensure_ascii, **kwargs)
|
||||
|
||||
def decode(self, data: AnyStr, **kwargs) -> Any:
|
||||
@staticmethod
|
||||
def decode(data: AnyStr, **kwargs) -> Any:
|
||||
return json.loads(data, **kwargs)
|
||||
|
||||
|
||||
class _UJSONEncoder(Encoder):
|
||||
def encode(self, data: Any, **kwargs) -> str:
|
||||
class UJSONEncoder(EncoderFactory):
|
||||
@staticmethod
|
||||
def encode(data: Any, **kwargs) -> str:
|
||||
ensure_ascii = kwargs.pop('ensure_ascii', False)
|
||||
escape_forward_slashes = kwargs.pop('escape_forward_slashes', False)
|
||||
|
||||
@@ -51,31 +59,30 @@ class _UJSONEncoder(Encoder):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def decode(self, data: AnyStr, **kwargs) -> Any:
|
||||
@staticmethod
|
||||
def decode(data: AnyStr, **kwargs) -> Any:
|
||||
return ujson.loads(data, **kwargs)
|
||||
|
||||
|
||||
class _Base64Encoder(Encoder):
|
||||
def encode(self, data: Any, **kwargs) -> bytes:
|
||||
class Base64Encoder(EncoderFactory):
|
||||
@staticmethod
|
||||
def encode(data: Any, **kwargs) -> bytes:
|
||||
return base64.b64encode(data, **kwargs)
|
||||
|
||||
def decode(self, data: Any, **kwargs) -> bytes:
|
||||
@staticmethod
|
||||
def decode(data: Any, **kwargs) -> bytes:
|
||||
validate = kwargs.pop('validate', False)
|
||||
|
||||
return base64.b64decode(data, validate=validate, **kwargs)
|
||||
|
||||
|
||||
class _PyBase64Encoder(Encoder):
|
||||
def encode(self, data: Any, **kwargs) -> bytes:
|
||||
class PyBase64Encoder(EncoderFactory):
|
||||
@staticmethod
|
||||
def encode(data: Any, **kwargs) -> bytes:
|
||||
return pybase64.b64encode(data, **kwargs)
|
||||
|
||||
def decode(self, data: Any, **kwargs) -> bytes:
|
||||
@staticmethod
|
||||
def decode(data: Any, **kwargs) -> bytes:
|
||||
validate = kwargs.pop('validate', False)
|
||||
|
||||
return pybase64.b64decode(data, validate=validate, **kwargs)
|
||||
|
||||
|
||||
JSONEncoder = _JSONEncoder()
|
||||
UJSONEncoder = _UJSONEncoder()
|
||||
Base64Encoder = _Base64Encoder()
|
||||
PyBase64Encoder = _PyBase64Encoder()
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# 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.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.Library.UserServers import UserServers
|
||||
from Furious.Library.UserSubs import UserSubs
|
||||
from Furious.Library.UserTUNSettings import UserTUNSettings
|
||||
|
||||
from typing import Union
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['Storage']
|
||||
|
||||
|
||||
class Storage:
|
||||
@staticmethod
|
||||
def UserActivatedItemIndex() -> int:
|
||||
try:
|
||||
return int(AppSettings.get('ActivatedItemIndex'))
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return -1
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def UserServers() -> list[ConfigFactory]:
|
||||
assert APP() is not None
|
||||
|
||||
return UserServers().data()
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def UserSubs() -> dict[str, dict]:
|
||||
assert APP() is not None
|
||||
|
||||
return UserSubs().data()
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def UserTUNSettings() -> dict[str, str]:
|
||||
assert APP() is not None
|
||||
|
||||
return UserTUNSettings().data()
|
||||
|
||||
class Extras:
|
||||
@staticmethod
|
||||
def UserHttpProxy() -> Union[str, None]:
|
||||
try:
|
||||
if APP().isSystemTrayConnected():
|
||||
index, servers = (
|
||||
Storage.UserActivatedItemIndex(),
|
||||
Storage.UserServers(),
|
||||
)
|
||||
|
||||
if index >= 0:
|
||||
return servers[index].httpProxy()
|
||||
else:
|
||||
# Should not reach here
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def UserServerRemark() -> Union[str, None]:
|
||||
try:
|
||||
if APP().isSystemTrayConnected():
|
||||
index, servers = (
|
||||
Storage.UserActivatedItemIndex(),
|
||||
Storage.UserServers(),
|
||||
)
|
||||
|
||||
if index >= 0:
|
||||
return f'{index + 1} - ' + servers[index].getExtras('remark')
|
||||
else:
|
||||
# Should not reach here
|
||||
return ''
|
||||
else:
|
||||
return ''
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Library.Encoder import *
|
||||
from Furious.Library.Configuration import configFactoryFromAny
|
||||
|
||||
__all__ = ['UserServers']
|
||||
|
||||
@@ -33,7 +33,7 @@ class UserServer:
|
||||
subsId: str
|
||||
|
||||
|
||||
class UserServers(SupportExitCleanup, StorageFactory):
|
||||
class UserServers(Mixins.CleanupOnExit, StorageFactory):
|
||||
# remark, config, subsId. (subsId corresponds to unique in user subscription)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -50,7 +50,7 @@ class UserServers(SupportExitCleanup, StorageFactory):
|
||||
|
||||
self._data = restore()
|
||||
self._list = list(
|
||||
constructFromAny(model.pop('config', ''), index=index, **model)
|
||||
configFactoryFromAny(model.pop('config', ''), index=index, **model)
|
||||
for index, model in enumerate(self._data['model'])
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ class UserServers(SupportExitCleanup, StorageFactory):
|
||||
),
|
||||
)
|
||||
|
||||
def data(self) -> list[ConfigurationFactory]:
|
||||
def data(self) -> list[ConfigFactory]:
|
||||
# Shallow copy
|
||||
return self._list
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Library.Encoder import *
|
||||
|
||||
__all__ = ['UserSubs']
|
||||
|
||||
@@ -38,7 +37,7 @@ class UserSub:
|
||||
unique: dict[str, dict]
|
||||
|
||||
|
||||
class UserSubs(SupportExitCleanup, StorageFactory):
|
||||
class UserSubs(Mixins.CleanupOnExit, StorageFactory):
|
||||
# unique: { remark, webURL }
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -17,17 +17,16 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Library.Encoder import *
|
||||
|
||||
__all__ = ['UserTUNSettings']
|
||||
|
||||
registerAppSettings('CustomTUNSettings')
|
||||
|
||||
|
||||
class UserTUNSettings(SupportExitCleanup, StorageFactory):
|
||||
class UserTUNSettings(Mixins.CleanupOnExit, StorageFactory):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
# 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 .Configuration import *
|
||||
from .EmptyFactoryHelper import *
|
||||
from .Encoder import *
|
||||
from .Tcping import *
|
||||
from .Storage import *
|
||||
from .Configuration import *
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
# 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 PySide6.QtWidgets import QApplication
|
||||
|
||||
from typing import Any
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__all__ = [
|
||||
'Translatable',
|
||||
'SupportConnectedCallback',
|
||||
'SupportThemeChangedCallback',
|
||||
'SupportExitCleanup',
|
||||
]
|
||||
|
||||
|
||||
class Translatable:
|
||||
ObjectsPool = list()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.translatable = kwargs.pop('translatable', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
Translatable.ObjectsPool.append(self)
|
||||
|
||||
def retranslate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def retranslateAll():
|
||||
for object_ in Translatable.ObjectsPool:
|
||||
assert isinstance(object_, Translatable)
|
||||
|
||||
if object_.translatable:
|
||||
object_.retranslate()
|
||||
|
||||
|
||||
class SupportConnectedCallback:
|
||||
ObjectsPool = list()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
SupportConnectedCallback.ObjectsPool.append(self)
|
||||
|
||||
def disconnectedCallback(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def connectedCallback(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def callConnectedCallback():
|
||||
for object_ in SupportConnectedCallback.ObjectsPool:
|
||||
assert isinstance(object_, SupportConnectedCallback)
|
||||
|
||||
object_.connectedCallback()
|
||||
|
||||
@staticmethod
|
||||
def callDisconnectedCallback():
|
||||
for object_ in SupportConnectedCallback.ObjectsPool:
|
||||
assert isinstance(object_, SupportConnectedCallback)
|
||||
|
||||
object_.disconnectedCallback()
|
||||
|
||||
|
||||
class SupportThemeChangedCallback:
|
||||
ObjectsPool = list()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
SupportThemeChangedCallback.ObjectsPool.append(self)
|
||||
|
||||
def themeChangedCallback(self, theme: str):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def callThemeChangedCallbackUnchecked(theme: str):
|
||||
for object_ in SupportThemeChangedCallback.ObjectsPool:
|
||||
assert isinstance(object_, SupportThemeChangedCallback)
|
||||
|
||||
object_.themeChangedCallback(theme)
|
||||
|
||||
@staticmethod
|
||||
def callThemeChangedCallback(theme: str):
|
||||
try:
|
||||
app = QApplication.instance()
|
||||
|
||||
if app is not None and app.isDarkModeEnabled():
|
||||
# Ignore application dark detect system
|
||||
logger.info(f'ignore system theme \'{theme}\' changes in dark mode')
|
||||
|
||||
return
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
pass
|
||||
|
||||
logger.info(f'system theme changed to \'{theme}\'')
|
||||
|
||||
SupportThemeChangedCallback.callThemeChangedCallbackUnchecked(theme)
|
||||
|
||||
|
||||
class SupportExitCleanup:
|
||||
ObjectsPool = list()
|
||||
VisitedType = dict()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.uniqueCleanup = kwargs.pop('uniqueCleanup', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
SupportExitCleanup.ObjectsPool.append(self)
|
||||
|
||||
def cleanup(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def cleanupAll():
|
||||
for object_ in SupportExitCleanup.ObjectsPool:
|
||||
assert isinstance(object_, SupportExitCleanup)
|
||||
|
||||
if object_.uniqueCleanup:
|
||||
obtype = str(type(object_))
|
||||
|
||||
if not SupportExitCleanup.VisitedType.get(obtype, False):
|
||||
object_.cleanup()
|
||||
|
||||
SupportExitCleanup.VisitedType[obtype] = True
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
object_.cleanup()
|
||||
|
||||
SupportExitCleanup.ObjectsPool.clear()
|
||||
SupportExitCleanup.VisitedType.clear()
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.QtFramework.WebGETManager import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt.WebGETManager import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtNetwork import *
|
||||
@@ -134,7 +134,7 @@ class _DNSResolver(WebGETManager):
|
||||
return
|
||||
|
||||
while resultMap['depth'] != 0 and startCounter < timeout:
|
||||
PySide6LegacyEventLoopWait(step)
|
||||
PySide6Legacy.eventLoopWait(step)
|
||||
|
||||
startCounter += step
|
||||
|
||||
@@ -17,41 +17,37 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.QtFramework.QtGui import bootstrapIcon, AppQIcon
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt.QtGui import *
|
||||
from Furious.Utility import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['ColorRGB', 'AppHue']
|
||||
|
||||
|
||||
class ColorRGB:
|
||||
LIGHT_BLUE = '#43ACED'
|
||||
LIGHT_RED = '#FF7276'
|
||||
LIGHT_PURPLE = '#DA70D6'
|
||||
__all__ = ['AppHue']
|
||||
|
||||
|
||||
class AppHue:
|
||||
class ColorRGB:
|
||||
LIGHT_BLUE = '#43ACED'
|
||||
LIGHT_RED = '#FF7276'
|
||||
LIGHT_PURPLE = '#DA70D6'
|
||||
|
||||
@staticmethod
|
||||
def disconnectedColor() -> str:
|
||||
return ColorRGB.LIGHT_BLUE
|
||||
return AppHue.ColorRGB.LIGHT_BLUE
|
||||
|
||||
@staticmethod
|
||||
def disconnectedWindowIcon() -> AppQIcon:
|
||||
return bootstrapIcon('rocket-takeoff-window.svg')
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def connectedColor() -> str:
|
||||
if not isAdministrator():
|
||||
return ColorRGB.LIGHT_RED
|
||||
if not SystemRuntime.isAdmin():
|
||||
return AppHue.ColorRGB.LIGHT_RED
|
||||
else:
|
||||
return ColorRGB.LIGHT_PURPLE
|
||||
return AppHue.ColorRGB.LIGHT_PURPLE
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def connectedWindowIcon() -> AppQIcon:
|
||||
if not isAdministrator():
|
||||
if not SystemRuntime.isAdmin():
|
||||
return bootstrapIcon('rocket-takeoff-connected-dark.svg')
|
||||
else:
|
||||
return bootstrapIcon('rocket-takeoff-admin-connected.svg')
|
||||
@@ -17,12 +17,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Externals import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = [
|
||||
'gettext',
|
||||
'LANGUAGE_TO_ABBR',
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework.DynamicTranslate import gettext as _
|
||||
from Furious.QtFramework.QtWidgets import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Qt.DynamicTranslate import gettext as _
|
||||
from Furious.Qt.QtWidgets import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import *
|
||||
@@ -137,7 +137,7 @@ class GuiEditorItemBasicRemark(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldRemark = config.getExtras('remark')
|
||||
newRemark = self.text()
|
||||
|
||||
@@ -150,7 +150,7 @@ class GuiEditorItemBasicRemark(GuiEditorItemTextInput):
|
||||
# Not modified
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
self.setText(config.getExtras('remark'))
|
||||
|
||||
|
||||
@@ -158,33 +158,33 @@ class GuiEditorItemProxyHttp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
oldHttp = config.httpProxyEndpoint()
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldHttp = config.httpProxy()
|
||||
newHttp = self.text()
|
||||
|
||||
if newHttp == '':
|
||||
if oldHttp == '':
|
||||
return False
|
||||
else:
|
||||
config.setHttpProxyEndpoint(newHttp)
|
||||
config.setHttpProxy(newHttp)
|
||||
|
||||
return True
|
||||
else:
|
||||
if isinstance(oldHttp, str):
|
||||
if newHttp != oldHttp:
|
||||
config.setHttpProxyEndpoint(newHttp)
|
||||
config.setHttpProxy(newHttp)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
config.setHttpProxyEndpoint(newHttp)
|
||||
config.setHttpProxy(newHttp)
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setText(config.httpProxyEndpoint())
|
||||
self.setText(config.httpProxy())
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -195,33 +195,33 @@ class GuiEditorItemProxySocks(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
oldSocks = config.socksProxyEndpoint()
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldSocks = config.socksProxy()
|
||||
newSocks = self.text()
|
||||
|
||||
if newSocks == '':
|
||||
if oldSocks == '':
|
||||
return False
|
||||
else:
|
||||
config.setSocksProxyEndpoint(newSocks)
|
||||
config.setSocksProxy(newSocks)
|
||||
|
||||
return True
|
||||
else:
|
||||
if isinstance(oldSocks, str):
|
||||
if newSocks != oldSocks:
|
||||
config.setSocksProxyEndpoint(newSocks)
|
||||
config.setSocksProxy(newSocks)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
config.setSocksProxyEndpoint(newSocks)
|
||||
config.setSocksProxy(newSocks)
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setText(config.socksProxyEndpoint())
|
||||
self.setText(config.socksProxy())
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -237,7 +237,7 @@ class GuiEditorWidget(GuiEditorItemFactory):
|
||||
def containerSequence(self) -> Sequence[GuiEditorItemWidgetContainer]:
|
||||
raise NotImplementedError
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
modified = False
|
||||
|
||||
for container in self._containers:
|
||||
@@ -250,7 +250,7 @@ class GuiEditorWidget(GuiEditorItemFactory):
|
||||
|
||||
return modified
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
for container in self._containers:
|
||||
try:
|
||||
container.factoryToInput(config)
|
||||
@@ -358,7 +358,7 @@ class GuiEditorWidgetQDialog(GuiEditorItemFactory, AppQDialog):
|
||||
self.accepted.disconnect()
|
||||
self.rejected.disconnect()
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
modified = False
|
||||
|
||||
for groupBox in self.groupBoxes:
|
||||
@@ -366,6 +366,6 @@ class GuiEditorWidgetQDialog(GuiEditorItemFactory, AppQDialog):
|
||||
|
||||
return modified
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
for groupBox in self.groupBoxes:
|
||||
groupBox.factoryToInput(config)
|
||||
@@ -17,9 +17,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework.WebGETManager import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt.WebGETManager import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtNetwork import *
|
||||
@@ -32,7 +31,7 @@ __all__ = ['NetworkStateManager']
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NetworkStateManager(SupportConnectedCallback, WebGETManager):
|
||||
class NetworkStateManager(Mixins.ConnectionAware, WebGETManager):
|
||||
MIN_JOB_INTERVAL = 2500
|
||||
MAX_JOB_INTERVAL = 2000000000
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
# 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 Furious.QtFramework.Ancestors import QTranslatable
|
||||
from Furious.QtFramework.DynamicTranslate import gettext as _
|
||||
from Furious.PyFramework.Ancestors import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt.DynamicTranslate import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
@@ -58,7 +57,7 @@ bootstrapIcon = functools.partial(iconFn, 'bootstrap')
|
||||
bootstrapIconWhite = functools.partial(iconFn, 'bootstrap/white')
|
||||
|
||||
|
||||
class AppQAction(QTranslatable, SupportThemeChangedCallback, QAction):
|
||||
class AppQAction(Mixins.QTranslatable, Mixins.ThemeAware, QAction):
|
||||
def __init__(
|
||||
self,
|
||||
text,
|
||||
@@ -184,7 +183,7 @@ class AppQAction(QTranslatable, SupportThemeChangedCallback, QAction):
|
||||
# PySide6 has system theme handling.
|
||||
super().setIcon(bootstrapIconWhite(self.iconFileName))
|
||||
else:
|
||||
if getUbuntuRelease() == '20.04' and self.isTrayAction:
|
||||
if SystemRuntime.ubuntuRelease() == '20.04' and self.isTrayAction:
|
||||
# Ubuntu 20.04 system dark theme does not change tray menu color.
|
||||
# Make it go black always
|
||||
super().setIcon(bootstrapIcon(self.iconFileName))
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
|
||||
from PySide6.QtNetwork import *
|
||||
|
||||
@@ -17,20 +17,16 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.QtFramework.Ancestors import *
|
||||
from Furious.QtFramework.DynamicTheme import AppHue
|
||||
from Furious.QtFramework.DynamicTranslate import gettext as _
|
||||
from Furious.QtFramework.QtGui import AppQAction, AppQSeperator
|
||||
from Furious.PyFramework import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt.DynamicTheme import *
|
||||
from Furious.Qt.DynamicTranslate import gettext as _
|
||||
from Furious.Qt.QtGui import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
import functools
|
||||
|
||||
from typing import Union
|
||||
|
||||
__all__ = [
|
||||
@@ -72,7 +68,7 @@ def moveToCenter(widget, parent=None):
|
||||
widget.move(geometry.topLeft())
|
||||
|
||||
|
||||
class AppQCheckBox(QTranslatable, QCheckBox):
|
||||
class AppQCheckBox(Mixins.QTranslatable, QCheckBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -80,7 +76,7 @@ class AppQCheckBox(QTranslatable, QCheckBox):
|
||||
self.setText(_(self.text()))
|
||||
|
||||
|
||||
class AppQComboBox(QTranslatable, QComboBox):
|
||||
class AppQComboBox(Mixins.QTranslatable, QComboBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -89,7 +85,7 @@ class AppQComboBox(QTranslatable, QComboBox):
|
||||
self.setItemText(index, _(self.itemText(index)))
|
||||
|
||||
|
||||
class AppQDialog(QTranslatable, SupportConnectedCallback, QDialog):
|
||||
class AppQDialog(Mixins.QTranslatable, Mixins.ConnectionAware, QDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -134,7 +130,7 @@ class AppQDialog(QTranslatable, SupportConnectedCallback, QDialog):
|
||||
self.setWindowIcon(AppHue.connectedWindowIcon())
|
||||
|
||||
|
||||
class AppQDialogButtonBox(QTranslatable, QDialogButtonBox):
|
||||
class AppQDialogButtonBox(Mixins.QTranslatable, QDialogButtonBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -143,7 +139,7 @@ class AppQDialogButtonBox(QTranslatable, QDialogButtonBox):
|
||||
button.setText(_(button.text()))
|
||||
|
||||
|
||||
class AppQGroupBox(QTranslatable, QGroupBox):
|
||||
class AppQGroupBox(Mixins.QTranslatable, QGroupBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -151,7 +147,7 @@ class AppQGroupBox(QTranslatable, QGroupBox):
|
||||
self.setTitle(_(self.title()))
|
||||
|
||||
|
||||
class AppQHeaderView(SupportExitCleanup, SupportConnectedCallback, QHeaderView):
|
||||
class AppQHeaderView(Mixins.CleanupOnExit, Mixins.ConnectionAware, QHeaderView):
|
||||
def sectionSizeSettingsEmpty(self):
|
||||
return (
|
||||
self.legacySectionSizeSettingsName == ''
|
||||
@@ -175,7 +171,7 @@ class AppQHeaderView(SupportExitCleanup, SupportConnectedCallback, QHeaderView):
|
||||
|
||||
self.setSectionsClickable(True)
|
||||
self.setStyleSheet(self.getStyleSheet(AppHue.currentColor()))
|
||||
self.setFont(QFont(APP().customFontName))
|
||||
self.setFont(QFont(AppFontName()))
|
||||
|
||||
# self.sectionResized.connect(self.handleSectionResized)
|
||||
|
||||
@@ -209,7 +205,7 @@ class AppQHeaderView(SupportExitCleanup, SupportConnectedCallback, QHeaderView):
|
||||
if self.sectionSizeTable.get(str(column)) is None:
|
||||
self.sectionSizeTable[str(column)] = self.defaultSectionSize()
|
||||
|
||||
with QBlockSignals(self):
|
||||
with Mixins.QBlockSignalContext(self):
|
||||
for key, value in reversed(self.sectionSizeTable.items()):
|
||||
self.resizeSection(int(key), value)
|
||||
except Exception:
|
||||
@@ -261,7 +257,7 @@ class AppQHeaderView(SupportExitCleanup, SupportConnectedCallback, QHeaderView):
|
||||
AppSettings.set(self.sectionSizeSettingsName, self.saveState())
|
||||
|
||||
|
||||
class AppQLabel(QTranslatable, QLabel):
|
||||
class AppQLabel(Mixins.QTranslatable, QLabel):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -269,7 +265,7 @@ class AppQLabel(QTranslatable, QLabel):
|
||||
self.setText(_(self.text()))
|
||||
|
||||
|
||||
class AppQListWidget(SupportConnectedCallback, QListWidget):
|
||||
class AppQListWidget(Mixins.ConnectionAware, QListWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -298,9 +294,9 @@ class AppQListWidget(SupportConnectedCallback, QListWidget):
|
||||
|
||||
|
||||
class AppQMainWindow(
|
||||
QTranslatable,
|
||||
SupportConnectedCallback,
|
||||
SupportExitCleanup,
|
||||
Mixins.QTranslatable,
|
||||
Mixins.ConnectionAware,
|
||||
Mixins.CleanupOnExit,
|
||||
QMainWindow,
|
||||
):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -349,7 +345,7 @@ class AppQMainWindow(
|
||||
pass
|
||||
|
||||
|
||||
class AppQMenu(QTranslatable, SupportConnectedCallback, QMenu):
|
||||
class AppQMenu(Mixins.QTranslatable, Mixins.ConnectionAware, QMenu):
|
||||
def __init__(self, *actions, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@@ -398,7 +394,7 @@ class AppQMenu(QTranslatable, SupportConnectedCallback, QMenu):
|
||||
self.setStyleSheet(self.getStyleSheet(AppHue.connectedColor()))
|
||||
|
||||
|
||||
class AppQMenuBar(SupportConnectedCallback, QMenuBar):
|
||||
class AppQMenuBar(Mixins.ConnectionAware, QMenuBar):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -426,7 +422,7 @@ class AppQMenuBar(SupportConnectedCallback, QMenuBar):
|
||||
self.setSelectionColor(AppHue.connectedColor())
|
||||
|
||||
|
||||
class AppQMessageBox(QTranslatable, SupportConnectedCallback, QMessageBox):
|
||||
class AppQMessageBox(Mixins.QTranslatable, Mixins.ConnectionAware, QMessageBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -475,7 +471,7 @@ class AppQMessageBox(QTranslatable, SupportConnectedCallback, QMessageBox):
|
||||
self.setWindowIcon(AppHue.connectedWindowIcon())
|
||||
|
||||
|
||||
class AppQPushButton(QTranslatable, QPushButton):
|
||||
class AppQPushButton(Mixins.QTranslatable, QPushButton):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -483,7 +479,7 @@ class AppQPushButton(QTranslatable, QPushButton):
|
||||
self.setText(_(self.text()))
|
||||
|
||||
|
||||
class AppQTableWidget(SupportConnectedCallback, QTableWidget):
|
||||
class AppQTableWidget(Mixins.ConnectionAware, QTableWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -552,7 +548,7 @@ class AppQTableWidget(SupportConnectedCallback, QTableWidget):
|
||||
self.setSelectionColor(AppHue.connectedColor())
|
||||
|
||||
|
||||
class AppQTabWidget(QTranslatable, QTabWidget):
|
||||
class AppQTabWidget(Mixins.QTranslatable, QTabWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -561,7 +557,7 @@ class AppQTabWidget(QTranslatable, QTabWidget):
|
||||
self.setTabText(index, _(self.tabText(index)))
|
||||
|
||||
|
||||
class AppQToolBar(QTranslatable, QToolBar):
|
||||
class AppQToolBar(Mixins.QTranslatable, QToolBar):
|
||||
def __init__(self, *actions, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework.TextEditorTheme import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt.TextEditorTheme import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
@@ -38,7 +37,7 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
class SupportPointSizeSettings(SupportExitCleanup):
|
||||
class SupportPointSizeSettings(Mixins.CleanupOnExit):
|
||||
def pointSizeSettingsEmpty(self):
|
||||
return self.pointSizeSettingsName == ''
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
|
||||
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.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt.QtWidgets import *
|
||||
from Furious.Qt.DynamicTranslate import gettext as _
|
||||
from Furious.Qt.WebGETManager import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtNetwork import *
|
||||
|
||||
from typing import Union, Callable
|
||||
from typing import Callable
|
||||
|
||||
import logging
|
||||
import functools
|
||||
@@ -103,7 +101,7 @@ class UpdatesManager(WebGETManager):
|
||||
if versionToValue(newVersion) > versionToValue(APPLICATION_VERSION):
|
||||
|
||||
def handleResultCode(code):
|
||||
if code == PySide6LegacyEnumValueWrapper(
|
||||
if code == PySide6Legacy.enumValueWrapper(
|
||||
AppQMessageBox.StandardButton.Yes
|
||||
):
|
||||
if QDesktopServices.openUrl(QtCore.QUrl(info['html_url'])):
|
||||
@@ -17,17 +17,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.QtFramework.QtNetwork import *
|
||||
from Furious.QtFramework.QtWidgets import *
|
||||
from Furious.QtFramework.DynamicTranslate import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt.QtNetwork import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtNetwork import *
|
||||
|
||||
from typing import AnyStr, Union, Callable
|
||||
from typing import Union
|
||||
|
||||
import logging
|
||||
import functools
|
||||
@@ -71,9 +67,8 @@ class WebGETManager(AppQNetworkAccessManager):
|
||||
|
||||
if not self.mustCallOnce:
|
||||
call()
|
||||
else:
|
||||
if not self.mustCalled:
|
||||
call()
|
||||
elif not self.mustCalled:
|
||||
call()
|
||||
|
||||
def handleReadyReadByNetworkReply(self, networkReply: QNetworkReply, **kwargs):
|
||||
self.hasDataCallback(networkReply, **kwargs)
|
||||
+2
-8
@@ -17,16 +17,10 @@
|
||||
|
||||
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 Furious.Frozenlib import *
|
||||
from Furious.Qt.WebGETManager import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtNetwork import *
|
||||
|
||||
from typing import AnyStr, Union, Callable
|
||||
|
||||
@@ -15,18 +15,16 @@
|
||||
# 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 .Ancestors import *
|
||||
from .DNSResolver import *
|
||||
from .CoreProcess import *
|
||||
from .DynamicTheme import *
|
||||
from .DynamicTranslate import *
|
||||
from .QtGui import *
|
||||
from .QtWidgets import *
|
||||
from .QtNetwork import *
|
||||
from .UpdatesManager import *
|
||||
from .GuiEditorXXX import *
|
||||
from .NetworkStateManager import *
|
||||
from .QtGui import *
|
||||
from .QtNetwork import *
|
||||
from .QtWidgets import *
|
||||
from .TextEditor import *
|
||||
from .TextEditorTheme import *
|
||||
from .GuiEditorXXX import *
|
||||
from .UpdatesManager import *
|
||||
from .WebGETManager import *
|
||||
from .XrayAssetDownloadManager import *
|
||||
@@ -1,74 +0,0 @@
|
||||
# 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.PyFramework import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
|
||||
__all__ = ['QBlockSignals', 'QTranslatable']
|
||||
|
||||
|
||||
class QProtection:
|
||||
def __init__(self, qobject: QtCore.QObject):
|
||||
self.qobject = qobject
|
||||
|
||||
def __enter__(self):
|
||||
if hasattr(self.qobject, 'setDisabled'):
|
||||
self.qobject.setDisabled(True)
|
||||
|
||||
def __exit__(self, exceptionType, exceptionValue, tb):
|
||||
if hasattr(self.qobject, 'setDisabled'):
|
||||
self.qobject.setDisabled(False)
|
||||
|
||||
|
||||
class QBlockSignals:
|
||||
def __init__(self, qobject: QtCore.QObject):
|
||||
self.qobject = qobject
|
||||
|
||||
def __enter__(self):
|
||||
if hasattr(self.qobject, 'blockSignals'):
|
||||
self.qobject.blockSignals(True)
|
||||
|
||||
def __exit__(self, exceptionType, exceptionValue, tb):
|
||||
if hasattr(self.qobject, 'blockSignals'):
|
||||
self.qobject.blockSignals(False)
|
||||
|
||||
|
||||
class QTranslatable(Translatable):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.useQProtection = kwargs.pop('useQProtection', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def retranslate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def retranslateAll():
|
||||
for ob in QTranslatable.ObjectsPool:
|
||||
assert isinstance(ob, QTranslatable)
|
||||
|
||||
if ob.translatable:
|
||||
if ob.useQProtection:
|
||||
assert isinstance(ob, QtCore.QObject)
|
||||
|
||||
with QProtection(ob):
|
||||
ob.retranslate()
|
||||
else:
|
||||
ob.retranslate()
|
||||
@@ -17,12 +17,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Core import *
|
||||
from Furious.Widget.ConnectProgressBar import ConnectProgressBar
|
||||
|
||||
@@ -107,7 +107,7 @@ class ConnectAction(AppQAction):
|
||||
APP().systemTray.RoutingAction.setDisabled(value)
|
||||
APP().systemTray.SystemProxyAction.setDisabled(value)
|
||||
|
||||
if isAdministrator():
|
||||
if SystemRuntime.isAdmin():
|
||||
TUNModeAction = APP().systemTray.SettingsAction.getTUNModeAction()
|
||||
|
||||
if TUNModeAction is not None:
|
||||
@@ -133,7 +133,7 @@ class ConnectAction(AppQAction):
|
||||
|
||||
AppSettings.turnON_('Connect')
|
||||
|
||||
SupportConnectedCallback.callConnectedCallback()
|
||||
Mixins.ConnectionAware.callConnectedCallback()
|
||||
|
||||
# Accept new action
|
||||
self.setDisabledAction(False)
|
||||
@@ -154,7 +154,7 @@ class ConnectAction(AppQAction):
|
||||
|
||||
pass
|
||||
|
||||
SupportConnectedCallback.callDisconnectedCallback()
|
||||
Mixins.ConnectionAware.callDisconnectedCallback()
|
||||
|
||||
def doDisconnectWithTrayMessage(self, message: str):
|
||||
self.doDisconnect()
|
||||
@@ -165,7 +165,7 @@ class ConnectAction(AppQAction):
|
||||
# Connect action
|
||||
assert self.textCompare('Connect')
|
||||
|
||||
if not AS_UserServers():
|
||||
if not Storage.UserServers():
|
||||
AppSettings.turnOFF('Connect')
|
||||
|
||||
self.setChecked(False)
|
||||
@@ -181,7 +181,7 @@ class ConnectAction(AppQAction):
|
||||
|
||||
return
|
||||
|
||||
if AS_UserActivatedItemIndex() < 0:
|
||||
if Storage.UserActivatedItemIndex() < 0:
|
||||
AppSettings.turnOFF('Connect')
|
||||
|
||||
self.setChecked(False)
|
||||
@@ -198,7 +198,7 @@ class ConnectAction(AppQAction):
|
||||
return
|
||||
|
||||
try:
|
||||
config = AS_UserServers()[AS_UserActivatedItemIndex()]
|
||||
config = Storage.UserServers()[Storage.UserActivatedItemIndex()]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
@@ -206,9 +206,9 @@ class ConnectAction(AppQAction):
|
||||
|
||||
self.setChecked(False)
|
||||
else:
|
||||
assert isinstance(config, ConfigurationFactory)
|
||||
assert isinstance(config, ConfigFactory)
|
||||
|
||||
if not validateProxyServer(config.httpProxyEndpoint()):
|
||||
if not validateProxyServer(config.httpProxy()):
|
||||
# Proxy server is not valid. Do not connect
|
||||
|
||||
AppSettings.turnOFF('Connect')
|
||||
@@ -232,20 +232,20 @@ class ConnectAction(AppQAction):
|
||||
self.doConnecting()
|
||||
|
||||
# Clear previous log
|
||||
loggerCore().clear()
|
||||
loggerTun_().clear()
|
||||
AppLoggerWindow.Core().clear()
|
||||
AppLoggerWindow.TUN_().clear()
|
||||
|
||||
success = self.coreManager.start(
|
||||
config,
|
||||
routing=AppSettings.get('Routing'),
|
||||
exitCallback=self.coreExitCallback,
|
||||
msgCallbackCore=lambda line: loggerCore().appendLine(line),
|
||||
msgCallbackTun_=lambda line: loggerTun_().appendLine(line),
|
||||
msgCallbackCore=lambda line: AppLoggerWindow.Core().appendLine(line),
|
||||
msgCallbackTUN_=lambda line: AppLoggerWindow.TUN_().appendLine(line),
|
||||
)
|
||||
|
||||
if self.actionQueue.empty():
|
||||
if success:
|
||||
SystemProxy.set(config.httpProxyEndpoint(), PROXY_SERVER_BYPASS)
|
||||
SystemProxy.set(config.httpProxy(), PROXY_SERVER_BYPASS)
|
||||
|
||||
self.doConnected()
|
||||
|
||||
@@ -280,7 +280,7 @@ class ConnectAction(AppQAction):
|
||||
f'{APPLICATION_NAME} {newVersion} ' + _('is available to download')
|
||||
)
|
||||
|
||||
connectedHttpProxy = connectedHttpProxyEndpoint()
|
||||
connectedHttpProxy = Storage.Extras.UserHttpProxy()
|
||||
|
||||
# Check for updates
|
||||
self.updatesManager.configureHttpProxy(connectedHttpProxy)
|
||||
@@ -308,7 +308,7 @@ class ConnectAction(AppQAction):
|
||||
if callable(action):
|
||||
action()
|
||||
|
||||
def coreExitCallback(self, core: CoreFactory, exitcode: int):
|
||||
def coreExitCallback(self, core: CoreProcessFactory, exitcode: int):
|
||||
def putItem(item):
|
||||
try:
|
||||
self.actionQueue.put_nowait(item)
|
||||
@@ -317,11 +317,11 @@ class ConnectAction(AppQAction):
|
||||
|
||||
pass
|
||||
|
||||
if exitcode == CoreFactory.ExitCode.SystemShuttingDown:
|
||||
if exitcode == CoreProcessFactory.ExitCode.SystemShuttingDown.value:
|
||||
# System shutting down. Do nothing
|
||||
return
|
||||
|
||||
if exitcode == CoreFactory.ExitCode.ConfigurationError:
|
||||
if exitcode == CoreProcessFactory.ExitCode.ConfigurationError.value:
|
||||
return putItem(
|
||||
functools.partial(
|
||||
self.doDisconnectWithTrayMessage,
|
||||
@@ -329,7 +329,7 @@ class ConnectAction(AppQAction):
|
||||
)
|
||||
)
|
||||
|
||||
if exitcode == CoreFactory.ExitCode.ServerStartFailure:
|
||||
if exitcode == CoreProcessFactory.ExitCode.ServerStartFailure.value:
|
||||
return putItem(
|
||||
functools.partial(
|
||||
self.doDisconnectWithTrayMessage,
|
||||
@@ -338,7 +338,7 @@ class ConnectAction(AppQAction):
|
||||
)
|
||||
|
||||
if isinstance(core, Hysteria1):
|
||||
if exitcode == Hysteria1.ExitCode.RemoteNetworkError:
|
||||
if exitcode == Hysteria1.ExitCode.RemoteNetworkError.value:
|
||||
return putItem(
|
||||
functools.partial(
|
||||
self.doDisconnectWithTrayMessage,
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
import functools
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
__all__ = ['EditConfigurationAction']
|
||||
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
import functools
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
__all__ = ['ExitAction']
|
||||
|
||||
|
||||
@@ -15,17 +15,15 @@
|
||||
# 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 Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6.QtWidgets import QApplication, QFileDialog
|
||||
|
||||
import os
|
||||
import logging
|
||||
import functools
|
||||
|
||||
__all__ = [
|
||||
'ImportFromFileAction',
|
||||
@@ -53,7 +51,7 @@ def showImportErrorMBox(clipboard: str):
|
||||
|
||||
|
||||
def importItemFromClipboard(clipboard: str):
|
||||
factory = constructFromAny(clipboard)
|
||||
factory = configFactoryFromAny(clipboard)
|
||||
|
||||
if not factory.isValid():
|
||||
showImportErrorMBox(clipboard)
|
||||
@@ -174,7 +172,9 @@ class ImportFromFileAction(AppQAction):
|
||||
# Show the MessageBox asynchronously
|
||||
mbox.open()
|
||||
else:
|
||||
factory = constructFromAny(plainText, remark=os.path.basename(filename))
|
||||
factory = configFactoryFromAny(
|
||||
plainText, remark=os.path.basename(filename)
|
||||
)
|
||||
|
||||
if factory.isValid():
|
||||
APP().mainWindow.appendNewItemByFactory(factory)
|
||||
@@ -209,10 +209,10 @@ class ImportURIFromClipboardAction(AppQAction):
|
||||
importItemFromClipboard(clipboard)
|
||||
else:
|
||||
imported = list()
|
||||
rowCount = len(AS_UserServers())
|
||||
rowCount = len(Storage.UserServers())
|
||||
|
||||
for uri in split:
|
||||
factory = constructFromAny(uri)
|
||||
factory = configFactoryFromAny(uri)
|
||||
|
||||
if factory.isValid():
|
||||
APP().mainWindow.appendNewItemByFactory(factory)
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
import logging
|
||||
import functools
|
||||
|
||||
__all__ = ['LanguageAction']
|
||||
|
||||
@@ -48,7 +47,7 @@ class LanguageChildAction(AppQAction):
|
||||
|
||||
AppSettings.set('Language', abbr)
|
||||
|
||||
QTranslatable.retranslateAll()
|
||||
Mixins.QTranslatable.retranslateAll()
|
||||
|
||||
def retranslate(self):
|
||||
# Nothing to do
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
import functools
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
__all__ = ['RoutingAction']
|
||||
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
from typing import Union
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['SettingsAction']
|
||||
|
||||
registerAppSettings('VPNMode', isBinary=True)
|
||||
@@ -32,10 +31,10 @@ registerAppSettings('UseMonochromeTrayIcon', isBinary=True)
|
||||
if PLATFORM == 'Darwin':
|
||||
registerAppSettings('HideDockIcon', isBinary=True)
|
||||
|
||||
registerAppSettings('StartupOnBoot', isBinary=True, default=BinarySettings.ON_)
|
||||
registerAppSettings('PowerSaveMode', isBinary=True, default=BinarySettings.ON_)
|
||||
registerAppSettings('StartupOnBoot', isBinary=True, default=AppBinarySettings.ON_)
|
||||
registerAppSettings('PowerSaveMode', isBinary=True, default=AppBinarySettings.ON_)
|
||||
registerAppSettings(
|
||||
'ShowProgressBarWhenConnecting', isBinary=True, default=BinarySettings.ON_
|
||||
'ShowProgressBarWhenConnecting', isBinary=True, default=AppBinarySettings.ON_
|
||||
)
|
||||
registerAppSettings('ShowTabAndSpacesInEditor', isBinary=True)
|
||||
|
||||
@@ -48,7 +47,7 @@ TRANSLATABLE_TUN_MODE = [
|
||||
|
||||
class TUNModeAction(AppQAction):
|
||||
def __init__(self, **kwargs):
|
||||
if isAdministrator():
|
||||
if SystemRuntime.isAdmin():
|
||||
super().__init__(_('TUN Mode'), **kwargs)
|
||||
else:
|
||||
super().__init__(_(f'TUN Mode Disabled ({ADMINISTRATOR_NAME})'), **kwargs)
|
||||
@@ -56,7 +55,7 @@ class TUNModeAction(AppQAction):
|
||||
self.setDisabled(True)
|
||||
|
||||
def triggeredCallback(self, checked):
|
||||
assert isAdministrator()
|
||||
assert SystemRuntime.isAdmin()
|
||||
|
||||
if checked:
|
||||
AppSettings.turnON_('VPNMode')
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
import functools
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
__all__ = ['SystemProxyAction']
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.Utility.Constants import *
|
||||
|
||||
from typing import Callable
|
||||
|
||||
@@ -43,14 +43,14 @@ else:
|
||||
|
||||
|
||||
class AppMainProcess(ProcessContext.Process):
|
||||
def __init__(self, loader: Callable[[], ApplicationFactory], **kwargs):
|
||||
def __init__(self, func: Callable[[], ApplicationFactory], **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.startupTime = str(datetime.datetime.now()).replace(':', '')
|
||||
self.logFileName = f'{self.startupTime}.log'
|
||||
self.fileWritten = multiprocessing.Manager().Value('b', False)
|
||||
|
||||
self.loader = loader
|
||||
self.func = func
|
||||
self.application = None
|
||||
|
||||
def exceptHook(self, exceptionType, exceptionValue, tb):
|
||||
@@ -58,9 +58,9 @@ class AppMainProcess(ProcessContext.Process):
|
||||
traceback.print_exception(exceptionType, exceptionValue, tb)
|
||||
|
||||
if isinstance(exceptionValue, AssertionError):
|
||||
exitcode = ApplicationFactory.ExitCode.AssertionError
|
||||
exitcode = ApplicationFactory.ExitCode.AssertionError.value
|
||||
else:
|
||||
exitcode = ApplicationFactory.ExitCode.UnknownException
|
||||
exitcode = ApplicationFactory.ExitCode.UnknownException.value
|
||||
|
||||
logger.error(f'stopped with exitcode {exitcode}')
|
||||
|
||||
@@ -92,7 +92,7 @@ class AppMainProcess(ProcessContext.Process):
|
||||
if APP() is None:
|
||||
crashLog = f'{stackLog}'
|
||||
else:
|
||||
crashLog = f'{APP().log()}\n{stackLog}'
|
||||
crashLog = f'{AppLoggerWindow.Self().plainText()}\n{stackLog}'
|
||||
|
||||
with open(CRASH_LOG_DIR / self.logFileName, 'w', encoding='utf-8') as file:
|
||||
file.write(crashLog)
|
||||
@@ -111,7 +111,7 @@ class AppMainProcess(ProcessContext.Process):
|
||||
def run(self):
|
||||
sys.excepthook = self.exceptHook
|
||||
|
||||
self.application = self.loader()
|
||||
self.application = self.func()
|
||||
|
||||
for sig in [signal.SIGTERM, signal.SIGINT]:
|
||||
signal.signal(sig, self.handler)
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
# 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.Utility.Constants import *
|
||||
from Furious.Utility.AppSettings import *
|
||||
|
||||
from typing import Union
|
||||
|
||||
import functools
|
||||
|
||||
__all___ = [
|
||||
'AS_UserActivatedItemIndex',
|
||||
'AS_UserServers',
|
||||
'AS_UserSubscription',
|
||||
'AS_UserTUNSettings',
|
||||
'connectedHttpProxyEndpoint',
|
||||
'connectedServerRemark',
|
||||
]
|
||||
|
||||
|
||||
def _activatedItemIndex() -> int:
|
||||
try:
|
||||
return int(AppSettings.get('ActivatedItemIndex'))
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
def _userServersData() -> list:
|
||||
try:
|
||||
return APP().userServers.data()
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def _userSubscriptionData() -> dict[str, dict]:
|
||||
try:
|
||||
return APP().userSubs.data()
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def _userTUNSettingsData() -> dict[str, str]:
|
||||
try:
|
||||
return APP().userTUNSettings.data()
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
AS_UserActivatedItemIndex = functools.partial(_activatedItemIndex)
|
||||
AS_UserServers = functools.partial(_userServersData)
|
||||
AS_UserSubscription = functools.partial(_userSubscriptionData)
|
||||
AS_UserTUNSettings = functools.partial(_userTUNSettingsData)
|
||||
|
||||
|
||||
def connectedHttpProxyEndpoint() -> Union[str, None]:
|
||||
try:
|
||||
if APP().isSystemTrayConnected():
|
||||
index = AS_UserActivatedItemIndex()
|
||||
|
||||
if index >= 0:
|
||||
return AS_UserServers()[index].httpProxyEndpoint()
|
||||
else:
|
||||
# Should not reach here
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def connectedServerRemark() -> str:
|
||||
try:
|
||||
if APP().isSystemTrayConnected():
|
||||
index = AS_UserActivatedItemIndex()
|
||||
|
||||
if index >= 0:
|
||||
return f'{index + 1} - ' + AS_UserServers()[index].getExtras('remark')
|
||||
else:
|
||||
# Should not reach here
|
||||
return ''
|
||||
else:
|
||||
return ''
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
@@ -1,47 +0,0 @@
|
||||
# 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 Furious.Utility.Constants import *
|
||||
from Furious.Utility.Utility import *
|
||||
|
||||
import time
|
||||
|
||||
__all__ = ['PySide6LegacyEnumValueWrapper', 'PySide6LegacyEventLoopWait']
|
||||
|
||||
|
||||
def PySide6LegacyEnumValueWrapper(enum):
|
||||
# Protect PySide6 enum wrapper behavior changes
|
||||
if versionToValue(PYSIDE6_VERSION) < versionToValue('6.2.2'):
|
||||
return enum
|
||||
else:
|
||||
return enum.value
|
||||
|
||||
|
||||
def PySide6LegacyEventLoopWait(ms):
|
||||
# Protect qWait method does not exist in some
|
||||
# old PySide6 version
|
||||
if versionToValue(PYSIDE6_VERSION) < versionToValue('6.3.1'):
|
||||
for counter in range(0, ms, 10):
|
||||
time.sleep(10 / 1000)
|
||||
|
||||
APP().processEvents()
|
||||
else:
|
||||
from PySide6.QtTest import QTest
|
||||
|
||||
QTest.qWait(ms)
|
||||
|
||||
APP().processEvents()
|
||||
@@ -15,9 +15,8 @@
|
||||
# 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 Furious.Utility.Constants import *
|
||||
from Furious.Utility.AppSettings import *
|
||||
from Furious.Utility.SystemRuntime import getAppImagePath, isScriptMode
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Utility.SystemRuntime import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
|
||||
@@ -35,7 +34,7 @@ class StartupOnBoot:
|
||||
@staticmethod
|
||||
def on_():
|
||||
def _on_():
|
||||
if isScriptMode():
|
||||
if SystemRuntime.isScriptMode():
|
||||
# Script mode
|
||||
logger.info('ignore turn on StartupOnBoot in script mode')
|
||||
|
||||
@@ -67,7 +66,7 @@ class StartupOnBoot:
|
||||
|
||||
return False
|
||||
|
||||
appImagePath = getAppImagePath()
|
||||
appImagePath = SystemRuntime.appImagePath()
|
||||
|
||||
if appImagePath:
|
||||
execPath = appImagePath
|
||||
@@ -122,13 +121,11 @@ class StartupOnBoot:
|
||||
'RunAtLoad': True,
|
||||
}
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
autostartDir,
|
||||
f'{APPLICATION_MACOS_SIGNATURE}.{APPLICATION_NAME}.plist',
|
||||
),
|
||||
'wb',
|
||||
) as file:
|
||||
plistFile = (
|
||||
f'{APPLICATION_MACOS_SIGNATURE}.{APPLICATION_NAME}.plist'
|
||||
)
|
||||
|
||||
with open(os.path.join(autostartDir, plistFile), 'wb') as file:
|
||||
plistlib.dump(plistData, file)
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
@@ -176,13 +173,10 @@ class StartupOnBoot:
|
||||
os.path.expanduser('~'), 'Library', 'LaunchAgents'
|
||||
)
|
||||
|
||||
plistFile = f'{APPLICATION_MACOS_SIGNATURE}.{APPLICATION_NAME}.plist'
|
||||
|
||||
try:
|
||||
os.remove(
|
||||
os.path.join(
|
||||
autostartDir,
|
||||
f'{APPLICATION_MACOS_SIGNATURE}.{APPLICATION_NAME}.plist',
|
||||
)
|
||||
)
|
||||
os.remove(os.path.join(autostartDir, plistFile))
|
||||
except FileNotFoundError:
|
||||
return True
|
||||
except Exception:
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Constants import *
|
||||
from Furious.Utility.Utility import parseHostPort, runExternalCommand
|
||||
from Furious.Utility.AppSettings import *
|
||||
from Furious.Frozenlib import *
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
@@ -149,6 +147,9 @@ class _SystemProxy:
|
||||
try:
|
||||
host, port = parseHostPort(server)
|
||||
|
||||
if host is None or port is None:
|
||||
raise
|
||||
|
||||
linuxProxyConfig('proxy.http', 'host', host)
|
||||
linuxProxyConfig('proxy.http', 'port', port)
|
||||
linuxProxyConfig('proxy.https', 'host', host)
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Utility.Constants import *
|
||||
from Furious.Utility.AppSettings import AppSettings
|
||||
from Furious.Utility.Utility import runExternalCommand
|
||||
from Furious.Frozenlib import *
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -27,99 +25,89 @@ import ctypes
|
||||
import functools
|
||||
import subprocess
|
||||
|
||||
__all__ = [
|
||||
'getPythonVersion',
|
||||
'getUbuntuRelease',
|
||||
'getAppImagePath',
|
||||
'isAdministrator',
|
||||
'isTUNMode',
|
||||
'isScriptMode',
|
||||
'isPythonw',
|
||||
'isWindows7',
|
||||
]
|
||||
__all__ = ['SystemRuntime']
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def getPythonVersion():
|
||||
return '.'.join(str(info) for info in sys.version_info)
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def getUbuntuRelease() -> str:
|
||||
try:
|
||||
if PLATFORM != 'Linux':
|
||||
return ''
|
||||
|
||||
result = runExternalCommand(
|
||||
['cat', '/etc/lsb-release'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
)
|
||||
|
||||
values = dict(
|
||||
list(line.split('='))
|
||||
for line in filter(lambda x: x != '', result.stdout.decode().split('\n'))
|
||||
)
|
||||
|
||||
if values['DISTRIB_ID'] == 'Ubuntu':
|
||||
return values['DISTRIB_RELEASE']
|
||||
else:
|
||||
return ''
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def getAppImagePath() -> str:
|
||||
if PLATFORM != 'Linux':
|
||||
return ''
|
||||
|
||||
if 'APPIMAGE' in os.environ:
|
||||
return os.environ['APPIMAGE']
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def isAdministrator() -> bool:
|
||||
if PLATFORM == 'Windows':
|
||||
return ctypes.windll.shell32.IsUserAnAdmin() == 1
|
||||
else:
|
||||
return os.geteuid() == 0
|
||||
|
||||
|
||||
def isTUNMode() -> bool:
|
||||
return isAdministrator() and AppSettings.isStateON_('VPNMode')
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def isScriptMode() -> bool:
|
||||
return sys.argv[0].endswith('.py')
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def isPythonw() -> bool:
|
||||
def isRealFile(file):
|
||||
if not hasattr(file, 'fileno'):
|
||||
return False
|
||||
|
||||
class SystemRuntime:
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def ubuntuRelease() -> str:
|
||||
try:
|
||||
tmp = os.dup(file.fileno())
|
||||
if PLATFORM != 'Linux':
|
||||
return ''
|
||||
|
||||
result = runExternalCommand(
|
||||
['cat', '/etc/lsb-release'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
)
|
||||
|
||||
values = dict(
|
||||
list(line.split('='))
|
||||
for line in filter(
|
||||
lambda x: x != '', result.stdout.decode().split('\n')
|
||||
)
|
||||
)
|
||||
|
||||
if values['DISTRIB_ID'] == 'Ubuntu':
|
||||
return values['DISTRIB_RELEASE']
|
||||
else:
|
||||
return ''
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return False
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
def appImagePath() -> str:
|
||||
if PLATFORM != 'Linux':
|
||||
return ''
|
||||
|
||||
if 'APPIMAGE' in os.environ:
|
||||
return os.environ['APPIMAGE']
|
||||
else:
|
||||
os.close(tmp)
|
||||
return ''
|
||||
|
||||
return True
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def isAdmin() -> bool:
|
||||
if PLATFORM == 'Windows':
|
||||
return ctypes.windll.shell32.IsUserAnAdmin() == 1
|
||||
else:
|
||||
return os.geteuid() == 0
|
||||
|
||||
# pythonw.exe. Also applies to packed GUI application on Windows
|
||||
return not isRealFile(sys.__stdout__) or not isRealFile(sys.__stderr__)
|
||||
@staticmethod
|
||||
def isTUNMode() -> bool:
|
||||
return SystemRuntime.isAdmin() and AppSettings.isStateON_('VPNMode')
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def isScriptMode() -> bool:
|
||||
return sys.argv[0].endswith('.py')
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def isWindows7() -> bool:
|
||||
return PLATFORM == 'Windows' and PLATFORM_RELEASE == '7'
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def isPythonw() -> bool:
|
||||
def isRealFile(file):
|
||||
if not hasattr(file, 'fileno'):
|
||||
return False
|
||||
|
||||
try:
|
||||
tmp = os.dup(file.fileno())
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
return False
|
||||
else:
|
||||
os.close(tmp)
|
||||
|
||||
return True
|
||||
|
||||
# pythonw.exe. Also applies to packed GUI application on Windows
|
||||
return not isRealFile(sys.__stdout__) or not isRealFile(sys.__stderr__)
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache(None)
|
||||
def isWindows7() -> bool:
|
||||
return PLATFORM == 'Windows' and PLATFORM_RELEASE == '7'
|
||||
|
||||
@@ -15,15 +15,7 @@
|
||||
# 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 .Constants import *
|
||||
from .PySide6Legacy import *
|
||||
from .StartupOnBoot import *
|
||||
from .SystemProxy import *
|
||||
from .SystemRoutingTable import *
|
||||
from .SystemRuntime import *
|
||||
|
||||
from .AppMainProcess import *
|
||||
from .AppSettings import *
|
||||
from .AppSettingsFn import *
|
||||
from .Utility import *
|
||||
from .Win32Session import *
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
# 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 Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Storage import *
|
||||
from Furious.Widget.SystemTrayIcon import *
|
||||
from Furious.Window.AppMainWindow import *
|
||||
from Furious.Window.LogViewerWindow import *
|
||||
@@ -32,12 +30,10 @@ from PySide6.QtWidgets import *
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import platform
|
||||
import threading
|
||||
import traceback
|
||||
import functools
|
||||
import qdarkstyle
|
||||
import darkdetect
|
||||
|
||||
@@ -48,34 +44,6 @@ registerAppSettings('CoreLogViewerWidgetPointSize')
|
||||
registerAppSettings('TunLogViewerWidgetPointSize')
|
||||
|
||||
|
||||
def rateLimited(maxCallPerSecond):
|
||||
"""
|
||||
Decorator function that limits the rate at which a function can be called.
|
||||
"""
|
||||
interval = 1.0 / float(maxCallPerSecond)
|
||||
called = time.monotonic()
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# Previously called
|
||||
nonlocal called
|
||||
|
||||
elapsed = time.monotonic() - called
|
||||
waitsec = interval - elapsed
|
||||
|
||||
if waitsec > 0:
|
||||
time.sleep(waitsec)
|
||||
|
||||
result = func(*args, **kwargs)
|
||||
called = time.monotonic()
|
||||
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class SystemTrayUnavailable(Exception):
|
||||
pass
|
||||
|
||||
@@ -178,11 +146,6 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
self.themeDetector = None
|
||||
self.themeListenerThread = None
|
||||
|
||||
# Initialize storage
|
||||
self.userServers = UserServers()
|
||||
self.userSubs = UserSubs()
|
||||
self.userTUNSettings = UserTUNSettings()
|
||||
|
||||
self.mainWindow = None
|
||||
self.systemTray = None
|
||||
|
||||
@@ -190,7 +153,7 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
self.threadPool = QtCore.QThreadPool()
|
||||
self.threadPool.setMaxThreadCount(max(OS_CPU_COUNT // 2, 1))
|
||||
|
||||
@rateLimited(maxCallPerSecond=2)
|
||||
@callRateLimited(maxCallPerSecond=2)
|
||||
@QtCore.Slot()
|
||||
def showExistingApp(self):
|
||||
if isinstance(self.systemTray, SystemTrayIcon):
|
||||
@@ -202,7 +165,7 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
pass
|
||||
|
||||
def configureLogging(self):
|
||||
self.logViewerWindowApp_ = LogViewerWindow(
|
||||
self.logViewerWindowSelf = LogViewerWindow(
|
||||
tabTitle=_('Furious Log'),
|
||||
fontFamily=self.customFontName,
|
||||
pointSizeSettingsName='AppLogViewerWidgetPointSize',
|
||||
@@ -223,16 +186,13 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
level=logging.INFO,
|
||||
handlers=(
|
||||
AppLogHandler(
|
||||
lambda record: self.logViewerWindowApp_.appendLine(record)
|
||||
lambda record: self.logViewerWindowSelf.appendLine(record)
|
||||
),
|
||||
logging.StreamHandler(),
|
||||
),
|
||||
)
|
||||
logging.raiseExceptions = False
|
||||
|
||||
def log(self):
|
||||
return self.logViewerWindowApp_.plainText()
|
||||
|
||||
def addCustomFont(self):
|
||||
fontFile = str(DATA_DIR / 'font' / 'CascadiaMono')
|
||||
fontName = 'Cascadia Mono'
|
||||
@@ -269,20 +229,18 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
def switchToDarkMode(self):
|
||||
self.setStyleSheet(qdarkstyle.load_stylesheet_pyside6())
|
||||
|
||||
SupportThemeChangedCallback.callThemeChangedCallbackUnchecked('Dark')
|
||||
Mixins.ThemeAware.callThemeChangedCallbackUnchecked('Dark')
|
||||
|
||||
def switchToAutoMode(self):
|
||||
self.setStyleSheet('')
|
||||
|
||||
SupportThemeChangedCallback.callThemeChangedCallbackUnchecked(
|
||||
darkdetect.theme()
|
||||
)
|
||||
Mixins.ThemeAware.callThemeChangedCallbackUnchecked(darkdetect.theme())
|
||||
|
||||
@staticmethod
|
||||
@callOnceOnly
|
||||
@QtCore.Slot()
|
||||
def cleanup():
|
||||
SupportExitCleanup.cleanupAll()
|
||||
Mixins.CleanupOnExit.cleanupAll()
|
||||
|
||||
if AppSettings.get('SystemProxyMode') == 'Auto':
|
||||
# Automatically configure
|
||||
@@ -345,7 +303,7 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
# sys.exit(None) in multiprocessing will produce
|
||||
# exitcode 1 in some Python version, which is
|
||||
# not what we want.
|
||||
return ApplicationFactory.ExitCode.ExitSuccess
|
||||
return ApplicationFactory.ExitCode.ExitSuccess.value
|
||||
|
||||
if not SystemTrayIcon.isSystemTrayAvailable():
|
||||
raise SystemTrayUnavailable(
|
||||
@@ -368,24 +326,24 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
if PLATFORM == 'Darwin':
|
||||
logger.info(f'mac_ver: {platform.mac_ver()}')
|
||||
|
||||
appImagePath = getAppImagePath()
|
||||
appImagePath = SystemRuntime.appImagePath()
|
||||
|
||||
if appImagePath:
|
||||
logger.info(f'running from Linux AppImage: \'{appImagePath}\'')
|
||||
else:
|
||||
logger.info('not running from Linux AppImage')
|
||||
|
||||
logger.info(f'python version: {getPythonVersion()}')
|
||||
logger.info(f'python version: {PLATFORM_PYTHON_VERSION}')
|
||||
logger.info(f'system version: {sys.version}')
|
||||
logger.info(f'sys.executable: \'{sys.executable}\'')
|
||||
logger.info(f'sys.argv: {sys.argv}')
|
||||
logger.info(f'appFilePath: \'{self.applicationFilePath()}\'')
|
||||
logger.info(f'isPythonw: {isPythonw()}')
|
||||
logger.info(f'isPythonw: {SystemRuntime.isPythonw()}')
|
||||
logger.info(f'system language is {SYSTEM_LANGUAGE}')
|
||||
logger.info(self.customFontLoadMsg)
|
||||
logger.info(f'current theme is {darkdetect.theme()}')
|
||||
|
||||
if PLATFORM != 'Windows' and not isScriptMode():
|
||||
if PLATFORM != 'Windows' and not SystemRuntime.isScriptMode():
|
||||
logger.info('theme detect method uses timer implementation')
|
||||
|
||||
@QtCore.Slot()
|
||||
@@ -395,9 +353,7 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
if self.currentTheme != currentTheme:
|
||||
self.currentTheme = currentTheme
|
||||
|
||||
SupportThemeChangedCallback.callThemeChangedCallback(
|
||||
currentTheme
|
||||
)
|
||||
Mixins.ThemeAware.callThemeChangedCallback(currentTheme)
|
||||
|
||||
self.currentTheme = darkdetect.theme()
|
||||
self.themeDetectTimer = QtCore.QTimer()
|
||||
@@ -418,7 +374,7 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
|
||||
self.themeDetector = ApplicationThemeDetector()
|
||||
self.themeDetector.themeChanged.connect(
|
||||
SupportThemeChangedCallback.callThemeChangedCallback
|
||||
Mixins.ThemeAware.callThemeChangedCallback
|
||||
)
|
||||
|
||||
self.themeListenerThread = threading.Thread(
|
||||
@@ -471,10 +427,10 @@ class Application(ApplicationFactory, SingletonApplication):
|
||||
|
||||
return self.exec()
|
||||
except SystemTrayUnavailable:
|
||||
return ApplicationFactory.ExitCode.PlatformNotSupported
|
||||
return ApplicationFactory.ExitCode.PlatformNotSupported.value
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
return ApplicationFactory.ExitCode.UnknownException
|
||||
return ApplicationFactory.ExitCode.UnknownException.value
|
||||
|
||||
@@ -15,60 +15,42 @@
|
||||
# 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 Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import QHBoxLayout, QProgressBar, QWidget
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
__all__ = ['ConnectProgressBar']
|
||||
|
||||
|
||||
class ConnectProgressBar(QTranslatable, SupportConnectedCallback, QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
class AutoUpdateProgressBar(Mixins.ConnectionAware, QProgressBar):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.setWindowTitle(_(APPLICATION_NAME))
|
||||
self.setWindowIcon(AppHue.currentWindowIcon())
|
||||
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_ShowWithoutActivating)
|
||||
self.setWindowFlags(QtCore.Qt.WindowType.WindowStaysOnTopHint)
|
||||
self.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
|
||||
self.setFixedSize(280, 61)
|
||||
self.setRange(0, 100)
|
||||
|
||||
@QtCore.Slot()
|
||||
def updateProgressBar():
|
||||
def update():
|
||||
# Update the progress bar value
|
||||
if self._progressBar.value() < 90:
|
||||
self._progressBar.setValue(self._progressBar.value() + 1)
|
||||
if self.value() < 90:
|
||||
self.setValue(self.value() + 1)
|
||||
|
||||
# Stop the timer when the progress bar reaches 100%
|
||||
if self._progressBar.value() >= 100:
|
||||
self._timer.stop()
|
||||
if self.value() > 99:
|
||||
self.timer.stop()
|
||||
|
||||
# Create a progress bar widget
|
||||
self._progressBar = QProgressBar(self)
|
||||
self._progressBar.setRange(0, 100)
|
||||
self._progressBar.setStyleSheet(self.getStyleSheet(AppHue.disconnectedColor()))
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(update)
|
||||
|
||||
# Create a timer to update the progress bar
|
||||
self._timer = QtCore.QTimer(self)
|
||||
self._timer.timeout.connect(updateProgressBar)
|
||||
|
||||
self._layout = QHBoxLayout()
|
||||
self._layout.addWidget(self._progressBar)
|
||||
|
||||
self.setLayout(self._layout)
|
||||
|
||||
def setValue(self, value: int):
|
||||
self._progressBar.setValue(value)
|
||||
self.setStyleSheet(self.getStyleSheet(AppHue.currentColor()))
|
||||
|
||||
def start(self, msec: int):
|
||||
self._timer.start(msec)
|
||||
self.timer.start(msec)
|
||||
|
||||
def stop(self):
|
||||
self._timer.stop()
|
||||
self.timer.stop()
|
||||
|
||||
@staticmethod
|
||||
def getStyleSheet(color):
|
||||
@@ -85,13 +67,44 @@ class ConnectProgressBar(QTranslatable, SupportConnectedCallback, QWidget):
|
||||
f'}}'
|
||||
)
|
||||
|
||||
def disconnectedCallback(self):
|
||||
self.setStyleSheet(self.getStyleSheet(AppHue.disconnectedColor()))
|
||||
|
||||
def connectedCallback(self):
|
||||
self.setStyleSheet(self.getStyleSheet(AppHue.connectedColor()))
|
||||
|
||||
|
||||
class ConnectProgressBar(Mixins.QTranslatable, Mixins.ConnectionAware, QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setWindowTitle(_(APPLICATION_NAME))
|
||||
self.setWindowIcon(AppHue.currentWindowIcon())
|
||||
self.setFixedSize(280, 61)
|
||||
|
||||
# Create a progress bar widget
|
||||
self._widget = AutoUpdateProgressBar(parent=self)
|
||||
self._widget.setRange(0, 100)
|
||||
|
||||
self._layout = QVBoxLayout()
|
||||
self._layout.addWidget(self._widget)
|
||||
|
||||
self.setLayout(self._layout)
|
||||
|
||||
def setValue(self, value: int):
|
||||
self._widget.setValue(value)
|
||||
|
||||
def start(self, msec: int):
|
||||
self._widget.start(msec)
|
||||
|
||||
def stop(self):
|
||||
self._widget.stop()
|
||||
|
||||
def disconnectedCallback(self):
|
||||
self.setWindowIcon(AppHue.disconnectedWindowIcon())
|
||||
self._progressBar.setStyleSheet(self.getStyleSheet(AppHue.disconnectedColor()))
|
||||
|
||||
def connectedCallback(self):
|
||||
self.setWindowIcon(AppHue.connectedWindowIcon())
|
||||
self._progressBar.setStyleSheet(self.getStyleSheet(AppHue.connectedColor()))
|
||||
|
||||
def retranslate(self):
|
||||
self.setWindowTitle(_(self.windowTitle()))
|
||||
|
||||
@@ -17,17 +17,16 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
import logging
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiHysteria1']
|
||||
|
||||
@@ -42,7 +41,7 @@ class GuiHy1ItemTextInput(GuiEditorItemTextInput):
|
||||
|
||||
self.key = key
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldValue = config.get(self.key, '')
|
||||
newValue = self.text()
|
||||
|
||||
@@ -62,7 +61,7 @@ class GuiHy1ItemTextInput(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setText(config.get(self.key, ''))
|
||||
except Exception:
|
||||
@@ -77,7 +76,7 @@ class GuiHy1ItemBasicProtocol(GuiEditorItemTextComboBox):
|
||||
|
||||
self.addItems(['', 'udp', 'wechat-video', 'faketcp'])
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldProtocol = config.get('protocol', '')
|
||||
newProtocol = self.text()
|
||||
|
||||
@@ -93,7 +92,7 @@ class GuiHy1ItemBasicProtocol(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setText(config.get('protocol', 'udp'))
|
||||
except Exception:
|
||||
@@ -109,7 +108,7 @@ class GuiHy1ItemSpeedUpMbps(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 1048576)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldUpMbps = config.get('up_mbps')
|
||||
newUpMbps = self.value()
|
||||
|
||||
@@ -125,7 +124,7 @@ class GuiHy1ItemSpeedUpMbps(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setValue(config.get('up_mbps'))
|
||||
except Exception:
|
||||
@@ -141,7 +140,7 @@ class GuiHy1ItemSpeedDownMbps(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 1048576)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldDownMbps = config.get('down_mbps')
|
||||
newDownMbps = self.value()
|
||||
|
||||
@@ -157,7 +156,7 @@ class GuiHy1ItemSpeedDownMbps(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setValue(config.get('down_mbps'))
|
||||
except Exception:
|
||||
@@ -170,7 +169,7 @@ class GuiHy1ItemTLSInsecure(GuiEditorItemTextCheckBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
try:
|
||||
oldChecked = config.get('insecure')
|
||||
except Exception:
|
||||
@@ -201,7 +200,7 @@ class GuiHy1ItemTLSInsecure(GuiEditorItemTextCheckBox):
|
||||
# Modified silently
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
checked = config['insecure']
|
||||
except Exception:
|
||||
@@ -304,10 +303,10 @@ class GuiHy1GroupBoxOther(GuiEditorItemFactory, AppQGroupBox):
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
pass
|
||||
|
||||
|
||||
@@ -315,7 +314,7 @@ class GuiHysteria1(GuiEditorWidgetQDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setTabText(Protocol.Hysteria1)
|
||||
self.setTabText(Protocol.Hysteria1.value)
|
||||
|
||||
def groupBoxSequence(self):
|
||||
return [
|
||||
|
||||
@@ -17,17 +17,16 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
import logging
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiHysteria2']
|
||||
|
||||
@@ -38,7 +37,7 @@ class GuiHy2ItemBasicServer(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldServer = config.get('server', '')
|
||||
newServer = self.text()
|
||||
|
||||
@@ -54,7 +53,7 @@ class GuiHy2ItemBasicServer(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setText(config.get('server', ''))
|
||||
except Exception:
|
||||
@@ -67,7 +66,7 @@ class GuiHy2ItemBasicAuth(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
oldAuth = config.get('auth')
|
||||
newAuth = self.text()
|
||||
|
||||
@@ -91,7 +90,7 @@ class GuiHy2ItemBasicAuth(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
self.setText(config.get('auth', ''))
|
||||
except Exception:
|
||||
@@ -107,7 +106,7 @@ class GuiHy2ItemObfsType(GuiEditorItemTextComboBox):
|
||||
# TODO: Only 'salamander' is currently supported by hy2
|
||||
self.addItems(['', 'salamander'])
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
try:
|
||||
oldObfsType = config['obfs']['type']
|
||||
except Exception:
|
||||
@@ -140,7 +139,7 @@ class GuiHy2ItemObfsType(GuiEditorItemTextComboBox):
|
||||
else:
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
obfsType = config['obfs']['type']
|
||||
except Exception:
|
||||
@@ -159,7 +158,7 @@ class GuiHy2ItemObfsPassword(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
newObfsPassword = self.text()
|
||||
|
||||
try:
|
||||
@@ -205,7 +204,7 @@ class GuiHy2ItemObfsPassword(GuiEditorItemTextInput):
|
||||
else:
|
||||
return modified
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
obfsType = config['obfs']['type']
|
||||
except Exception:
|
||||
@@ -237,7 +236,7 @@ class GuiHy2ItemTLSTextInput(GuiEditorItemTextInput):
|
||||
|
||||
self.key = key
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
newValue = self.text()
|
||||
|
||||
if newValue == '':
|
||||
@@ -293,7 +292,7 @@ class GuiHy2ItemTLSTextInput(GuiEditorItemTextInput):
|
||||
else:
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
value = config['tls'][self.key]
|
||||
except Exception:
|
||||
@@ -308,7 +307,7 @@ class GuiHy2ItemTLSInsecure(GuiEditorItemTextCheckBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
try:
|
||||
oldChecked = config['tls']['insecure']
|
||||
except Exception:
|
||||
@@ -339,7 +338,7 @@ class GuiHy2ItemTLSInsecure(GuiEditorItemTextCheckBox):
|
||||
else:
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
checked = config['tls']['insecure']
|
||||
except Exception:
|
||||
@@ -444,10 +443,10 @@ class GuiHy2GroupBoxOther(GuiEditorItemFactory, AppQGroupBox):
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
pass
|
||||
|
||||
|
||||
@@ -455,7 +454,7 @@ class GuiHysteria2(GuiEditorWidgetQDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setTabText(Protocol.Hysteria2)
|
||||
self.setTabText(Protocol.Hysteria2.value)
|
||||
|
||||
def groupBoxSequence(self):
|
||||
return [
|
||||
|
||||
@@ -17,71 +17,23 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.GuiVTransport import *
|
||||
from Furious.Widget.GuiVTLS import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiShadowsocks']
|
||||
|
||||
|
||||
def getProxyOutboundObject(config: ConfigurationFactory) -> dict:
|
||||
if not isinstance(config.get('outbounds'), list):
|
||||
config['outbounds'] = []
|
||||
|
||||
for outbound in config['outbounds']:
|
||||
if isinstance(outbound, dict):
|
||||
tag = outbound.get('tag')
|
||||
|
||||
if isinstance(tag, str) and tag == 'proxy':
|
||||
return outbound
|
||||
|
||||
outboundObject = {
|
||||
'tag': 'proxy',
|
||||
'protocol': 'shadowsocks',
|
||||
'settings': {
|
||||
'servers': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'method': '',
|
||||
'password': '',
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
'ota': False,
|
||||
}
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
|
||||
# Proxy outbound not found. Add it
|
||||
config['outbounds'].append(outboundObject)
|
||||
|
||||
return outboundObject
|
||||
|
||||
|
||||
def getProxyOutboundServer(config: ConfigurationFactory) -> dict:
|
||||
proxyOutbound = getProxyOutboundObject(config)
|
||||
|
||||
try:
|
||||
server = proxyOutbound['settings']['servers'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
server = {}
|
||||
|
||||
if not isinstance(server, dict):
|
||||
server = {}
|
||||
|
||||
return server
|
||||
getProxyOutboundServer = functools.partial(
|
||||
ConfigXray.getProxyOutboundServer,
|
||||
protocol=Protocol.Shadowsocks,
|
||||
default=configXrayEmptyProxyOutboundObject(Protocol.Shadowsocks),
|
||||
)
|
||||
|
||||
|
||||
class GuiSSItemTextInput(GuiEditorItemTextInput):
|
||||
@@ -92,7 +44,7 @@ class GuiSSItemTextInput(GuiEditorItemTextInput):
|
||||
|
||||
self.key = key
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldValue = proxyOutboundServer.get(self.key, '')
|
||||
@@ -114,7 +66,7 @@ class GuiSSItemTextInput(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -132,7 +84,7 @@ class GuiSSItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 65535)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldPort = proxyOutboundServer.get('port')
|
||||
@@ -150,7 +102,7 @@ class GuiSSItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -182,7 +134,7 @@ class GuiSSItemBasicMethod(GuiEditorItemTextComboBox):
|
||||
]
|
||||
)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldMethod = proxyOutboundServer.get('method', '')
|
||||
@@ -200,7 +152,7 @@ class GuiSSItemBasicMethod(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -240,7 +192,7 @@ class GuiShadowsocks(GuiEditorWidgetQDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setTabText(Protocol.Shadowsocks)
|
||||
self.setTabText(Protocol.Shadowsocks.value)
|
||||
|
||||
def groupBoxSequence(self):
|
||||
return [
|
||||
|
||||
@@ -17,14 +17,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
from PySide6.QtGui import *
|
||||
|
||||
import logging
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiTUNSettings']
|
||||
|
||||
@@ -164,7 +167,7 @@ class GuiTUNSettings(GuiEditorWidgetQDialog):
|
||||
self.setFixedSize(int(460 * GOLDEN_RATIO), int(460))
|
||||
|
||||
# Shallow copy
|
||||
config = AS_UserTUNSettings()
|
||||
config = Storage.UserTUNSettings()
|
||||
|
||||
try:
|
||||
self.factoryToInput(config)
|
||||
@@ -179,7 +182,7 @@ class GuiTUNSettings(GuiEditorWidgetQDialog):
|
||||
def handleAccepted(self, config: dict):
|
||||
modified = self.inputToFactory(config)
|
||||
|
||||
if modified and isTUNMode():
|
||||
if modified and SystemRuntime.isTUNMode():
|
||||
showNewChangesNextTimeMBox()
|
||||
|
||||
self.accepted.disconnect()
|
||||
|
||||
+16
-62
@@ -17,69 +17,23 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.GuiVTransport import *
|
||||
from Furious.Widget.GuiVTLS import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiTrojan']
|
||||
|
||||
|
||||
def getProxyOutboundObject(config: ConfigurationFactory) -> dict:
|
||||
if not isinstance(config.get('outbounds'), list):
|
||||
config['outbounds'] = []
|
||||
|
||||
for outbound in config['outbounds']:
|
||||
if isinstance(outbound, dict):
|
||||
tag = outbound.get('tag')
|
||||
|
||||
if isinstance(tag, str) and tag == 'proxy':
|
||||
return outbound
|
||||
|
||||
outboundObject = {
|
||||
'tag': 'proxy',
|
||||
'protocol': 'trojan',
|
||||
'settings': {
|
||||
'servers': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'password': '',
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
}
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
|
||||
# Proxy outbound not found. Add it
|
||||
config['outbounds'].append(outboundObject)
|
||||
|
||||
return outboundObject
|
||||
|
||||
|
||||
def getProxyOutboundServer(config: ConfigurationFactory) -> dict:
|
||||
proxyOutbound = getProxyOutboundObject(config)
|
||||
|
||||
try:
|
||||
server = proxyOutbound['settings']['servers'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
server = {}
|
||||
|
||||
if not isinstance(server, dict):
|
||||
server = {}
|
||||
|
||||
return server
|
||||
getProxyOutboundServer = functools.partial(
|
||||
ConfigXray.getProxyOutboundServer,
|
||||
protocol=Protocol.Trojan,
|
||||
default=configXrayEmptyProxyOutboundObject(Protocol.Trojan),
|
||||
)
|
||||
|
||||
|
||||
class GuiTrojanItemTextInput(GuiEditorItemTextInput):
|
||||
@@ -90,7 +44,7 @@ class GuiTrojanItemTextInput(GuiEditorItemTextInput):
|
||||
|
||||
self.key = key
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldValue = proxyOutboundServer.get(self.key, '')
|
||||
@@ -112,7 +66,7 @@ class GuiTrojanItemTextInput(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -130,7 +84,7 @@ class GuiTrojanItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 65535)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldPort = proxyOutboundServer.get('port')
|
||||
@@ -148,7 +102,7 @@ class GuiTrojanItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -187,7 +141,7 @@ class GuiTrojan(GuiEditorWidgetQDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setTabText(Protocol.Trojan)
|
||||
self.setTabText(Protocol.Trojan.value)
|
||||
|
||||
def groupBoxSequence(self):
|
||||
return [
|
||||
|
||||
+27
-86
@@ -17,95 +17,36 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.GuiVTransport import *
|
||||
from Furious.Widget.GuiVTLS import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiVLESS']
|
||||
|
||||
getProxyOutboundServer = functools.partial(
|
||||
ConfigXray.getProxyOutboundServer,
|
||||
protocol=Protocol.VLESS,
|
||||
default=configXrayEmptyProxyOutboundObject(Protocol.VLESS),
|
||||
)
|
||||
|
||||
def getProxyOutboundObject(config: ConfigurationFactory) -> dict:
|
||||
if not isinstance(config.get('outbounds'), list):
|
||||
config['outbounds'] = []
|
||||
|
||||
for outbound in config['outbounds']:
|
||||
if isinstance(outbound, dict):
|
||||
tag = outbound.get('tag')
|
||||
|
||||
if isinstance(tag, str) and tag == 'proxy':
|
||||
return outbound
|
||||
|
||||
outboundObject = {
|
||||
'tag': 'proxy',
|
||||
'protocol': 'vless',
|
||||
'settings': {
|
||||
'vnext': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'users': [
|
||||
{
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
|
||||
# Proxy outbound not found. Add it
|
||||
config['outbounds'].append(outboundObject)
|
||||
|
||||
return outboundObject
|
||||
|
||||
|
||||
def getProxyOutboundServer(config: ConfigurationFactory) -> dict:
|
||||
proxyOutbound = getProxyOutboundObject(config)
|
||||
|
||||
try:
|
||||
server = proxyOutbound['settings']['vnext'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
server = {}
|
||||
|
||||
if not isinstance(server, dict):
|
||||
server = {}
|
||||
|
||||
return server
|
||||
|
||||
|
||||
def getProxyOutboundUser(config: ConfigurationFactory) -> dict:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
try:
|
||||
user = proxyOutboundServer['users'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
user = {}
|
||||
|
||||
if not isinstance(user, dict):
|
||||
user = {}
|
||||
|
||||
return user
|
||||
getProxyOutboundUser = functools.partial(
|
||||
ConfigXray.getProxyOutboundUser,
|
||||
protocol=Protocol.VLESS,
|
||||
default=configXrayEmptyProxyOutboundObject(Protocol.VLESS),
|
||||
)
|
||||
|
||||
|
||||
class GuiVLESSItemBasicAddress(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldAddress = proxyOutboundServer.get('address', '')
|
||||
@@ -127,7 +68,7 @@ class GuiVLESSItemBasicAddress(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -145,7 +86,7 @@ class GuiVLESSItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 65535)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldPort = proxyOutboundServer.get('port')
|
||||
@@ -163,7 +104,7 @@ class GuiVLESSItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -178,7 +119,7 @@ class GuiVLESSItemBasicId(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
oldId = proxyOutboundUser.get('id', '')
|
||||
@@ -200,7 +141,7 @@ class GuiVLESSItemBasicId(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
@@ -215,7 +156,7 @@ class GuiVLESSItemBasicEncryption(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
oldEncryption = proxyOutboundUser.get('encryption', '')
|
||||
@@ -237,7 +178,7 @@ class GuiVLESSItemBasicEncryption(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
@@ -261,7 +202,7 @@ class GuiVLESSItemBasicFlow(GuiEditorItemTextComboBox):
|
||||
]
|
||||
)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
oldFlow = proxyOutboundUser.get('flow', '')
|
||||
@@ -279,7 +220,7 @@ class GuiVLESSItemBasicFlow(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
@@ -320,7 +261,7 @@ class GuiVLESS(GuiEditorWidgetQDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setTabText(Protocol.VLESS)
|
||||
self.setTabText(Protocol.VLESS.value)
|
||||
|
||||
def groupBoxSequence(self):
|
||||
return [
|
||||
|
||||
+27
-86
@@ -17,95 +17,36 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.GuiVTransport import *
|
||||
from Furious.Widget.GuiVTLS import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['GuiVMess']
|
||||
|
||||
getProxyOutboundServer = functools.partial(
|
||||
ConfigXray.getProxyOutboundServer,
|
||||
protocol=Protocol.VMess,
|
||||
default=configXrayEmptyProxyOutboundObject(Protocol.VMess),
|
||||
)
|
||||
|
||||
def getProxyOutboundObject(config: ConfigurationFactory) -> dict:
|
||||
if not isinstance(config.get('outbounds'), list):
|
||||
config['outbounds'] = []
|
||||
|
||||
for outbound in config['outbounds']:
|
||||
if isinstance(outbound, dict):
|
||||
tag = outbound.get('tag')
|
||||
|
||||
if isinstance(tag, str) and tag == 'proxy':
|
||||
return outbound
|
||||
|
||||
outboundObject = {
|
||||
'tag': 'proxy',
|
||||
'protocol': 'vmess',
|
||||
'settings': {
|
||||
'vnext': [
|
||||
{
|
||||
'address': '',
|
||||
'port': 0,
|
||||
'users': [
|
||||
{
|
||||
'email': PROXY_OUTBOUND_USER_EMAIL,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
'streamSettings': {
|
||||
'network': 'tcp',
|
||||
},
|
||||
'mux': {
|
||||
'enabled': False,
|
||||
'concurrency': -1,
|
||||
},
|
||||
}
|
||||
|
||||
# Proxy outbound not found. Add it
|
||||
config['outbounds'].append(outboundObject)
|
||||
|
||||
return outboundObject
|
||||
|
||||
|
||||
def getProxyOutboundServer(config: ConfigurationFactory) -> dict:
|
||||
proxyOutbound = getProxyOutboundObject(config)
|
||||
|
||||
try:
|
||||
server = proxyOutbound['settings']['vnext'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
server = {}
|
||||
|
||||
if not isinstance(server, dict):
|
||||
server = {}
|
||||
|
||||
return server
|
||||
|
||||
|
||||
def getProxyOutboundUser(config: ConfigurationFactory) -> dict:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
try:
|
||||
user = proxyOutboundServer['users'][0]
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
user = {}
|
||||
|
||||
if not isinstance(user, dict):
|
||||
user = {}
|
||||
|
||||
return user
|
||||
getProxyOutboundUser = functools.partial(
|
||||
ConfigXray.getProxyOutboundUser,
|
||||
protocol=Protocol.VMess,
|
||||
default=configXrayEmptyProxyOutboundObject(Protocol.VMess),
|
||||
)
|
||||
|
||||
|
||||
class GuiVMessItemBasicAddress(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldAddress = proxyOutboundServer.get('address', '')
|
||||
@@ -127,7 +68,7 @@ class GuiVMessItemBasicAddress(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -145,7 +86,7 @@ class GuiVMessItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 65535)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
oldPort = proxyOutboundServer.get('port')
|
||||
@@ -163,7 +104,7 @@ class GuiVMessItemBasicPort(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundServer = getProxyOutboundServer(config)
|
||||
|
||||
@@ -178,7 +119,7 @@ class GuiVMessItemBasicId(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
oldId = proxyOutboundUser.get('id', '')
|
||||
@@ -200,7 +141,7 @@ class GuiVMessItemBasicId(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
@@ -218,7 +159,7 @@ class GuiVMessItemBasicAlterId(GuiEditorItemTextSpinBox):
|
||||
# Range
|
||||
self.setRange(0, 65535)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
oldAlterId = proxyOutboundUser.get('alterId')
|
||||
@@ -236,7 +177,7 @@ class GuiVMessItemBasicAlterId(GuiEditorItemTextSpinBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
@@ -262,7 +203,7 @@ class GuiVMessItemBasicSecurity(GuiEditorItemTextComboBox):
|
||||
]
|
||||
)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
oldSecurity = proxyOutboundUser.get('security', '')
|
||||
@@ -280,7 +221,7 @@ class GuiVMessItemBasicSecurity(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
proxyOutboundUser = getProxyOutboundUser(config)
|
||||
|
||||
@@ -321,7 +262,7 @@ class GuiVMess(GuiEditorWidgetQDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setTabText(Protocol.VMess)
|
||||
self.setTabText(Protocol.VMess.value)
|
||||
|
||||
def groupBoxSequence(self):
|
||||
return [
|
||||
|
||||
+30
-31
@@ -18,9 +18,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
@@ -42,8 +41,8 @@ class GuiVTLSItemSecurity(GuiEditorItemTextComboBox):
|
||||
|
||||
self.addItems(STREAM_SECURITY)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
try:
|
||||
oldSecurity = streamSettings['security']
|
||||
@@ -81,9 +80,9 @@ class GuiVTLSItemSecurity(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
self.setText(streamSettings.get('security', ''))
|
||||
except Exception:
|
||||
@@ -101,8 +100,8 @@ class GuiVTLSItemXXXServerName(GuiEditorItemTextInput):
|
||||
|
||||
self.securityKey = securityKey
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get(self.securityKey), dict):
|
||||
streamSettings[self.securityKey] = {}
|
||||
@@ -136,9 +135,9 @@ class GuiVTLSItemXXXServerName(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xxxObject = getXrayProxyOutboundStream(config)[self.securityKey]
|
||||
xxxObject = ConfigXray.getProxyOutboundStream(config)[self.securityKey]
|
||||
|
||||
self.setText(xxxObject.get('serverName', ''))
|
||||
except Exception:
|
||||
@@ -170,8 +169,8 @@ class GuiVTLSItemXXXFingerprint(GuiEditorItemTextInput):
|
||||
|
||||
self.securityKey = securityKey
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get(self.securityKey), dict):
|
||||
streamSettings[self.securityKey] = {}
|
||||
@@ -205,9 +204,9 @@ class GuiVTLSItemXXXFingerprint(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xxxObject = getXrayProxyOutboundStream(config)[self.securityKey]
|
||||
xxxObject = ConfigXray.getProxyOutboundStream(config)[self.securityKey]
|
||||
|
||||
self.setText(xxxObject.get('fingerprint', ''))
|
||||
except Exception:
|
||||
@@ -234,8 +233,8 @@ class GuiVTLSItemTLSAlpn(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('tlsSettings'), dict):
|
||||
streamSettings['tlsSettings'] = {}
|
||||
@@ -269,9 +268,9 @@ class GuiVTLSItemTLSAlpn(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
tlsObject = getXrayProxyOutboundStream(config)['tlsSettings']
|
||||
tlsObject = ConfigXray.getProxyOutboundStream(config)['tlsSettings']
|
||||
|
||||
self.setText(','.join(tlsObject.get('alpn', [])))
|
||||
except Exception:
|
||||
@@ -284,8 +283,8 @@ class GuiVTLSItemTLSAllowInsecure(GuiEditorItemTextCheckBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('tlsSettings'), dict):
|
||||
streamSettings['tlsSettings'] = {}
|
||||
@@ -316,9 +315,9 @@ class GuiVTLSItemTLSAllowInsecure(GuiEditorItemTextCheckBox):
|
||||
else:
|
||||
return False
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
tlsObject = getXrayProxyOutboundStream(config)['tlsSettings']
|
||||
tlsObject = ConfigXray.getProxyOutboundStream(config)['tlsSettings']
|
||||
|
||||
self.setChecked(tlsObject.get('allowInsecure', False))
|
||||
except Exception:
|
||||
@@ -336,8 +335,8 @@ class GuiVTLSItemRealityXXX(GuiEditorItemTextInput):
|
||||
|
||||
self.realityKey = realityKey
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('realitySettings'), dict):
|
||||
streamSettings['realitySettings'] = {}
|
||||
@@ -371,9 +370,9 @@ class GuiVTLSItemRealityXXX(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
realityObject = getXrayProxyOutboundStream(config)['realitySettings']
|
||||
realityObject = ConfigXray.getProxyOutboundStream(config)['realitySettings']
|
||||
|
||||
self.setText(realityObject.get(self.realityKey, ''))
|
||||
except Exception:
|
||||
@@ -498,7 +497,7 @@ class GuiVTLSQGroupBox(GuiEditorItemFactory, AppQGroupBox):
|
||||
|
||||
super().__init__('TLS', **kwargs, translatable=translatable)
|
||||
|
||||
self._config = ConfigurationFactory()
|
||||
self._config = ConfigFactory()
|
||||
|
||||
self._widget = GuiVTLSPageStackedWidget()
|
||||
self._widget.connectActivated(self.handleActivated)
|
||||
@@ -524,14 +523,14 @@ class GuiVTLSQGroupBox(GuiEditorItemFactory, AppQGroupBox):
|
||||
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
return self.page(self.currentIndex()).inputToFactory(config)
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
# Shallow copy
|
||||
self._config = config
|
||||
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
security = streamSettings.get('security', '')
|
||||
|
||||
if not isinstance(security, str):
|
||||
|
||||
@@ -19,10 +19,8 @@ from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.Library import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Widget.GuiVTLS import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
@@ -49,8 +47,8 @@ class GuiVTransportItemNetwork(GuiEditorItemTextComboBox):
|
||||
|
||||
self.addItems(STREAM_NETWORK)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
try:
|
||||
oldNetwork = streamSettings['network']
|
||||
@@ -88,9 +86,9 @@ class GuiVTransportItemNetwork(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
self.setText(streamSettings.get('network', ''))
|
||||
except Exception:
|
||||
@@ -107,8 +105,8 @@ class GuiVTransportItemTypeXXX(GuiEditorItemTextComboBox):
|
||||
|
||||
self.networkKey = networkKey
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get(self.networkKey), dict):
|
||||
streamSettings[self.networkKey] = {}
|
||||
@@ -145,9 +143,9 @@ class GuiVTransportItemTypeXXX(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xxxObject = getXrayProxyOutboundStream(config)[self.networkKey]
|
||||
xxxObject = ConfigXray.getProxyOutboundStream(config)[self.networkKey]
|
||||
|
||||
self.setText(xxxObject['header']['type'])
|
||||
except Exception:
|
||||
@@ -175,8 +173,8 @@ class GuiVTransportItemHostTcp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('tcpSettings'), dict):
|
||||
streamSettings['tcpSettings'] = {}
|
||||
@@ -219,9 +217,9 @@ class GuiVTransportItemHostTcp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
tcpObject = getXrayProxyOutboundStream(config)['tcpSettings']
|
||||
tcpObject = ConfigXray.getProxyOutboundStream(config)['tcpSettings']
|
||||
|
||||
self.setText(','.join(tcpObject['header']['request']['headers']['Host']))
|
||||
except Exception:
|
||||
@@ -234,8 +232,8 @@ class GuiVTransportItemPathTcp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('tcpSettings'), dict):
|
||||
streamSettings['tcpSettings'] = {}
|
||||
@@ -275,9 +273,9 @@ class GuiVTransportItemPathTcp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
tcpObject = getXrayProxyOutboundStream(config)['tcpSettings']
|
||||
tcpObject = ConfigXray.getProxyOutboundStream(config)['tcpSettings']
|
||||
|
||||
self.setText(','.join(tcpObject['header']['request']['path']))
|
||||
except Exception:
|
||||
@@ -310,8 +308,8 @@ class GuiVTransportItemSeedKcp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('kcpSettings'), dict):
|
||||
streamSettings['kcpSettings'] = {}
|
||||
@@ -345,9 +343,9 @@ class GuiVTransportItemSeedKcp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
kcpObject = getXrayProxyOutboundStream(config)['kcpSettings']
|
||||
kcpObject = ConfigXray.getProxyOutboundStream(config)['kcpSettings']
|
||||
|
||||
self.setText(kcpObject.get('seed', ''))
|
||||
except Exception:
|
||||
@@ -360,8 +358,8 @@ class GuiVTransportItemHostWs(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('wsSettings'), dict):
|
||||
streamSettings['wsSettings'] = {}
|
||||
@@ -398,9 +396,9 @@ class GuiVTransportItemHostWs(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
wsObject = getXrayProxyOutboundStream(config)['wsSettings']
|
||||
wsObject = ConfigXray.getProxyOutboundStream(config)['wsSettings']
|
||||
|
||||
self.setText(wsObject['headers']['Host'])
|
||||
except Exception:
|
||||
@@ -413,8 +411,8 @@ class GuiVTransportItemPathWs(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('wsSettings'), dict):
|
||||
streamSettings['wsSettings'] = {}
|
||||
@@ -448,9 +446,9 @@ class GuiVTransportItemPathWs(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
wsObject = getXrayProxyOutboundStream(config)['wsSettings']
|
||||
wsObject = ConfigXray.getProxyOutboundStream(config)['wsSettings']
|
||||
|
||||
self.setText(wsObject.get('path', ''))
|
||||
except Exception:
|
||||
@@ -463,8 +461,8 @@ class GuiVTransportItemHostHttpUpgrade(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('httpupgradeSettings'), dict):
|
||||
streamSettings['httpupgradeSettings'] = {}
|
||||
@@ -498,9 +496,9 @@ class GuiVTransportItemHostHttpUpgrade(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
httpUpgradeObject = getXrayProxyOutboundStream(config)[
|
||||
httpUpgradeObject = ConfigXray.getProxyOutboundStream(config)[
|
||||
'httpupgradeSettings'
|
||||
]
|
||||
|
||||
@@ -515,8 +513,8 @@ class GuiVTransportItemPathHttpUpgrade(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('httpupgradeSettings'), dict):
|
||||
streamSettings['httpupgradeSettings'] = {}
|
||||
@@ -550,9 +548,9 @@ class GuiVTransportItemPathHttpUpgrade(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
httpUpgradeObject = getXrayProxyOutboundStream(config)[
|
||||
httpUpgradeObject = ConfigXray.getProxyOutboundStream(config)[
|
||||
'httpupgradeSettings'
|
||||
]
|
||||
|
||||
@@ -567,8 +565,8 @@ class GuiVTransportItemHostSplitHttp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('splithttpSettings'), dict):
|
||||
streamSettings['splithttpSettings'] = {}
|
||||
@@ -602,9 +600,11 @@ class GuiVTransportItemHostSplitHttp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
splitHttpObject = getXrayProxyOutboundStream(config)['splithttpSettings']
|
||||
splitHttpObject = ConfigXray.getProxyOutboundStream(config)[
|
||||
'splithttpSettings'
|
||||
]
|
||||
|
||||
self.setText(splitHttpObject.get('host', ''))
|
||||
except Exception:
|
||||
@@ -617,8 +617,8 @@ class GuiVTransportItemPathSplitHttp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('splithttpSettings'), dict):
|
||||
streamSettings['splithttpSettings'] = {}
|
||||
@@ -652,9 +652,11 @@ class GuiVTransportItemPathSplitHttp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
splitHttpObject = getXrayProxyOutboundStream(config)['splithttpSettings']
|
||||
splitHttpObject = ConfigXray.getProxyOutboundStream(config)[
|
||||
'splithttpSettings'
|
||||
]
|
||||
|
||||
self.setText(splitHttpObject.get('path', ''))
|
||||
except Exception:
|
||||
@@ -667,8 +669,8 @@ class GuiVTransportItemHostXHttp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||||
streamSettings['xhttpSettings'] = {}
|
||||
@@ -702,9 +704,9 @@ class GuiVTransportItemHostXHttp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||||
xhttpObject = ConfigXray.getProxyOutboundStream(config)['xhttpSettings']
|
||||
|
||||
self.setText(xhttpObject.get('host', ''))
|
||||
except Exception:
|
||||
@@ -717,8 +719,8 @@ class GuiVTransportItemPathXHttp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||||
streamSettings['xhttpSettings'] = {}
|
||||
@@ -752,9 +754,9 @@ class GuiVTransportItemPathXHttp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||||
xhttpObject = ConfigXray.getProxyOutboundStream(config)['xhttpSettings']
|
||||
|
||||
self.setText(xhttpObject.get('path', ''))
|
||||
except Exception:
|
||||
@@ -776,8 +778,8 @@ class GuiVTransportItemModeXHttp(GuiEditorItemTextComboBox):
|
||||
]
|
||||
)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||||
streamSettings['xhttpSettings'] = {}
|
||||
@@ -811,9 +813,9 @@ class GuiVTransportItemModeXHttp(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||||
xhttpObject = ConfigXray.getProxyOutboundStream(config)['xhttpSettings']
|
||||
|
||||
self.setText(xhttpObject.get('mode', ''))
|
||||
except Exception:
|
||||
@@ -826,8 +828,8 @@ class GuiVTransportItemExtraXHttp(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||||
streamSettings['xhttpSettings'] = {}
|
||||
@@ -869,9 +871,9 @@ class GuiVTransportItemExtraXHttp(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||||
xhttpObject = ConfigXray.getProxyOutboundStream(config)['xhttpSettings']
|
||||
|
||||
if xhttpObject.get('extra', ''):
|
||||
self.setText(UJSONEncoder.encode(xhttpObject.get('extra', '')))
|
||||
@@ -887,8 +889,8 @@ class GuiVTransportItemHostH2(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('httpSettings'), dict):
|
||||
streamSettings['httpSettings'] = {}
|
||||
@@ -922,9 +924,9 @@ class GuiVTransportItemHostH2(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
httpObject = getXrayProxyOutboundStream(config)['httpSettings']
|
||||
httpObject = ConfigXray.getProxyOutboundStream(config)['httpSettings']
|
||||
|
||||
self.setText(','.join(httpObject.get('host', [])))
|
||||
except Exception:
|
||||
@@ -937,8 +939,8 @@ class GuiVTransportItemPathH2(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('httpSettings'), dict):
|
||||
streamSettings['httpSettings'] = {}
|
||||
@@ -972,9 +974,9 @@ class GuiVTransportItemPathH2(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
httpObject = getXrayProxyOutboundStream(config)['httpSettings']
|
||||
httpObject = ConfigXray.getProxyOutboundStream(config)['httpSettings']
|
||||
|
||||
self.setText(httpObject.get('path', ''))
|
||||
except Exception:
|
||||
@@ -1015,8 +1017,8 @@ class GuiVTransportItemSecurityQuic(GuiEditorItemTextComboBox):
|
||||
]
|
||||
)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('quicSettings'), dict):
|
||||
streamSettings['quicSettings'] = {}
|
||||
@@ -1047,9 +1049,9 @@ class GuiVTransportItemSecurityQuic(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
quicObject = getXrayProxyOutboundStream(config)['quicSettings']
|
||||
quicObject = ConfigXray.getProxyOutboundStream(config)['quicSettings']
|
||||
|
||||
self.setText(quicObject.get('security', ''))
|
||||
except Exception:
|
||||
@@ -1062,8 +1064,8 @@ class GuiVTransportItemKeyQuic(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('quicSettings'), dict):
|
||||
streamSettings['quicSettings'] = {}
|
||||
@@ -1097,9 +1099,9 @@ class GuiVTransportItemKeyQuic(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
quicObject = getXrayProxyOutboundStream(config)['quicSettings']
|
||||
quicObject = ConfigXray.getProxyOutboundStream(config)['quicSettings']
|
||||
|
||||
self.setText(quicObject.get('key', ''))
|
||||
except Exception:
|
||||
@@ -1120,8 +1122,8 @@ class GuiVTransportItemModeGRPC(GuiEditorItemTextComboBox):
|
||||
]
|
||||
)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('grpcSettings'), dict):
|
||||
streamSettings['grpcSettings'] = {}
|
||||
@@ -1160,9 +1162,9 @@ class GuiVTransportItemModeGRPC(GuiEditorItemTextComboBox):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
grpcObject = getXrayProxyOutboundStream(config)['grpcSettings']
|
||||
grpcObject = ConfigXray.getProxyOutboundStream(config)['grpcSettings']
|
||||
|
||||
multi = grpcObject.get('multiMode', False)
|
||||
|
||||
@@ -1182,8 +1184,8 @@ class GuiVTransportItemAuthorityGRPC(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('grpcSettings'), dict):
|
||||
streamSettings['grpcSettings'] = {}
|
||||
@@ -1217,9 +1219,9 @@ class GuiVTransportItemAuthorityGRPC(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
grpcObject = getXrayProxyOutboundStream(config)['grpcSettings']
|
||||
grpcObject = ConfigXray.getProxyOutboundStream(config)['grpcSettings']
|
||||
|
||||
self.setText(grpcObject.get('authority', ''))
|
||||
except Exception:
|
||||
@@ -1232,8 +1234,8 @@ class GuiVTransportItemServiceNameGRPC(GuiEditorItemTextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
|
||||
if not isinstance(streamSettings.get('grpcSettings'), dict):
|
||||
streamSettings['grpcSettings'] = {}
|
||||
@@ -1267,9 +1269,9 @@ class GuiVTransportItemServiceNameGRPC(GuiEditorItemTextInput):
|
||||
|
||||
return True
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
try:
|
||||
grpcObject = getXrayProxyOutboundStream(config)['grpcSettings']
|
||||
grpcObject = ConfigXray.getProxyOutboundStream(config)['grpcSettings']
|
||||
|
||||
self.setText(grpcObject.get('serviceName', ''))
|
||||
except Exception:
|
||||
@@ -1442,7 +1444,7 @@ class GuiVTransportQGroupBox(GuiEditorItemFactory, AppQGroupBox):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(_('Transport'), **kwargs)
|
||||
|
||||
self._config = ConfigurationFactory()
|
||||
self._config = ConfigFactory()
|
||||
|
||||
self._widget = GuiVTransportPageStackedWidget()
|
||||
self._widget.connectActivated(self.handleActivated)
|
||||
@@ -1468,14 +1470,14 @@ class GuiVTransportQGroupBox(GuiEditorItemFactory, AppQGroupBox):
|
||||
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||||
def inputToFactory(self, config: ConfigFactory) -> bool:
|
||||
return self.page(self.currentIndex()).inputToFactory(config)
|
||||
|
||||
def factoryToInput(self, config: ConfigurationFactory):
|
||||
def factoryToInput(self, config: ConfigFactory):
|
||||
# Shallow copy
|
||||
self._config = config
|
||||
|
||||
streamSettings = getXrayProxyOutboundStream(config)
|
||||
streamSettings = ConfigXray.getProxyOutboundStream(config)
|
||||
network = streamSettings.get('network', '')
|
||||
|
||||
if not isinstance(network, str):
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['IndentSpinBox']
|
||||
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
# 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 Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.TrayActions import *
|
||||
|
||||
@@ -34,9 +34,9 @@ __all__ = ['SystemTrayIcon']
|
||||
|
||||
|
||||
class SystemTrayIcon(
|
||||
QTranslatable,
|
||||
SupportConnectedCallback,
|
||||
SupportThemeChangedCallback,
|
||||
Mixins.QTranslatable,
|
||||
Mixins.ConnectionAware,
|
||||
Mixins.ThemeAware,
|
||||
QSystemTrayIcon,
|
||||
):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -150,7 +150,7 @@ class SystemTrayIcon(
|
||||
|
||||
if (
|
||||
PLATFORM == 'Darwin'
|
||||
or isWindows7()
|
||||
or SystemRuntime.isWindows7()
|
||||
or (PLATFORM == 'Windows' and darkdetect.theme() == 'Light')
|
||||
):
|
||||
# Darker
|
||||
@@ -164,12 +164,12 @@ class SystemTrayIcon(
|
||||
|
||||
return
|
||||
|
||||
if isAdministrator():
|
||||
if SystemRuntime.isAdmin():
|
||||
self.setIcon(bootstrapIcon('rocket-takeoff-admin-connected.svg'))
|
||||
else:
|
||||
if (
|
||||
PLATFORM == 'Darwin'
|
||||
or isWindows7()
|
||||
or SystemRuntime.isWindows7()
|
||||
or (PLATFORM == 'Windows' and darkdetect.theme() == 'Light')
|
||||
):
|
||||
# Darker
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,12 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
@@ -76,7 +74,7 @@ class UserSubsAppQComboBox(AppQComboBox):
|
||||
|
||||
def retranslate(self):
|
||||
# Do not emit 'currentTextChanged' when retranslate
|
||||
with QBlockSignals(self):
|
||||
with Mixins.QBlockSignalContext(self):
|
||||
super().retranslate()
|
||||
|
||||
|
||||
@@ -105,7 +103,7 @@ TRANSLATABLE_HEADERS = [
|
||||
]
|
||||
|
||||
|
||||
class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
class UserSubsQTableWidget(Mixins.QTranslatable, AppQTableWidget):
|
||||
AutoUpdateOptions = {
|
||||
'': None,
|
||||
'Never': None,
|
||||
@@ -126,7 +124,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
ProxyOptions = {
|
||||
'': lambda: None,
|
||||
'Use current proxy': lambda: connectedHttpProxyEndpoint(),
|
||||
'Use current proxy': lambda: Storage.Extras.UserHttpProxy(),
|
||||
'Force proxy': lambda: '127.0.0.1:10809',
|
||||
'No proxy': lambda: None,
|
||||
}
|
||||
@@ -150,7 +148,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.timers = list(QtCore.QTimer() for i in range(len(AS_UserSubscription())))
|
||||
self.timers = list(QtCore.QTimer() for i in range(len(Storage.UserSubs())))
|
||||
|
||||
# Must set before flush all
|
||||
self.setColumnCount(len(self.Headers))
|
||||
@@ -200,7 +198,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
@QtCore.Slot(QTableWidgetItem)
|
||||
def handleItemChanged(self, item: QTableWidgetItem):
|
||||
unique = list(AS_UserSubscription().keys())[item.row()]
|
||||
unique = list(Storage.UserSubs().keys())[item.row()]
|
||||
mapped = UserSubsQTableWidget.ItemKey[item.column()]
|
||||
|
||||
# 'autoupdate', 'proxy' is not triggered here
|
||||
@@ -208,7 +206,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
item.setToolTip(item.text())
|
||||
|
||||
AS_UserSubscription()[unique][mapped] = item.text()
|
||||
Storage.UserSubs()[unique][mapped] = item.text()
|
||||
|
||||
@QtCore.Slot(QtCore.QPoint)
|
||||
def handleCustomContextMenuRequested(self, point):
|
||||
@@ -222,14 +220,16 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
return
|
||||
|
||||
def handleResultCode(_indexes, code):
|
||||
if code == PySide6LegacyEnumValueWrapper(AppQMessageBox.StandardButton.Yes):
|
||||
if code == PySide6Legacy.enumValueWrapper(
|
||||
AppQMessageBox.StandardButton.Yes
|
||||
):
|
||||
for i in range(len(_indexes)):
|
||||
deleteIndex = _indexes[i] - i
|
||||
deleteUnique = list(AS_UserSubscription().keys())[deleteIndex]
|
||||
deleteUnique = list(Storage.UserSubs().keys())[deleteIndex]
|
||||
|
||||
self.removeRow(deleteIndex)
|
||||
|
||||
AS_UserSubscription().pop(deleteUnique)
|
||||
Storage.UserSubs().pop(deleteUnique)
|
||||
|
||||
# Begin timer cleanup
|
||||
qtimer = self.timers[deleteIndex]
|
||||
@@ -277,8 +277,8 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
def handleAutoUpdateComboBoxCurrentTextChanged(self, text: str, row: int):
|
||||
textEnglish = _(text, 'EN')
|
||||
|
||||
unique = list(AS_UserSubscription().keys())[row]
|
||||
subsob = AS_UserSubscription()[unique]
|
||||
unique = list(Storage.UserSubs().keys())[row]
|
||||
subsob = Storage.UserSubs()[unique]
|
||||
|
||||
remark, webURL = subsob['remark'], subsob['webURL']
|
||||
|
||||
@@ -346,8 +346,8 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
def handleProxyComboBoxCurrentTextChanged(self, text: str, row: int):
|
||||
textEnglish = _(text, 'EN')
|
||||
|
||||
unique = list(AS_UserSubscription().keys())[row]
|
||||
subsob = AS_UserSubscription()[unique]
|
||||
unique = list(Storage.UserSubs().keys())[row]
|
||||
subsob = Storage.UserSubs()[unique]
|
||||
|
||||
try:
|
||||
proxyFn = self.ProxyOptions[textEnglish]
|
||||
@@ -380,7 +380,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
if oldItem is None:
|
||||
# Item does not exist
|
||||
newItem.setFont(QFont(APP().customFontName))
|
||||
newItem.setFont(QFont(AppFontName()))
|
||||
else:
|
||||
# Use existing
|
||||
newItem.setFont(oldItem.font())
|
||||
@@ -409,7 +409,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
if oldItem is None:
|
||||
# Item does not exist
|
||||
newItem.setFont(QFont(APP().customFontName))
|
||||
newItem.setFont(QFont(AppFontName()))
|
||||
else:
|
||||
# Use existing
|
||||
newItem.setFont(oldItem.font())
|
||||
@@ -439,7 +439,7 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
if oldItem is None:
|
||||
# Item does not exist
|
||||
newItem.setFont(QFont(APP().customFontName))
|
||||
newItem.setFont(QFont(AppFontName()))
|
||||
else:
|
||||
# Use existing
|
||||
newItem.setFont(oldItem.font())
|
||||
@@ -464,12 +464,12 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
def flushAll(self):
|
||||
if self.rowCount() == 0:
|
||||
# Should insert row
|
||||
for index, key in enumerate(AS_UserSubscription()):
|
||||
for index, key in enumerate(Storage.UserSubs()):
|
||||
self.insertRow(index)
|
||||
self.flushRow(index, AS_UserSubscription()[key])
|
||||
self.flushRow(index, Storage.UserSubs()[key])
|
||||
else:
|
||||
for index, key in enumerate(AS_UserSubscription()):
|
||||
self.flushRow(index, AS_UserSubscription()[key])
|
||||
for index, key in enumerate(Storage.UserSubs()):
|
||||
self.flushRow(index, Storage.UserSubs()[key])
|
||||
|
||||
def appendNewItem(self, **kwargs):
|
||||
unique, remark, webURL, autoupdate, proxy = (
|
||||
@@ -493,11 +493,11 @@ class UserSubsQTableWidget(QTranslatable, AppQTableWidget):
|
||||
|
||||
# 'unique' should be unique in subscription storage,
|
||||
# but protect it anyway.
|
||||
if unique not in AS_UserSubscription():
|
||||
if unique not in Storage.UserSubs():
|
||||
# Add timer
|
||||
self.timers.append(QtCore.QTimer())
|
||||
|
||||
AS_UserSubscription().update(subsob)
|
||||
Storage.UserSubs().update(subsob)
|
||||
|
||||
row = self.rowCount()
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
@@ -55,7 +55,7 @@ class AssetExistsMBox(AppQMessageBox):
|
||||
self.moveToCenter()
|
||||
|
||||
|
||||
class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
class XrayAssetViewerQListWidget(Mixins.ThemeAware, AppQListWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -65,7 +65,7 @@ class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
self.setSelectionMode(AppQListWidget.SelectionMode.ExtendedSelection)
|
||||
self.setIconSize(QtCore.QSize(64, 64))
|
||||
|
||||
if PLATFORM == 'Linux' and getUbuntuRelease() == '20.04':
|
||||
if PLATFORM == 'Linux' and SystemRuntime.ubuntuRelease() == '20.04':
|
||||
self.initialTheme = darkdetect.theme()
|
||||
else:
|
||||
self.initialTheme = None
|
||||
@@ -117,7 +117,7 @@ class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
)
|
||||
|
||||
item = QListWidgetItem(f'{filename:{maxlen + 6}}{mdate}')
|
||||
item.setFont(QFont(APP().customFontName))
|
||||
item.setFont(QFont(AppFontName()))
|
||||
|
||||
if AppSettings.isStateON_('DarkMode'):
|
||||
# Custom dark mode
|
||||
@@ -143,7 +143,7 @@ class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
self.addItem(item)
|
||||
|
||||
def flushItem(self):
|
||||
if PLATFORM == 'Linux' and getUbuntuRelease() == '20.04':
|
||||
if PLATFORM == 'Linux' and SystemRuntime.ubuntuRelease() == '20.04':
|
||||
assert self.initialTheme is not None
|
||||
|
||||
# Ubuntu 20.04. Flush by initial theme(Ubuntu 20.04 theme changes bug)
|
||||
@@ -183,7 +183,7 @@ class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
if os.path.isfile(XRAY_ASSET_DIR / basename):
|
||||
|
||||
def handleResultCode(_filename, code):
|
||||
if code == PySide6LegacyEnumValueWrapper(
|
||||
if code == PySide6Legacy.enumValueWrapper(
|
||||
AppQMessageBox.StandardButton.Yes
|
||||
):
|
||||
append(_filename)
|
||||
@@ -210,7 +210,9 @@ class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
return
|
||||
|
||||
def handleResultCode(_indexes, code):
|
||||
if code == PySide6LegacyEnumValueWrapper(AppQMessageBox.StandardButton.Yes):
|
||||
if code == PySide6Legacy.enumValueWrapper(
|
||||
AppQMessageBox.StandardButton.Yes
|
||||
):
|
||||
for index in _indexes:
|
||||
os.remove(XRAY_ASSET_DIR / self.item(index).text())
|
||||
|
||||
@@ -244,7 +246,7 @@ class XrayAssetViewerQListWidget(SupportThemeChangedCallback, AppQListWidget):
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def themeChangedCallback(self, theme):
|
||||
if PLATFORM == 'Linux' and getUbuntuRelease() == '20.04':
|
||||
if PLATFORM == 'Linux' and SystemRuntime.ubuntuRelease() == '20.04':
|
||||
# Ubuntu 20.04 system dark theme does not
|
||||
# change menu color. Do nothing
|
||||
pass
|
||||
|
||||
@@ -17,21 +17,19 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.UserServersQTableWidget import *
|
||||
from Furious.Widget.GuiTUNSettings import *
|
||||
from Furious.Window.UserSubsWindow import *
|
||||
from Furious.Window.LogViewerWindow import *
|
||||
from Furious.Window.XrayAssetViewerWindow import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtWidgets import *
|
||||
from PySide6.QtNetwork import *
|
||||
|
||||
from typing import Union
|
||||
|
||||
@@ -78,7 +76,7 @@ class AppNetworkStateManager(NetworkStateManager):
|
||||
|
||||
self.stopTest()
|
||||
|
||||
connectedHttpProxy = connectedHttpProxyEndpoint()
|
||||
connectedHttpProxy = Storage.Extras.UserHttpProxy()
|
||||
|
||||
if connectedHttpProxy is None:
|
||||
parent = self.parent()
|
||||
@@ -121,7 +119,7 @@ class AppMainWindow(AppQMainWindow):
|
||||
deleteUniqueCallback=lambda unique: self.userServersQTableWidget.deleteItemByIndex(
|
||||
list(
|
||||
index
|
||||
for index, server in enumerate(AS_UserServers())
|
||||
for index, server in enumerate(Storage.UserServers())
|
||||
if server.getExtras('subsId') == unique
|
||||
)
|
||||
),
|
||||
@@ -134,7 +132,7 @@ class AppMainWindow(AppQMainWindow):
|
||||
logActions = [
|
||||
AppQAction(
|
||||
_('Show Furious Log...'),
|
||||
callback=lambda: loggerApp_().showMaximized(),
|
||||
callback=lambda: AppLoggerWindow.Self().showMaximized(),
|
||||
shortcut=QtCore.QKeyCombination(
|
||||
QtCore.Qt.KeyboardModifier.ControlModifier
|
||||
| QtCore.Qt.KeyboardModifier.ShiftModifier,
|
||||
@@ -143,7 +141,7 @@ class AppMainWindow(AppQMainWindow):
|
||||
),
|
||||
AppQAction(
|
||||
_('Show Core Log...'),
|
||||
callback=lambda: loggerCore().showMaximized(),
|
||||
callback=lambda: AppLoggerWindow.Core().showMaximized(),
|
||||
shortcut=QtCore.QKeyCombination(
|
||||
QtCore.Qt.KeyboardModifier.ControlModifier
|
||||
| QtCore.Qt.KeyboardModifier.ShiftModifier,
|
||||
@@ -152,7 +150,7 @@ class AppMainWindow(AppQMainWindow):
|
||||
),
|
||||
AppQAction(
|
||||
_('Show Tun2socks Log...'),
|
||||
callback=lambda: loggerTun_().showMaximized(),
|
||||
callback=lambda: AppLoggerWindow.TUN_().showMaximized(),
|
||||
shortcut=QtCore.QKeyCombination(
|
||||
QtCore.Qt.KeyboardModifier.ControlModifier
|
||||
| QtCore.Qt.KeyboardModifier.ShiftModifier,
|
||||
@@ -210,7 +208,7 @@ class AppMainWindow(AppQMainWindow):
|
||||
AppQAction(
|
||||
_('Update Subscription (Use Current Proxy)'),
|
||||
callback=lambda: self.userServersQTableWidget.updateSubs(
|
||||
connectedHttpProxyEndpoint(),
|
||||
Storage.Extras.UserHttpProxy(),
|
||||
parent=self,
|
||||
),
|
||||
),
|
||||
@@ -408,10 +406,10 @@ class AppMainWindow(AppQMainWindow):
|
||||
def updateSubsByUnique(self, unique: str, httpProxy: Union[str, None], **kwargs):
|
||||
self.userServersQTableWidget.updateSubsByUnique(unique, httpProxy, **kwargs)
|
||||
|
||||
def appendNewItemByFactory(self, factory: ConfigurationFactory):
|
||||
def appendNewItemByFactory(self, factory: ConfigFactory):
|
||||
self.userServersQTableWidget.appendNewItemByFactory(factory)
|
||||
|
||||
def flushRow(self, row: int, item: ConfigurationFactory):
|
||||
def flushRow(self, row: int, item: ConfigFactory):
|
||||
self.userServersQTableWidget.flushRow(row, item)
|
||||
|
||||
def showTabAndSpaces(self):
|
||||
@@ -428,19 +426,19 @@ class AppMainWindow(AppQMainWindow):
|
||||
return GuiTUNSettings(parent=parent, **kwargs)
|
||||
|
||||
guiTUNSettings = cachedGuiTUNSettings()
|
||||
guiTUNSettings.factoryToInput(AS_UserTUNSettings())
|
||||
guiTUNSettings.factoryToInput(Storage.UserTUNSettings())
|
||||
|
||||
return guiTUNSettings
|
||||
|
||||
def checkForUpdates(self, **kwargs):
|
||||
self.updatesManager.configureHttpProxy(connectedHttpProxyEndpoint())
|
||||
self.updatesManager.configureHttpProxy(Storage.Extras.UserHttpProxy())
|
||||
self.updatesManager.checkForUpdates(**kwargs)
|
||||
|
||||
def resetNetworkState(self):
|
||||
self.networkState.setText('')
|
||||
|
||||
def setNetworkState(self, success: bool, **kwargs):
|
||||
remark = connectedServerRemark()
|
||||
remark = Storage.Extras.UserServerRemark()
|
||||
|
||||
if success:
|
||||
if remark:
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
import functools
|
||||
|
||||
__all__ = ['LogViewerWindow']
|
||||
|
||||
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
@@ -55,7 +54,7 @@ class QRCodeWindow(AppQMainWindow):
|
||||
for index in indexes:
|
||||
qrdata = io.BytesIO()
|
||||
|
||||
config = AS_UserServers()[index]
|
||||
config = Storage.UserServers()[index]
|
||||
|
||||
try:
|
||||
uri = config.toURI()
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.PyFramework import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Library import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.IndentSpinBox import *
|
||||
|
||||
from PySide6 import QtCore
|
||||
@@ -98,7 +97,7 @@ class TextEditorWindow(AppQMainWindow):
|
||||
)
|
||||
|
||||
self.jsonEditor = DraculaJSONTextEditor(
|
||||
fontFamily=APP().customFontName,
|
||||
fontFamily=AppFontName(),
|
||||
pointSizeSettingsName='ServerWidgetPointSize',
|
||||
)
|
||||
self.jsonEditor.setLineWrapMode(DraculaJSONTextEditor.LineWrapMode.NoWrap)
|
||||
@@ -230,7 +229,7 @@ class TextEditorWindow(AppQMainWindow):
|
||||
|
||||
def setPlainText(self, text: str, blockSignals: bool):
|
||||
if blockSignals:
|
||||
with QBlockSignals(self.jsonEditor):
|
||||
with Mixins.QBlockSignalContext(self.jsonEditor):
|
||||
self.jsonEditor.setPlainText(text)
|
||||
else:
|
||||
self.jsonEditor.setPlainText(text)
|
||||
@@ -262,12 +261,12 @@ class TextEditorWindow(AppQMainWindow):
|
||||
|
||||
return False
|
||||
else:
|
||||
old = AS_UserServers()[index]
|
||||
new = constructFromDict(jsonObject, **old.kwargs)
|
||||
old = Storage.UserServers()[index]
|
||||
new = configFactoryFromDict(jsonObject, **old.kwargs)
|
||||
|
||||
old.deleted = True
|
||||
|
||||
AS_UserServers()[index] = new
|
||||
Storage.UserServers()[index] = new
|
||||
|
||||
try:
|
||||
APP().mainWindow.flushRow(index, new)
|
||||
@@ -276,7 +275,7 @@ class TextEditorWindow(AppQMainWindow):
|
||||
|
||||
pass
|
||||
|
||||
if index == AS_UserActivatedItemIndex():
|
||||
if index == Storage.UserActivatedItemIndex():
|
||||
showNewChangesNextTimeMBox(parent=self, method=showChangesMethod)
|
||||
|
||||
self.markAsSaved()
|
||||
@@ -306,7 +305,7 @@ class TextEditorWindow(AppQMainWindow):
|
||||
|
||||
def setIndent(self):
|
||||
def handleResultCode(_indentSpinBox, code):
|
||||
if code == PySide6LegacyEnumValueWrapper(AppQDialog.DialogCode.Accepted):
|
||||
if code == PySide6Legacy.enumValueWrapper(AppQDialog.DialogCode.Accepted):
|
||||
plain = self.jsonEditor.toPlainText()
|
||||
|
||||
try:
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
# 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 Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.UserSubsQTableWidget import *
|
||||
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6.QtGui import *
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import *
|
||||
from PySide6.QtNetwork import *
|
||||
|
||||
import uuid
|
||||
import functools
|
||||
@@ -115,7 +114,7 @@ class UserSubsWindow(AppQMainWindow):
|
||||
|
||||
def addSubs(self):
|
||||
def handleResultCode(_addSubsDialog, code):
|
||||
if code == PySide6LegacyEnumValueWrapper(AppQDialog.DialogCode.Accepted):
|
||||
if code == PySide6Legacy.enumValueWrapper(AppQDialog.DialogCode.Accepted):
|
||||
remark = _addSubsDialog.subsRemark()
|
||||
webURL = _addSubsDialog.subsWebURL()
|
||||
|
||||
|
||||
@@ -17,18 +17,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Widget.XrayAssetViewerQListWidget import *
|
||||
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtWidgets import *
|
||||
|
||||
import logging
|
||||
import functools
|
||||
|
||||
__all__ = ['XrayAssetViewerWindow']
|
||||
|
||||
|
||||
+1
-1
@@ -15,4 +15,4 @@
|
||||
# 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 .Utility import AppResources
|
||||
from .Frozenlib import AppResources
|
||||
|
||||
+21
-22
@@ -15,9 +15,10 @@
|
||||
# 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 Furious.Frozenlib import *
|
||||
from Furious.Interface import *
|
||||
from Furious.QtFramework import *
|
||||
from Furious.QtFramework import gettext as _
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Utility import *
|
||||
from Furious.Widget.Application import Application
|
||||
|
||||
@@ -49,12 +50,12 @@ def runAppMain():
|
||||
# For Qt runtime. Not used
|
||||
_app = Application(sys.argv)
|
||||
|
||||
if exitcode == ApplicationFactory.ExitCode.PlatformNotSupported:
|
||||
messageBox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
if exitcode == ApplicationFactory.ExitCode.PlatformNotSupported.value:
|
||||
mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
|
||||
messageBox.setWindowIcon(bootstrapIcon('rocket-takeoff-window.svg'))
|
||||
messageBox.setWindowTitle(_(APPLICATION_NAME))
|
||||
messageBox.setText(
|
||||
mbox.setWindowIcon(bootstrapIcon('rocket-takeoff-window.svg'))
|
||||
mbox.setWindowTitle(_(APPLICATION_NAME))
|
||||
mbox.setText(
|
||||
_(f'{APPLICATION_NAME} is not able to run on this operating system')
|
||||
)
|
||||
|
||||
@@ -68,7 +69,7 @@ def runAppMain():
|
||||
'machine',
|
||||
)
|
||||
|
||||
messageBox.setInformativeText(
|
||||
mbox.setInformativeText(
|
||||
_('Operating system information')
|
||||
+ '\n'
|
||||
+ '\n'.join(
|
||||
@@ -77,9 +78,9 @@ def runAppMain():
|
||||
)
|
||||
|
||||
# Show the MessageBox and wait for the user to close it
|
||||
messageBox.exec()
|
||||
mbox.exec()
|
||||
else:
|
||||
if exitcode == ApplicationFactory.ExitCode.AssertionError:
|
||||
if exitcode == ApplicationFactory.ExitCode.AssertionError.value:
|
||||
# Assertion error
|
||||
text = _(
|
||||
f'{APPLICATION_NAME} encountered an internal error and needs to be stopped'
|
||||
@@ -90,11 +91,11 @@ def runAppMain():
|
||||
f'{APPLICATION_NAME} stopped unexpectedly due to an unknown exception'
|
||||
)
|
||||
|
||||
messageBox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Critical)
|
||||
|
||||
messageBox.setWindowIcon(bootstrapIcon('rocket-takeoff-window.svg'))
|
||||
messageBox.setWindowTitle(_(APPLICATION_NAME))
|
||||
messageBox.setText(text)
|
||||
mbox.setWindowIcon(bootstrapIcon('rocket-takeoff-window.svg'))
|
||||
mbox.setWindowTitle(_(APPLICATION_NAME))
|
||||
mbox.setText(text)
|
||||
|
||||
if process.fileWritten.value:
|
||||
# Crash log saved
|
||||
@@ -102,15 +103,13 @@ def runAppMain():
|
||||
|
||||
logger.info(f'crash log has been saved to {crashLogFile}')
|
||||
|
||||
messageBox.setInformativeText(
|
||||
mbox.setInformativeText(
|
||||
_('Crash log has been saved to') + f' {crashLogFile}'
|
||||
)
|
||||
button0 = messageBox.addButton(
|
||||
button0 = mbox.addButton(
|
||||
_('Open crash log'), AppQMessageBox.ButtonRole.AcceptRole
|
||||
)
|
||||
button1 = messageBox.addButton(
|
||||
_('OK'), AppQMessageBox.ButtonRole.RejectRole
|
||||
)
|
||||
button1 = mbox.addButton(_('OK'), AppQMessageBox.ButtonRole.RejectRole)
|
||||
|
||||
def handleButtonClicked(button):
|
||||
if button == button0:
|
||||
@@ -125,16 +124,16 @@ def runAppMain():
|
||||
# OK. Do nothing
|
||||
pass
|
||||
|
||||
messageBox.buttonClicked.connect(handleButtonClicked)
|
||||
mbox.buttonClicked.connect(handleButtonClicked)
|
||||
|
||||
# Show the MessageBox and wait for the user to close it
|
||||
messageBox.exec()
|
||||
mbox.exec()
|
||||
else:
|
||||
# Crash log wasn't saved
|
||||
logger.info(f'crash log was not saved')
|
||||
|
||||
# Show the MessageBox and wait for the user to close it
|
||||
messageBox.exec()
|
||||
mbox.exec()
|
||||
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
+3
-3
@@ -17,9 +17,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Library import *
|
||||
from Furious.Utility import *
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Externals import *
|
||||
from Furious.Library import *
|
||||
|
||||
import os
|
||||
import re
|
||||
@@ -58,7 +58,7 @@ def getMagicNameFromPath(path):
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def getAppConstantsByName(name):
|
||||
return getattr(Furious.Utility.Constants, name)
|
||||
return getattr(Furious.Frozenlib.Constants, name)
|
||||
|
||||
|
||||
APPLICATION_SOURCE_CODE_PATH = getAppSourceCodePath(PACKAGE_DIR)
|
||||
|
||||
Reference in New Issue
Block a user