From dea0a22b67b4cb21ba28638cc048fe29036f2522 Mon Sep 17 00:00:00 2001 From: M4x Date: Thu, 3 Oct 2019 23:34:11 -0700 Subject: [PATCH 1/5] Impove resolution on HiDPI --- v2rayL-GUI/v2rayLui.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/v2rayL-GUI/v2rayLui.py b/v2rayL-GUI/v2rayLui.py index 2e0be85..15f127f 100644 --- a/v2rayL-GUI/v2rayLui.py +++ b/v2rayL-GUI/v2rayLui.py @@ -12,9 +12,10 @@ QMessageBox, QFileDialog, QSystemTrayIcon, - qApp + qApp, + QMainWindow ) - ++from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon, QPixmap from v2rayL_api import V2rayL, MyException import pyzbar.pyzbar as pyzbar @@ -482,6 +483,7 @@ def output_conf_by_qr(self, region): if __name__ == "__main__": import sys + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QApplication(sys.argv) myWin = MyMainWindow() # 显示在屏幕上 From d2810bdfed822ca198e47c31e752c2e0c4d76ee8 Mon Sep 17 00:00:00 2001 From: jiangxufeng Date: Sat, 5 Oct 2019 21:12:53 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E3=80=81=E6=89=8B=E5=8A=A8=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E3=80=81=E8=87=AA=E9=80=82=E5=BA=94=E9=AB=98?= =?UTF-8?q?=E5=88=86=E8=BE=A8=E7=8E=87=E3=80=81=E8=8B=A5=E5=B9=B2=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- v2rayL-GUI/new_ui.py | 380 +++++++++++++++++++++++++++++------ v2rayL-GUI/sub2conf_api.py | 9 +- v2rayL-GUI/v2rayL_api.py | 40 ++-- v2rayL-GUI/v2rayL_threads.py | 2 +- v2rayL-GUI/v2rayLui.py | 166 +++++++++++++-- 6 files changed, 500 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 258532c..36c855a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ v2ray linux 客户端,使用pyqt5编写GUI界面,核心基于v2ray-core(v2ra - 设置自动更新订阅、更换地址 - 支持协议:vmess、shadowsocks - 通过`vmess://`、`ss://`分享链接添加配置,通过二维码添加配置 +- 手动添加配置,修改本地监听端口 - 导出配置、生成配置分享链接、生成分享二维码 - 最小化至托盘、测试延时、检查更新 - ...... @@ -60,9 +61,9 @@ bash <(curl -s -L http://dl.thinker.ink/uninstall.sh) ![首页](http://cloud.thinker.ink/download/a043a08860f239f8d0cbeb2dc2a5b6d5.png) -![setting1](http://cloud.thinker.ink/download/c32e16f3b205b25c615b86a7beb8eb8d.png) +![setting1](http://cloud.thinker.ink/images/364231980b07e8881164f61cd220d0bb.png) -![setting2](http://cloud.thinker.ink/images/cc18605727deaf878cb0a2fa07ec230f.png) +![setting2](http://cloud.thinker.ink/images/8835526765d479143879c08fe1ecb8a4.png) # 感谢 diff --git a/v2rayL-GUI/new_ui.py b/v2rayL-GUI/new_ui.py index 3baabfd..d1945b0 100644 --- a/v2rayL-GUI/new_ui.py +++ b/v2rayL-GUI/new_ui.py @@ -330,10 +330,20 @@ def init_ui(self): self.share_child_ui = Ui_Share_Dialog() self.share_child_ui.setupUi(self.share_ui) - # 二维码分享配置窗口 - self.qr_ui = QDialog() - self.qr_child_ui = Ui_Qr_Dialog() - self.qr_child_ui.setupUi(self.qr_ui) + # # 二维码分享配置窗口 + # self.qr_ui = QDialog() + # self.qr_child_ui = Ui_Qr_Dialog() + # self.qr_child_ui.setupUi(self.qr_ui) + + # 添加ss窗口 + self.ss_add_ui = QDialog() + self.ss_add_child_ui = Ui_Add_Ss_Dialog() + self.ss_add_child_ui.setupUi(self.ss_add_ui) + + # 添加vmess窗口 + self.vmess_add_ui = QDialog() + self.vmess_add_child_ui = Ui_Add_Vmess_Dialog() + self.vmess_add_child_ui.setupUi(self.vmess_add_ui) self.current_page = self.first_widget self.config_setting_widget.hide() @@ -445,12 +455,9 @@ def setupUi(self, Form): self.pushButton.setStyleSheet("#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" "#pushButton:hover{ background: #5599FF;}") self.pushButton.setObjectName("pushButton") - self.label_6 = QLabel(Form) - self.label_6.setGeometry(QRect(40, 190, 141, 21)) - self.label_6.setStyleSheet("font: 13pt \"Purisa\";\n") - self.label_6.setObjectName("label_6") + self.pushButton_2 = QPushButton(Form) - self.pushButton_2.setGeometry(QRect(210, 184, 171, 31)) + self.pushButton_2.setGeometry(QRect(570, 184, 171, 31)) self.pushButton_2.setStyleSheet("#pushButton_2{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" "#pushButton_2:hover{ background: #5599FF;}") self.pushButton_2.setObjectName("pushButton_2") @@ -482,6 +489,19 @@ def setupUi(self, Form): self.switchBtn = SwitchBtn(self.label_9) self.switchBtn.setGeometry(0, 0, 60, 30) + self.pushButton_vmess = QPushButton(Form) + self.pushButton_vmess.setGeometry(QRect(40, 184, 171, 31)) + self.pushButton_vmess.setStyleSheet( + "#pushButton_vmess{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" + "#pushButton_vmess:hover{ background: #5599FF;}") + self.pushButton_vmess.setObjectName("pushButton_vmess") + + self.pushButton_ss = QPushButton(Form) + self.pushButton_ss.setGeometry(QRect(250, 184, 171, 31)) + self.pushButton_ss.setStyleSheet( + "#pushButton_ss{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" + "#pushButton_ss:hover{ background: #5599FF;}") + self.pushButton_ss.setObjectName("pushButton_ss") self.retranslateUi(Form) QMetaObject.connectSlotsByName(Form) @@ -496,10 +516,11 @@ def retranslateUi(self, Form): self.label_4.setText(_translate("Form", "通过URI添加")) self.label_5.setText(_translate("Form", "通过二维码添加")) self.pushButton.setText(_translate("Form", "点击选择二维码")) - self.label_6.setText(_translate("Form", "导出当前完整配置")) - self.pushButton_2.setText(_translate("Form", "选择保存地址")) + self.pushButton_2.setText(_translate("Form", "导出当前完整配置")) self.label_7.setText(_translate("Form", "订阅")) self.label_8.setText(_translate("Form", "程序启动时自动更新订阅")) + self.pushButton_vmess.setText(_translate("Form", "手动配置Vmess")) + self.pushButton_ss.setText(_translate("Form", "手动配置shadowsocks")) class Ui_SystemSettings(object): @@ -519,16 +540,6 @@ def setupUi(self, SystemSettings): self.label_3.setGeometry(QRect(450, 130, 67, 17)) self.label_3.setStyleSheet("font: 14pt \"Purisa\";") self.label_3.setObjectName("label_3") - # self.socks_spinBox = QSpinBox(SystemSettings) - # self.socks_spinBox.setGeometry(QRect(530, 120, 131, 31)) - # self.socks_spinBox.setMinimum(1080) - # self.socks_spinBox.setMaximum(10000) - # self.socks_spinBox.setObjectName("socks_spinBox") - # self.socks_spinBox_2 = QSpinBox(SystemSettings) - # self.socks_spinBox_2.setGeometry(QRect(150, 120, 131, 31)) - # self.socks_spinBox_2.setMinimum(1080) - # self.socks_spinBox_2.setMaximum(10000) - # self.socks_spinBox_2.setObjectName("socks_spinBox_2") self.label_4 = QLabel(SystemSettings) self.label_4.setGeometry(QRect(310, 230, 101, 31)) self.label_4.setStyleSheet("font-size: 24px;\n" @@ -560,15 +571,17 @@ def setupUi(self, SystemSettings): self.label_8.setGeometry(QRect(320, 375, 201, 71)) self.label_8.setObjectName("label_8") - self.label_9 = QLabel(SystemSettings) - self.label_9.setGeometry(QRect(130, 130, 67, 17)) - self.label_9.setStyleSheet("font: 14pt \"Purisa\";") - self.label_9.setObjectName("label_9") + self.http_sp = QSpinBox(SystemSettings) + self.http_sp.setGeometry(QRect(130, 124, 80, 30)) + self.http_sp.setMinimum(1080) + self.http_sp.setMaximum(10080) + self.http_sp.setValue(1081) - self.label_10 = QLabel(SystemSettings) - self.label_10.setGeometry(QRect(525, 130, 67, 17)) - self.label_10.setStyleSheet("font: 14pt \"Purisa\";") - self.label_10.setObjectName("label_10") + self.socks_sp = QSpinBox(SystemSettings) + self.socks_sp.setGeometry(QRect(525, 124, 80, 30)) + self.socks_sp.setMinimum(1080) + self.socks_sp.setMaximum(10080) + self.socks_sp.setValue(1080) self.switchBtn = SwitchBtn(self.label_8, True) self.switchBtn.setGeometry(0, 0, 60, 30) @@ -585,12 +598,12 @@ def retranslateUi(self, SystemSettings): self.label_3.setText(_translate("SystemSettings", "Socks:")) self.label_4.setText(_translate("SystemSettings", "版本更新")) self.label_5.setText(_translate("SystemSettings", "当前版本:")) - self.version_label.setText(_translate("SystemSettings", "v2.0.4")) + self.version_label.setText(_translate("SystemSettings", "v2.1.0")) self.checkupdateButton.setText(_translate("SystemSettings", "检查更新")) self.label_7.setText(_translate("SystemSettings", "程序启动时自动进行检查更新")) - self.label_6.setText(_translate("SystemSettings", "**端口可选范围:1080-10000")) - self.label_9.setText(_translate("SystemSettings", "1081")) - self.label_10.setText(_translate("SystemSettings", "1080")) + self.label_6.setText(_translate("SystemSettings", "**端口可选范围:1080-10080,每次修改都将更新**")) + # self.label_9.setText(_translate("SystemSettings", "1081")) + # self.label_10.setText(_translate("SystemSettings", "1080")) class Ui_HelpUi(object): @@ -617,14 +630,15 @@ def retranslateUi(self, HelpUi): "\n" "

当前版本

\n" "

——————————————————————

\n" - "

v2.0.4

\n" + "

v2.1.0

\n" "


\n" "

说明

\n" "

——————————————————————

")) - self.label.setText(_translate("HelpUi", "1. 具有全新的V2rayL UI\n" - "2. github地址:https://github.com/jiangxufeng/v2rayL\n" - "3. 目前支持协议有:Vmess、shadowsocks\n" - "4. 支持通过分享链接、二维码导入和分享配置\n" + self.label.setText(_translate("HelpUi", + "1. github地址:https://github.com/jiangxufeng/v2rayL\n" + "2. 目前支持协议有:Vmess、shadowsocks\n" + "3. 支持通过分享链接、二维码导入和分享配置,手动配置\n" + "4. 支持修改本地监听端口,范围为1080~10080\n" "5. 开发环境为Ubuntu18.04+Python3.6,其他linux系统可能不兼容\n" "6. 程序可能存在未测试到的Bug,使用过程中发现Bug请在github提交")) @@ -776,57 +790,289 @@ def retranslateUi(self, Form): class Ui_Share_Dialog(object): + def setupUi(self, dialog): + dialog.setObjectName("dialog") + dialog.resize(565, 480) + dialog.setStyleSheet("#dialog{color:#232C51; background:white; }") + dialog.setWindowOpacity(0.95) # 设置窗口透明度 + self.label = QLabel(dialog) + self.label.setGeometry(QRect(170, 20, 200, 200)) + self.label.setText("") + self.label.setObjectName("label") + self.textBrowser = QTextBrowser(dialog) + self.textBrowser.setGeometry(QRect(20, 250, 501, 171)) + self.textBrowser.setObjectName("textBrowser") + + self.retranslateUi(dialog) + QMetaObject.connectSlotsByName(dialog) + + def retranslateUi(self, dialog): + _translate = QCoreApplication.translate + dialog.setWindowTitle(_translate("dialog", "配置分享")) + + +class Ui_Add_Ss_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(226, 171) + Dialog.resize(314, 329) + Dialog.setStyleSheet(""" + #Dialog{ + color:#232C51; + background:white; + } + """) + Dialog.setWindowOpacity(0.95) # 设置窗口透明度 + # Dialog.setAttribute(Qt.WA_TranslucentBackground) # 设置窗口背景透明 self.label = QLabel(Dialog) - self.label.setGeometry(QRect(20, 20, 191, 17)) - self.label.setStyleSheet("font: 75 13pt \"新宋体\";") + self.label.setGeometry(QRect(20, 30, 111, 20)) self.label.setObjectName("label") + self.label_2 = QLabel(Dialog) + self.label_2.setGeometry(QRect(20, 80, 121, 17)) + self.label_2.setObjectName("label_2") + self.lineEdit_2 = QLineEdit(Dialog) + self.lineEdit_2.setGeometry(QRect(70, 30, 221, 25)) + self.lineEdit_2.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_2.setText("") + self.lineEdit_2.setObjectName("lineEdit_2") + self.label_3 = QLabel(Dialog) + self.label_3.setGeometry(QRect(20, 130, 121, 17)) + self.label_3.setObjectName("label_3") + self.label_4 = QLabel(Dialog) + self.label_4.setGeometry(QRect(20, 180, 131, 17)) + self.label_4.setObjectName("label_4") + self.label_5 = QLabel(Dialog) + self.label_5.setGeometry(QRect(20, 230, 121, 17)) + self.label_5.setObjectName("label_5") + self.lineEdit_3 = QLineEdit(Dialog) + self.lineEdit_3.setGeometry(QRect(70, 80, 221, 25)) + self.lineEdit_3.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_3.setText("") + self.lineEdit_3.setObjectName("lineEdit_3") + self.lineEdit_4 = QLineEdit(Dialog) + self.lineEdit_4.setGeometry(QRect(70, 130, 221, 25)) + self.lineEdit_4.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_4.setText("") + self.lineEdit_4.setObjectName("lineEdit_4") + self.lineEdit_5 = QLineEdit(Dialog) + self.lineEdit_5.setGeometry(QRect(70, 180, 221, 25)) + self.lineEdit_5.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_5.setText("") + self.lineEdit_5.setObjectName("lineEdit_5") + self.lineEdit_5.setEchoMode(QLineEdit.Password) + self.comboBox = QComboBox(Dialog) + self.comboBox.setGeometry(QRect(100, 230, 191, 25)) + self.comboBox.setObjectName("comboBox") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") self.pushButton = QPushButton(Dialog) - self.pushButton.setGeometry(QRect(40, 60, 141, 31)) - self.pushButton.setStyleSheet("#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" - "#pushButton:hover{ background: #5599FF;}") + self.pushButton.setGeometry(QRect(80, 280, 151, 31)) + self.pushButton.setStyleSheet("#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; } \n" +"#pushButton:hover{ background: #5599FF;}") self.pushButton.setObjectName("pushButton") - self.pushButton_2 = QPushButton(Dialog) - self.pushButton_2.setGeometry(QRect(40, 110, 141, 31)) - self.pushButton_2.setStyleSheet("#pushButton_2{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" - "#pushButton_2:hover{ background: #5599FF;}") - self.pushButton_2.setObjectName("pushButton_2") + self.line = QFrame(Dialog) + self.line.setGeometry(QRect(70, 50, 231, 16)) + self.line.setFrameShape(QFrame.HLine) + self.line.setFrameShadow(QFrame.Sunken) + self.line.setObjectName("line") + self.line_2 = QFrame(Dialog) + self.line_2.setGeometry(QRect(70, 100, 231, 16)) + self.line_2.setFrameShape(QFrame.HLine) + self.line_2.setFrameShadow(QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.line_3 = QFrame(Dialog) + self.line_3.setGeometry(QRect(70, 150, 231, 16)) + self.line_3.setFrameShape(QFrame.HLine) + self.line_3.setFrameShadow(QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.line_4 = QFrame(Dialog) + self.line_4.setGeometry(QRect(70, 200, 231, 16)) + self.line_4.setFrameShape(QFrame.HLine) + self.line_4.setFrameShadow(QFrame.Sunken) + self.line_4.setObjectName("line_4") self.retranslateUi(Dialog) QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "配置分享")) - self.label.setText(_translate("Dialog", "请选择以下一种方式分享")) - self.pushButton.setText(_translate("Dialog", "二维码分享")) - self.pushButton_2.setText(_translate("Dialog", "链接分享")) - - -class Ui_Qr_Dialog(object): + Dialog.setWindowTitle(_translate("Dialog", "手动添加ShadowSocks配置")) + self.label.setText(_translate("Dialog", "别名:")) + self.label_2.setText(_translate("Dialog", "地址:")) + self.label_3.setText(_translate("Dialog", "端口:")) + self.label_4.setText(_translate("Dialog", "密码:")) + self.label_5.setText(_translate("Dialog", "加密方式:")) + self.comboBox.setItemText(0, _translate("Dialog", "aes-256-cfb")) + self.comboBox.setItemText(1, _translate("Dialog", "aes-128-cfb")) + self.comboBox.setItemText(2, _translate("Dialog", "chacha20")) + self.comboBox.setItemText(3, _translate("Dialog", "chacha20-ietf")) + self.comboBox.setItemText(4, _translate("Dialog", "aes-256-gcm")) + self.comboBox.setItemText(5, _translate("Dialog", "aes-128-gcm")) + self.comboBox.setItemText(6, _translate("Dialog", "chacha20-poly1305")) + self.comboBox.setItemText(7, _translate("Dialog", "chacha20-ietf-poly1305")) + self.pushButton.setText(_translate("Dialog", "确认添加")) + + +class Ui_Add_Vmess_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(218, 218) - Dialog.setMinimumSize(QSize(218, 218)) - Dialog.setMaximumSize(QSize(218, 218)) - self.gridLayout = QGridLayout(Dialog) - self.gridLayout.setObjectName("gridLayout") + Dialog.resize(465, 527) + Dialog.setStyleSheet("#Dialog{color:#232C51; background:white; }") + Dialog.setWindowOpacity(0.95) # 设置窗口透明度 self.label = QLabel(Dialog) - self.label.setEnabled(True) - self.label.setMinimumSize(QSize(200, 200)) - self.label.setMaximumSize(QSize(200, 200)) - self.label.setText("") + self.label.setGeometry(QRect(30, 35, 111, 17)) self.label.setObjectName("label") - self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.lineEdit = QLineEdit(Dialog) + self.lineEdit.setGeometry(QRect(140, 30, 301, 25)) + self.lineEdit.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit.setText("") + self.lineEdit.setObjectName("lineEdit") + self.label_2 = QLabel(Dialog) + self.label_2.setGeometry(QRect(30, 75, 121, 17)) + self.label_2.setObjectName("label_2") + self.lineEdit_2 = QLineEdit(Dialog) + self.lineEdit_2.setGeometry(QRect(120, 70, 321, 25)) + self.lineEdit_2.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_2.setText("") + self.lineEdit_2.setObjectName("lineEdit_2") + self.label_3 = QLabel(Dialog) + self.label_3.setGeometry(QRect(30, 115, 101, 17)) + self.label_3.setObjectName("label_3") + self.lineEdit_3 = QLineEdit(Dialog) + self.lineEdit_3.setGeometry(QRect(120, 110, 321, 25)) + self.lineEdit_3.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_3.setText("") + self.lineEdit_3.setObjectName("lineEdit_3") + self.label_4 = QLabel(Dialog) + self.label_4.setGeometry(QRect(30, 155, 91, 17)) + self.label_4.setObjectName("label_4") + self.lineEdit_4 = QLineEdit(Dialog) + self.lineEdit_4.setGeometry(QRect(120, 150, 321, 25)) + self.lineEdit_4.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_4.setText("") + self.lineEdit_4.setObjectName("lineEdit_4") + self.lineEdit_5 = QLineEdit(Dialog) + self.lineEdit_5.setGeometry(QRect(150, 190, 291, 25)) + self.lineEdit_5.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_5.setText("") + self.lineEdit_5.setObjectName("lineEdit_5") + self.label_5 = QLabel(Dialog) + self.label_5.setGeometry(QRect(30, 195, 111, 17)) + self.label_5.setObjectName("label_5") + self.label_6 = QLabel(Dialog) + self.label_6.setGeometry(QRect(30, 240, 121, 17)) + self.label_6.setObjectName("label_6") + self.label_7 = QLabel(Dialog) + self.label_7.setGeometry(QRect(30, 330, 111, 17)) + self.label_7.setObjectName("label_7") + self.comboBox = QComboBox(Dialog) + self.comboBox.setGeometry(QRect(150, 235, 86, 25)) + #self.comboBox.setStyleSheet("background-color: rgb(255, 255, 255);") + self.comboBox.setObjectName("comboBox") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.label_8 = QLabel(Dialog) + self.label_8.setGeometry(QRect(30, 286, 221, 21)) + self.label_8.setObjectName("label_8") + self.comboBox_2 = QComboBox(Dialog) + self.comboBox_2.setGeometry(QRect(150, 325, 86, 25)) + #self.comboBox_2.setStyleSheet("background-color: rgb(255, 255, 255);") + self.comboBox_2.setObjectName("comboBox_2") + self.comboBox_2.addItem("") + self.comboBox_2.addItem("") + self.comboBox_2.addItem("") + self.comboBox_2.addItem("") + self.comboBox_2.addItem("") + self.comboBox_2.addItem("") + self.comboBox_2.addItem("") + self.label_9 = QLabel(Dialog) + self.label_9.setGeometry(QRect(30, 365, 121, 17)) + self.label_9.setObjectName("label_9") + self.lineEdit_6 = QLineEdit(Dialog) + self.lineEdit_6.setGeometry(QRect(150, 360, 291, 25)) + self.lineEdit_6.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_6.setText("") + self.lineEdit_6.setObjectName("lineEdit_6") + self.label_10 = QLabel(Dialog) + self.label_10.setGeometry(QRect(30, 410, 67, 17)) + self.label_10.setObjectName("label_10") + self.lineEdit_7 = QLineEdit(Dialog) + self.lineEdit_7.setGeometry(QRect(80, 405, 361, 25)) + self.lineEdit_7.setStyleSheet("border-style:none none solid none;background-color:transparent;") + self.lineEdit_7.setText("") + self.lineEdit_7.setObjectName("lineEdit_7") + self.line = QFrame(Dialog) + self.line.setGeometry(QRect(140, 45, 301, 16)) + self.line.setFrameShape(QFrame.HLine) + self.line.setFrameShadow(QFrame.Sunken) + self.line.setObjectName("line") + self.line_2 = QFrame(Dialog) + self.line_2.setGeometry(QRect(120, 90, 321, 16)) + self.line_2.setFrameShape(QFrame.HLine) + self.line_2.setFrameShadow(QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.line_3 = QFrame(Dialog) + self.line_3.setGeometry(QRect(120, 130, 321, 16)) + self.line_3.setFrameShape(QFrame.HLine) + self.line_3.setFrameShadow(QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.line_4 = QFrame(Dialog) + self.line_4.setGeometry(QRect(120, 170, 321, 16)) + self.line_4.setFrameShape(QFrame.HLine) + self.line_4.setFrameShadow(QFrame.Sunken) + self.line_4.setObjectName("line_4") + self.line_5 = QFrame(Dialog) + self.line_5.setGeometry(QRect(150, 210, 291, 20)) + self.line_5.setFrameShape(QFrame.HLine) + self.line_5.setFrameShadow(QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.line_6 = QFrame(Dialog) + self.line_6.setGeometry(QRect(150, 380, 291, 20)) + self.line_6.setFrameShape(QFrame.HLine) + self.line_6.setFrameShadow(QFrame.Sunken) + self.line_6.setObjectName("line_6") + self.line_7 = QFrame(Dialog) + self.line_7.setGeometry(QRect(80, 420, 361, 20)) + self.line_7.setFrameShape(QFrame.HLine) + self.line_7.setFrameShadow(QFrame.Sunken) + self.line_7.setObjectName("line_7") + self.pushButton = QPushButton(Dialog) + self.pushButton.setGeometry(QRect(160, 464, 151, 31)) + self.pushButton.setStyleSheet("#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" +"#pushButton:hover{ background: #5599FF;}") + self.pushButton.setObjectName("pushButton") self.retranslateUi(Dialog) QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "二维码分享配置")) + Dialog.setWindowTitle(_translate("Dialog", "手动添加Vmess配置")) + self.label.setText(_translate("Dialog", "别名(remark):")) + self.label_2.setText(_translate("Dialog", "地址(addr):")) + self.label_3.setText(_translate("Dialog", "端口(port):")) + self.label_4.setText(_translate("Dialog", "用户ID(id):")) + self.label_5.setText(_translate("Dialog", "额外ID(alterId):")) + self.label_6.setText(_translate("Dialog", "传输协议(net):")) + self.label_7.setText(_translate("Dialog", "伪装类型(type):")) + self.comboBox.setItemText(0, _translate("Dialog", "ws")) + self.comboBox.setItemText(1, _translate("Dialog", "kcp")) + self.label_8.setText(_translate("Dialog", "下列配置无则保持默认")) + self.comboBox_2.setItemText(0, _translate("Dialog", "none")) + self.comboBox_2.setItemText(1, _translate("Dialog", "http")) + self.comboBox_2.setItemText(2, _translate("Dialog", "utp")) + self.comboBox_2.setItemText(3, _translate("Dialog", "wechat-video")) + self.comboBox_2.setItemText(4, _translate("Dialog", "dtls")) + self.comboBox_2.setItemText(5, _translate("Dialog", "strp")) + self.comboBox_2.setItemText(6, _translate("Dialog", "wireguard")) + self.label_9.setText(_translate("Dialog", "伪装域名(host):")) + self.label_10.setText(_translate("Dialog", "path:")) + self.pushButton.setText(_translate("Dialog", "确认添加")) def main(): app = QApplication(sys.argv) diff --git a/v2rayL-GUI/sub2conf_api.py b/v2rayL-GUI/sub2conf_api.py index 96aaa07..295d9eb 100755 --- a/v2rayL-GUI/sub2conf_api.py +++ b/v2rayL-GUI/sub2conf_api.py @@ -6,8 +6,9 @@ import json import pickle import requests +import copy import urllib.parse as parse -from config import conf_template as conf +from config import conf_template as tpl class Sub2Conf(object): @@ -65,8 +66,12 @@ def b642conf(self, prot, tp, b64str): region = region + "_local" self.saved_conf[["local", "subs"][tp]][region] = ret - def setconf(self, region): + def setconf(self, region, http, socks): use_conf = self.conf[region] + conf = copy.deepcopy(tpl) + conf["inbounds"][0]["port"] = socks + conf["inbounds"][1]["port"] = http + if use_conf['prot'] == "vmess": conf['outbounds'][0]["protocol"] = "vmess" conf['outbounds'][0]["settings"]["vnext"] = list() diff --git a/v2rayL-GUI/v2rayL_api.py b/v2rayL-GUI/v2rayL_api.py index 28bd580..3b86a7b 100755 --- a/v2rayL-GUI/v2rayL_api.py +++ b/v2rayL-GUI/v2rayL_api.py @@ -13,12 +13,14 @@ def __init__(self): try: with open("/etc/v2rayL/ncurrent", "rb") as f: - self.current, self.url, self.auto, self.check = pickle.load(f) + self.current, self.url, self.auto, self.check, self.http, self.socks = pickle.load(f) except: self.current = "未连接至VPN" self.url = None self.auto = False self.check = False + self.http = 1081 + self.socks = 1080 self.subs = Sub2Conf(subs_url=self.url) @@ -27,7 +29,8 @@ def __init__(self): self.subs.update() except: with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, None, False), jf) + pickle.dump(("未连接至VPN", None, False, False, + self.http, self.socks), jf) raise MyException("更新失败, 已关闭自动更新,请更新订阅地址") def auto_check(self, flag): @@ -44,14 +47,17 @@ def subscribe(self, flag): if flag: with open("/etc/v2rayL/ncurrent", "wb") as jf: self.auto = True - pickle.dump((self.current, self.url, self.auto, self.check), jf) + pickle.dump((self.current, self.url, self.auto, self.check, + self.http, self.socks), jf) else: with open("/etc/v2rayL/ncurrent", "wb") as jf: self.auto = False - pickle.dump((self.current, self.url, self.auto, self.check), jf) + pickle.dump((self.current, self.url, self.auto, self.check, + self.http, self.socks), jf) - def connect(self, region): - self.subs.setconf(region) + def connect(self, region, flag): + if not flag: + self.subs.setconf(region, self.http, self.socks) try: output = subprocess.getoutput(["sudo systemctl status v2rayL.service"]) if "Active: active" in output: @@ -66,7 +72,8 @@ def connect(self, region): else: self.current = region with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, self.url, self.auto, self.check), jf) + pickle.dump((self.current, self.url, self.auto, self.check, + self.http, self.socks), jf) def disconnect(self): try: @@ -75,12 +82,14 @@ def disconnect(self): subprocess.call(["sudo systemctl stop v2rayL.service"], shell=True) self.current = "未连接至VPN" with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, self.url, self.auto, self.check), jf) + pickle.dump((self.current, self.url, self.auto, self.check, + self.http, self.socks), jf) else: if self.current != "未连接至VPN": self.current = "未连接至VPN" with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, self.url, self.auto, self.check), jf) + pickle.dump((self.current, self.url, self.auto, self.check, + self.http, self.socks), jf) else: raise MyException("服务未开启,无需断开连接.") except Exception as e: @@ -91,7 +100,8 @@ def update(self, url): self.subs = Sub2Conf(subs_url=url) self.subs.update() with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, url, self.auto, self.check), jf) + pickle.dump((self.current, url, self.auto, self.check, + self.http, self.socks), jf) else: if self.current in self.subs.saved_conf["subs"]: @@ -101,11 +111,13 @@ def update(self, url): pass with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump(("未连接至VPN", None, False), jf) + pickle.dump(("未连接至VPN", None, False, False, + self.http, self.socks), jf) else: with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, None, False), jf) + pickle.dump((self.current, None, False, False, + self.http, self.socks), jf) with open("/etc/v2rayL/data", "wb") as f: pickle.dump({"local": self.subs.saved_conf["local"], "subs": {}}, f) @@ -120,8 +132,8 @@ def delconf(self, region): def ping(self): try: proxy = { - "http": "127.0.0.1:1081", - "https": "127.0.0.1:1081" + "http": "127.0.0.1:{}".format(self.http), + "https": "127.0.0.1:{}".format(self.http) } req = requests.get("http://www.google.com", proxies=proxy, timeout=10) if req.status_code == 200: diff --git a/v2rayL-GUI/v2rayL_threads.py b/v2rayL-GUI/v2rayL_threads.py index 872d1e8..2c513c1 100755 --- a/v2rayL-GUI/v2rayL_threads.py +++ b/v2rayL-GUI/v2rayL_threads.py @@ -29,7 +29,7 @@ def run(self): self.sinOut.emit(("conn", "@@Fail@@", "未选中配置无法连接,请导入配置后再次连接", None)) else: try: - self.v2rayL.connect(region) + self.v2rayL.connect(region, False) except MyException as e: self.sinOut.emit(("conn", "@@Fail@@", e.args[0], None)) else: diff --git a/v2rayL-GUI/v2rayLui.py b/v2rayL-GUI/v2rayLui.py index 15f127f..0c06e2a 100644 --- a/v2rayL-GUI/v2rayLui.py +++ b/v2rayL-GUI/v2rayLui.py @@ -2,6 +2,9 @@ # Author: Suummmmer # Date: 2019-08-13 +import json +import random +import base64 import requests import subprocess import pickle @@ -13,9 +16,9 @@ QFileDialog, QSystemTrayIcon, qApp, - QMainWindow + QDialog ) -+from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon, QPixmap from v2rayL_api import V2rayL, MyException import pyzbar.pyzbar as pyzbar @@ -28,7 +31,7 @@ CheckUpdateThread, VersionUpdateThread ) -from new_ui import MainUi, SwitchBtn +from new_ui import MainUi, SwitchBtn, Ui_Add_Ss_Dialog, Ui_Add_Vmess_Dialog class SystemTray(object): @@ -82,7 +85,7 @@ def __init__(self, parent=None): super(MyMainWindow, self).__init__(parent) self.init_ui() - self.version = "2.0.4" + self.version = "2.1.0" # 获取api操作 self.v2rayL = V2rayL() @@ -129,7 +132,9 @@ def __init__(self, parent=None): # 填充当前订阅地址 self.config_setting_ui.lineEdit.setText(self.v2rayL.url) - # + # 端口 + self.system_setting_ui.http_sp.setValue(self.v2rayL.http) + self.system_setting_ui.socks_sp.setValue(self.v2rayL.socks) # # 显示当前所有配置 self.display_all_conf() @@ -152,7 +157,12 @@ def __init__(self, parent=None): self.ping_start.sinOut.connect(self.alert) # 得到反馈 self.check_update_start.sinOut.connect(self.alert) self.version_update_start.sinOut.connect(self.alert) - + self.system_setting_ui.http_sp.valueChanged.connect(lambda: self.value_change(True)) + self.system_setting_ui.socks_sp.valueChanged.connect(lambda: self.value_change(False)) + self.config_setting_ui.pushButton_ss.clicked.connect(self.show_add_ss_dialog) + self.config_setting_ui.pushButton_vmess.clicked.connect(self.show_add_vmess_dialog) + self.ss_add_child_ui.pushButton.clicked.connect(self.add_ss_by_input) + self.vmess_add_child_ui.pushButton.clicked.connect(self.add_vmess_by_input) # 设置最小化到托盘 SystemTray(self) @@ -361,7 +371,7 @@ def alert(self, tp): if choice == QMessageBox.Yes: shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL {}".format(ret) subprocess.call([shell], shell=True) - self.version_update_start.url = "http://cloud.thinker.ink/update.sh" + self.version_update_start.url = "http://dl.thinker.ink/update.sh" self.version_update_start.start() elif tp == "vrud": @@ -439,8 +449,24 @@ def start_ping_th(self): self.ping_start.start() def show_share_dialog(self, region): - self.share_child_ui.pushButton.clicked.connect(lambda: self.output_conf_by_qr(region)) - self.share_child_ui.pushButton_2.clicked.connect(lambda: self.output_conf_by_uri(region)) + ret = self.v2rayL.subs.conf2b64(region) + # 生成二维码 + url = "http://api.k780.com:88/?app=qr.get&data={}".format(ret) + try: + req = requests.get(url) + if req.status_code == 200: + qr = QPixmap() + qr.loadFromData(req.content) + self.share_child_ui.label.setPixmap(qr) + self.share_child_ui.label.setScaledContents(True) + else: + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,可能原因:调用API发生错误" + subprocess.call([shell], shell=True) + except: + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,请将错误在github中提交" + subprocess.call([shell], shell=True) + + self.share_child_ui.textBrowser.setText(ret) self.share_ui.show() def output_conf_by_uri(self, region): @@ -448,14 +474,8 @@ def output_conf_by_uri(self, region): 输出分享链接 :return: """ - # if self.v2rayL.current != "未连接至VPN": - # ret = self.v2rayL.subs.conf2b64(self.v2rayL.current) - # QMessageBox.information(self, "分享链接", self.tr(ret)) - # else: - # shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 请连接一个VPN后再选择分享 -t 3" - # subprocess.call([shell], shell=True) ret = self.v2rayL.subs.conf2b64(region) - QMessageBox.information(self, "分享链接", self.tr(ret)) + # QMessageBox.information(self, "分享链接", self.tr(ret)) def output_conf_by_qr(self, region): """ @@ -480,6 +500,120 @@ def output_conf_by_qr(self, region): shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,请将错误在github中提交" subprocess.call([shell], shell=True) + def value_change(self, flag): + """ + 端口改变 + :param flag: true时为http, false为socks + :return: + """ + with open("/etc/v2rayL/config.json", "r") as f: + ret = json.load(f) + + if flag: + new_port = self.system_setting_ui.http_sp.value() + if new_port == ret["inbounds"][0]["port"]: + new_port = new_port + 1 if new_port < 10079 else new_port - 1 + self.system_setting_ui.http_sp.setValue(new_port) + ret["inbounds"][1]["port"] = new_port + else: + new_port = self.system_setting_ui.socks_sp.value() + if new_port == ret["inbounds"][1]["port"]: + new_port = new_port + 1 if new_port < 10079 else new_port - 1 + self.system_setting_ui.socks_sp.setValue(new_port) + ret["inbounds"][0]["port"] = new_port + + with open("/etc/v2rayL/config.json", "w") as f: + f.write(json.dumps(ret, indent=4)) + + if flag: + self.v2rayL.http = new_port + with open("/etc/v2rayL/ncurrent", "wb") as jf: + pickle.dump((self.v2rayL.current, self.v2rayL.url, self.v2rayL.auto, self.v2rayL.check, + new_port, self.v2rayL.socks), jf) + else: + self.v2rayL.socks = new_port + with open("/etc/v2rayL/ncurrent", "wb") as jf: + pickle.dump((self.v2rayL.current, self.v2rayL.url, self.v2rayL.auto, self.v2rayL.check, + self.v2rayL.http, new_port), jf) + + self.v2rayL.connect(self.v2rayL.current, True) + + def show_add_ss_dialog(self): + """ + 显示手动添加ss配置窗口 + :return: + """ + self.ss_add_ui.show() + + def show_add_vmess_dialog(self): + self.vmess_add_ui.show() + + def add_ss_by_input(self): + remark = self.ss_add_child_ui.lineEdit_2.text().strip() + addr = self.ss_add_child_ui.lineEdit_3.text().strip() + port = self.ss_add_child_ui.lineEdit_4.text().strip() + password = self.ss_add_child_ui.lineEdit_5.text().strip() + method = self.ss_add_child_ui.comboBox.currentText() + # print(remark, addr, port, password, security) + if not remark: + remark = "shadowsocks_" + str(random.choice(range(10000, 99999))) + + b64str = "ss://"+base64.b64encode("{}:{}@{}:{}".format(method, password, addr, port).encode()).decode()\ + + "#" + remark + + self.v2rayL.addconf(b64str) + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 添加成功" + subprocess.call([shell], shell=True) + self.v2rayL = V2rayL() + self.display_all_conf() + self.ss_add_child_ui.lineEdit_2.setText("") + self.ss_add_child_ui.lineEdit_3.setText("") + self.ss_add_child_ui.lineEdit_4.setText("") + self.ss_add_child_ui.lineEdit_5.setText("") + self.ss_add_ui.hide() + + def add_vmess_by_input(self): + remark = self.vmess_add_child_ui.lineEdit.text().strip() + addr = self.vmess_add_child_ui.lineEdit_2.text().strip() + port = self.vmess_add_child_ui.lineEdit_3.text().strip() + uid = self.vmess_add_child_ui.lineEdit_4.text().strip() + aid = self.vmess_add_child_ui.lineEdit_5.text().strip() + net = self.vmess_add_child_ui.comboBox.currentText() + types = self.vmess_add_child_ui.comboBox_2.currentText() + host = self.vmess_add_child_ui.lineEdit_6.text().strip() + path = self.vmess_add_child_ui.lineEdit_7.text().strip() + # print(remark, addr, port, password, security) + if not remark: + remark = "vmess_" + str(random.choice(range(10000, 99999))) + conf = { + 'add': addr, + 'port': port, + 'host': host, + 'ps': remark, + 'id': uid, + 'aid': aid, + 'net': net, + 'type': types, + 'path': path, + 'tls': "", + "v": 2 + } + b64str = "vmess://" + base64.b64encode(str(conf).encode()).decode() + + self.v2rayL.addconf(b64str) + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 添加成功" + subprocess.call([shell], shell=True) + self.v2rayL = V2rayL() + self.display_all_conf() + self.vmess_add_child_ui.lineEdit.setText("") + self.vmess_add_child_ui.lineEdit_2.setText("") + self.vmess_add_child_ui.lineEdit_3.setText("") + self.vmess_add_child_ui.lineEdit_4.setText("") + self.vmess_add_child_ui.lineEdit_5.setText("") + self.vmess_add_child_ui.lineEdit_6.setText("") + self.vmess_add_child_ui.lineEdit_7.setText("") + self.vmess_add_ui.hide() + if __name__ == "__main__": import sys From 762e1d6ff5a33a1cfdd26e80654c24ae54d766d8 Mon Sep 17 00:00:00 2001 From: jiangxufeng Date: Tue, 29 Oct 2019 23:13:56 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0log=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0tcp=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96ws-tls=E7=AD=89=E5=8D=8F=E8=AE=AE=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B=E9=97=AA?= =?UTF-8?q?=E9=80=80bug=E4=BB=A5=E5=8F=8Aservice=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2rayL-GUI/config.py | 5 +- v2rayL-GUI/new_ui.py | 67 +++++-- v2rayL-GUI/sub2conf_api.py | 67 ++++++- v2rayL-GUI/utils.py | 79 ++++++++ v2rayL-GUI/v2rayL_api.py | 177 +++++++++++++----- v2rayL-GUI/v2rayL_threads.py | 36 +++- v2rayL-GUI/v2rayLui.py | 351 ++++++++++++++++++++++------------- 7 files changed, 576 insertions(+), 206 deletions(-) create mode 100644 v2rayL-GUI/utils.py diff --git a/v2rayL-GUI/config.py b/v2rayL-GUI/config.py index abdacad..1dda85f 100755 --- a/v2rayL-GUI/config.py +++ b/v2rayL-GUI/config.py @@ -35,7 +35,10 @@ } ], "log": { - "loglevel": "warning" + "loglevel": "warning", + "access": "/etc/v2rayL/v2ray_access.log", + "error": "/etc/v2rayL/v2ray_error.log", + }, "outbounds": [{ "mux": { diff --git a/v2rayL-GUI/new_ui.py b/v2rayL-GUI/new_ui.py index d1945b0..bce436e 100644 --- a/v2rayL-GUI/new_ui.py +++ b/v2rayL-GUI/new_ui.py @@ -345,6 +345,19 @@ def init_ui(self): self.vmess_add_child_ui = Ui_Add_Vmess_Dialog() self.vmess_add_child_ui.setupUi(self.vmess_add_ui) + # 托盘菜单 + self.a1 = QAction('恢复(Show)') + self.a2 = QAction('退出(Exit)') + self.logMenu = QMenu("日志(Log)") + self.a3 = QAction('启用(Enable)', checkable=True) + self.a4 = QAction('禁用(Disable)', checkable=True) + self.logMenu.addAction(self.a3) + self.logMenu.addAction(self.a4) + self.tpMenu = QMenu() + self.tpMenu.addAction(self.a1) + self.tpMenu.addMenu(self.logMenu) + self.tpMenu.addAction(self.a2) + self.current_page = self.first_widget self.config_setting_widget.hide() self.system_setting_widget.hide() @@ -563,14 +576,20 @@ def setupUi(self, SystemSettings): self.label_7.setStyleSheet("font: 13pt \"Purisa\";") self.label_7.setObjectName("label_7") self.label_6 = QLabel(SystemSettings) - self.label_6.setGeometry(QRect(60, 170, 461, 17)) + self.label_6.setGeometry(QRect(60, 170, 500, 17)) self.label_6.setStyleSheet("font: 10pt \"Purisa\";\n" "color: rgb(186, 189, 182);") self.label_6.setObjectName("label_6") self.label_8 = QLabel(SystemSettings) - self.label_8.setGeometry(QRect(320, 375, 201, 71)) + self.label_8.setGeometry(QRect(180, 375, 201, 71)) self.label_8.setObjectName("label_8") + # self.label_9 = QLabel(SystemSettings) + # self.label_9.setGeometry(QRect(300, 380, 231, 21)) + # self.label_9.setStyleSheet("font: 13pt \"Purisa\";") + # self.label_9.setObjectName("label_7") + + self.http_sp = QSpinBox(SystemSettings) self.http_sp.setGeometry(QRect(130, 124, 80, 30)) self.http_sp.setMinimum(1080) @@ -586,6 +605,7 @@ def setupUi(self, SystemSettings): self.switchBtn = SwitchBtn(self.label_8, True) self.switchBtn.setGeometry(0, 0, 60, 30) + self.retranslateUi(SystemSettings) QMetaObject.connectSlotsByName(SystemSettings) @@ -598,11 +618,11 @@ def retranslateUi(self, SystemSettings): self.label_3.setText(_translate("SystemSettings", "Socks:")) self.label_4.setText(_translate("SystemSettings", "版本更新")) self.label_5.setText(_translate("SystemSettings", "当前版本:")) - self.version_label.setText(_translate("SystemSettings", "v2.1.0")) + self.version_label.setText(_translate("SystemSettings", "v2.1.1")) self.checkupdateButton.setText(_translate("SystemSettings", "检查更新")) - self.label_7.setText(_translate("SystemSettings", "程序启动时自动进行检查更新")) - self.label_6.setText(_translate("SystemSettings", "**端口可选范围:1080-10080,每次修改都将更新**")) - # self.label_9.setText(_translate("SystemSettings", "1081")) + self.label_7.setText(_translate("SystemSettings", "自动检查更新")) + self.label_6.setText(_translate("SystemSettings", "**端口可选范围:1080-10080,每次修改都将更新。**")) + # self.label_9.setText(_translate("SystemSettings", "开机自动连接")) # self.label_10.setText(_translate("SystemSettings", "1080")) @@ -630,7 +650,7 @@ def retranslateUi(self, HelpUi): "\n" "

当前版本

\n" "

——————————————————————

\n" - "

v2.1.0

\n" + "

v2.1.1

\n" "


\n" "

说明

\n" "

——————————————————————

")) @@ -976,6 +996,7 @@ def setupUi(self, Dialog): self.comboBox.setObjectName("comboBox") self.comboBox.addItem("") self.comboBox.addItem("") + self.comboBox.addItem("") self.label_8 = QLabel(Dialog) self.label_8.setGeometry(QRect(30, 286, 221, 21)) self.label_8.setObjectName("label_8") @@ -990,6 +1011,13 @@ def setupUi(self, Dialog): self.comboBox_2.addItem("") self.comboBox_2.addItem("") self.comboBox_2.addItem("") + + self.comboBox_3 = QComboBox(Dialog) + self.comboBox_3.setGeometry(QRect(150, 437, 86, 25)) + # self.comboBox_2.setStyleSheet("background-color: rgb(255, 255, 255);") + self.comboBox_3.setObjectName("comboBox_2") + self.comboBox_3.addItem("") + self.comboBox_3.addItem("") self.label_9 = QLabel(Dialog) self.label_9.setGeometry(QRect(30, 365, 121, 17)) self.label_9.setObjectName("label_9") @@ -999,10 +1027,13 @@ def setupUi(self, Dialog): self.lineEdit_6.setText("") self.lineEdit_6.setObjectName("lineEdit_6") self.label_10 = QLabel(Dialog) - self.label_10.setGeometry(QRect(30, 410, 67, 17)) + self.label_10.setGeometry(QRect(30, 405, 67, 19)) self.label_10.setObjectName("label_10") + self.label_11 = QLabel(Dialog) + self.label_11.setGeometry(QRect(30, 437, 125, 20)) + self.label_11.setObjectName("label_11") self.lineEdit_7 = QLineEdit(Dialog) - self.lineEdit_7.setGeometry(QRect(80, 405, 361, 25)) + self.lineEdit_7.setGeometry(QRect(80, 400, 361, 25)) self.lineEdit_7.setStyleSheet("border-style:none none solid none;background-color:transparent;") self.lineEdit_7.setText("") self.lineEdit_7.setObjectName("lineEdit_7") @@ -1012,37 +1043,37 @@ def setupUi(self, Dialog): self.line.setFrameShadow(QFrame.Sunken) self.line.setObjectName("line") self.line_2 = QFrame(Dialog) - self.line_2.setGeometry(QRect(120, 90, 321, 16)) + self.line_2.setGeometry(QRect(120, 88, 321, 16)) self.line_2.setFrameShape(QFrame.HLine) self.line_2.setFrameShadow(QFrame.Sunken) self.line_2.setObjectName("line_2") self.line_3 = QFrame(Dialog) - self.line_3.setGeometry(QRect(120, 130, 321, 16)) + self.line_3.setGeometry(QRect(120, 128, 321, 16)) self.line_3.setFrameShape(QFrame.HLine) self.line_3.setFrameShadow(QFrame.Sunken) self.line_3.setObjectName("line_3") self.line_4 = QFrame(Dialog) - self.line_4.setGeometry(QRect(120, 170, 321, 16)) + self.line_4.setGeometry(QRect(120, 168, 321, 16)) self.line_4.setFrameShape(QFrame.HLine) self.line_4.setFrameShadow(QFrame.Sunken) self.line_4.setObjectName("line_4") self.line_5 = QFrame(Dialog) - self.line_5.setGeometry(QRect(150, 210, 291, 20)) + self.line_5.setGeometry(QRect(150, 208, 291, 20)) self.line_5.setFrameShape(QFrame.HLine) self.line_5.setFrameShadow(QFrame.Sunken) self.line_5.setObjectName("line_5") self.line_6 = QFrame(Dialog) - self.line_6.setGeometry(QRect(150, 380, 291, 20)) + self.line_6.setGeometry(QRect(150, 378, 291, 20)) self.line_6.setFrameShape(QFrame.HLine) self.line_6.setFrameShadow(QFrame.Sunken) self.line_6.setObjectName("line_6") self.line_7 = QFrame(Dialog) - self.line_7.setGeometry(QRect(80, 420, 361, 20)) + self.line_7.setGeometry(QRect(80, 415, 361, 20)) self.line_7.setFrameShape(QFrame.HLine) self.line_7.setFrameShadow(QFrame.Sunken) self.line_7.setObjectName("line_7") self.pushButton = QPushButton(Dialog) - self.pushButton.setGeometry(QRect(160, 464, 151, 31)) + self.pushButton.setGeometry(QRect(160, 475, 151, 31)) self.pushButton.setStyleSheet("#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 13px; }\n" "#pushButton:hover{ background: #5599FF;}") self.pushButton.setObjectName("pushButton") @@ -1062,6 +1093,7 @@ def retranslateUi(self, Dialog): self.label_7.setText(_translate("Dialog", "伪装类型(type):")) self.comboBox.setItemText(0, _translate("Dialog", "ws")) self.comboBox.setItemText(1, _translate("Dialog", "kcp")) + self.comboBox.setItemText(2, _translate("Dialog", "tcp")) self.label_8.setText(_translate("Dialog", "下列配置无则保持默认")) self.comboBox_2.setItemText(0, _translate("Dialog", "none")) self.comboBox_2.setItemText(1, _translate("Dialog", "http")) @@ -1070,8 +1102,11 @@ def retranslateUi(self, Dialog): self.comboBox_2.setItemText(4, _translate("Dialog", "dtls")) self.comboBox_2.setItemText(5, _translate("Dialog", "strp")) self.comboBox_2.setItemText(6, _translate("Dialog", "wireguard")) + self.comboBox_3.setItemText(0, _translate("Dialog", "")) + self.comboBox_3.setItemText(1, _translate("Dialog", "tls")) self.label_9.setText(_translate("Dialog", "伪装域名(host):")) self.label_10.setText(_translate("Dialog", "path:")) + self.label_11.setText(_translate("Dialog", "底层传输安全:")) self.pushButton.setText(_translate("Dialog", "确认添加")) def main(): diff --git a/v2rayL-GUI/sub2conf_api.py b/v2rayL-GUI/sub2conf_api.py index 295d9eb..132ac7a 100755 --- a/v2rayL-GUI/sub2conf_api.py +++ b/v2rayL-GUI/sub2conf_api.py @@ -41,6 +41,13 @@ def __init__(self, subs_url=None, conf_url=None): ''' def b642conf(self, prot, tp, b64str): + """ + base64转化为dict类型配置 + :param prot: + :param tp: + :param b64str: + :return: + """ if prot == "vmess": ret = eval(parse.unquote(base64.b64decode(b64str).decode())) region = ret['ps'] @@ -67,6 +74,13 @@ def b642conf(self, prot, tp, b64str): self.saved_conf[["local", "subs"][tp]][region] = ret def setconf(self, region, http, socks): + """ + 生成配置 + :param region: 当前VPN别名 + :param http: http端口 + :param socks: socks端口 + :return: + """ use_conf = self.conf[region] conf = copy.deepcopy(tpl) conf["inbounds"][0]["port"] = socks @@ -94,7 +108,7 @@ def setconf(self, region, http, socks): "security": "tls" if use_conf["tls"] else "", "tlssettings": { "allowInsecure": True, - "serverName": use_conf["tls"] + "serverName": use_conf["host"] }, "wssettings": { "connectionReuse": True, @@ -122,6 +136,46 @@ def setconf(self, region, http, socks): }, "security": "" } + # tcp + elif use_conf["net"] == "tcp": + conf['outbounds'][0]["streamSettings"] = { + "network": use_conf["net"], + "security": "", + "tcpsettings": { + "connectionReuse": True, + "header": { + "request": { + "version": "1.1", + "method": "GET", + "path": [use_conf["path"]], + "headers": { + "User-Agent": ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"], + "Accept-Encoding": ["gzip, deflate"], + "Connection": ["keep-alive"], + "Pragma": "no-cache", + "Host": [use_conf["host"]] + } + }, + "type": use_conf["type"] + } + } if use_conf["type"] != "none" else {} + } + # QUIC + # elif use_conf["net"] == "quic": + # conf['outbounds'][0]["streamSettings"] = { + # "network": use_conf["net"], + # "security": "tls" if use_conf["tls"] else "none", + # "tlssettings": { + # "allowInsecure": True, + # "serverName": use_conf["host"] + # }, + # "quicsettings": { + # "headers": { + # "type": use_conf['type'] + # }, + # "key": + # } + # } elif use_conf['prot'] == "shadowsocks": conf['outbounds'][0]["protocol"] = "shadowsocks" @@ -141,6 +195,11 @@ def setconf(self, region, http, socks): f.write(json.dumps(conf, indent=4)) def delconf(self, region): + """ + 删除一个配置 + :param region: 配置名 + :return: + """ self.conf.pop(region) try: self.saved_conf['local'].pop(region) @@ -157,15 +216,15 @@ def update(self): 更新订阅 """ try: - ret = requests.get(self.subs_url) + ret = requests.get(self.subs_url, timeout=15) if ret.status_code != 200: raise MyException("无法获取订阅信息,订阅站点访问失败") all_subs = base64.b64decode(ret.text + "==").decode().strip() if "vmess" not in all_subs or "ss" not in all_subs: raise MyException("解析订阅信息失败,请确认链接正确") all_subs = all_subs.split("\n") - except: - raise MyException("解析订阅信息失败") + except Exception as e: + raise MyException(e.args[0]) for sub in all_subs: self.origin.append(sub.split("://")) diff --git a/v2rayL-GUI/utils.py b/v2rayL-GUI/utils.py new file mode 100644 index 0000000..4e432bb --- /dev/null +++ b/v2rayL-GUI/utils.py @@ -0,0 +1,79 @@ +# -*- coding:utf-8 -*- +# Author: Suummmmer +# Date: 2019-10-24 + +import sys +import datetime +from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox, qApp +from PyQt5.QtGui import QIcon +from PyQt5.QtCore import ( + QtInfoMsg, + QtWarningMsg, + QtCriticalMsg, + QtFatalMsg +) + + +class SystemTray(object): + # 程序托盘类 + def __init__(self, w, app): + self.app = app + self.w = w + QApplication.setQuitOnLastWindowClosed(False) # 禁止默认的closed方法, + self.w.show() # 不设置显示则为启动最小化到托盘 + self.tp = QSystemTrayIcon(self.w) + self.initUI() + self.run() + + # sys.stdout = EmittingStream(textWritten=self.w.output_ter_result) + # sys.stderr = EmittingStream(textWritten=self.w.output_ter_result) + + def initUI(self): + # 设置托盘图标 + self.tp.setIcon(QIcon('/etc/v2rayL/images/logo.ico')) + + def quitApp(self): + # 退出程序 + self.w.show() # w.hide() #设置退出时是否显示主窗口 + re = QMessageBox.question(self.w, "提示", "确认退出?", QMessageBox.Yes | + QMessageBox.No, QMessageBox.No) + if re == QMessageBox.Yes: + self.tp.setVisible(False) # 隐藏托盘控件 + qApp.quit() # 退出程序 + self.w.v2rayL.disconnect() + + def act(self, reason): + # 主界面显示方法 + if reason == 2 or reason == 3: + self.w.show() + + def run(self): + self.w.a2.triggered.connect(self.quitApp) + self.tp.setContextMenu(self.w.tpMenu) + self.tp.show() # 不调用show不会显示系统托盘消息,图标隐藏无法调用 + + # 绑定提醒信息点击事件 + # self.tp.messageClicked.connect(self.message) + # 绑定托盘菜单点击事件 + self.tp.activated.connect(self.act) + sys.exit(self.app.exec_()) # 持续对app的连接 + + +def qt_message_handler(mode, context, message): + if mode == QtInfoMsg: + mode = 'INFO' + elif mode == QtWarningMsg: + mode = 'WARNING' + elif mode == QtCriticalMsg: + mode = 'CRITICAL' + elif mode == QtFatalMsg: + mode = 'FATAL' + else: + mode = 'DEBUG' + + en, info = message.split("@$ff$@") + if en == "True": + with open("/etc/v2rayL/v2rayL_op.log", "a+") as f: + f.write(' %s - %s: %s\n' % (datetime.datetime.now(), mode, info)) + +# https://sub.qianglie.xyz/subscribe.php?sid=5582&token=g6e8VdU6C40H \ No newline at end of file diff --git a/v2rayL-GUI/v2rayL_api.py b/v2rayL-GUI/v2rayL_api.py index 3b86a7b..7e5841d 100755 --- a/v2rayL-GUI/v2rayL_api.py +++ b/v2rayL-GUI/v2rayL_api.py @@ -8,56 +8,121 @@ from sub2conf_api import Sub2Conf, MyException +class CurrentStatus(object): + """ + 保存当前状态 + """ + def __init__(self, current="未连接至VPN", url=None, auto=False, check=False, + http=1081, socks=1080, log=True): + """ + :param current: 当前连接状态 + :param url: 当前订阅地址 + :param auto: 是否开启自动更新订阅 + :param check: 是否开启自动检查更新 + :param http: http监听端口 + :param socks: socks监听端口 + :param log: 是否启用v2rayL日志 + """ + self.current = current + self.url = url + self.auto = auto + self.check = check + self.http = http + self.socks = socks + self.log = log + + class V2rayL(object): def __init__(self): try: with open("/etc/v2rayL/ncurrent", "rb") as f: - self.current, self.url, self.auto, self.check, self.http, self.socks = pickle.load(f) + # self.current, self.url, self.auto, self.check, self.http, self.socks, self.log = pickle.load(f) + self.current_status = pickle.load(f) except: - self.current = "未连接至VPN" - self.url = None - self.auto = False - self.check = False - self.http = 1081 - self.socks = 1080 - - self.subs = Sub2Conf(subs_url=self.url) - - if self.auto and self.url: - try: - self.subs.update() - except: - with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump(("未连接至VPN", None, False, False, - self.http, self.socks), jf) - raise MyException("更新失败, 已关闭自动更新,请更新订阅地址") + # self.current = "未连接至VPN" + # self.url = None + # self.auto = False + # self.check = False + # self.http = 1081 + # self.socks = 1080 + # self.log = True + self.current_status = CurrentStatus() + + self.subs = Sub2Conf(subs_url=self.current_status.url) + + # if self.current_status.auto and self.current_status.url: + # try: + # self.subs.update() + # except: + # with open("/etc/v2rayL/ncurrent", "wb") as jf: + # self.current_status = CurrentStatus(http=self.current_status.http, + # socks=self.current_status.socks, + # log=self.current_status.log) + # pickle.dump(self.current_status, jf) + # raise MyException("更新失败, 已关闭自动更新,请更新订阅地址") def auto_check(self, flag): + """ + 启用/禁用自动检查更新 + """ if flag: with open("/etc/v2rayL/ncurrent", "wb") as jf: - self.check = True - pickle.dump((self.current, self.url, self.auto, self.check), jf) + self.current_status.check = True + pickle.dump(self.current_status, jf) else: with open("/etc/v2rayL/ncurrent", "wb") as jf: - self.check = False - pickle.dump((self.current, self.url, self.auto, self.check), jf) + self.current_status.check = False + pickle.dump(self.current_status, jf) def subscribe(self, flag): + """ + 启用/禁用自动更新订阅 + """ + if flag: + with open("/etc/v2rayL/ncurrent", "wb") as jf: + self.current_status.auto = True + pickle.dump(self.current_status, jf) + else: + with open("/etc/v2rayL/ncurrent", "wb") as jf: + self.current_status.auto = False + pickle.dump(self.current_status, jf) + + def logging(self, flag): + """ + 启用/禁用日志 + """ if flag: with open("/etc/v2rayL/ncurrent", "wb") as jf: - self.auto = True - pickle.dump((self.current, self.url, self.auto, self.check, - self.http, self.socks), jf) + self.current_status.log = True + pickle.dump(self.current_status, jf) else: with open("/etc/v2rayL/ncurrent", "wb") as jf: - self.auto = False - pickle.dump((self.current, self.url, self.auto, self.check, - self.http, self.socks), jf) + self.current_status.log = False + pickle.dump(self.current_status, jf) + + # def auto_on(self, flag): + # """ + # 启用/禁用开机自动连接 + # """ + # if flag: + # with open("/etc/v2rayL/ncurrent", "wb") as jf: + # self.current_status.on = True + # pickle.dump(self.current_status, jf) + # else: + # with open("/etc/v2rayL/ncurrent", "wb") as jf: + # self.current_status.on = False + # pickle.dump(self.current_status, jf) def connect(self, region, flag): + """ + 连接VPN + :param region: VPN别名 + :param flag: 是否是正常连接/更新端口重连 + :return: + """ if not flag: - self.subs.setconf(region, self.http, self.socks) + self.subs.setconf(region, self.current_status.http, self.current_status.socks) try: output = subprocess.getoutput(["sudo systemctl status v2rayL.service"]) if "Active: active" in output: @@ -70,70 +135,86 @@ def connect(self, region, flag): except: raise MyException("连接失败,请尝试更新订阅后再次连接或检查v2rayL.service是否正常运行") else: - self.current = region + self.current_status.current = region with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, self.url, self.auto, self.check, - self.http, self.socks), jf) + pickle.dump(self.current_status, jf) def disconnect(self): + """ + 断开连接 + """ try: output = subprocess.getoutput(["sudo systemctl status v2rayL.service"]) if "Active: active" in output: subprocess.call(["sudo systemctl stop v2rayL.service"], shell=True) - self.current = "未连接至VPN" + self.current_status.current = "未连接至VPN" with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, self.url, self.auto, self.check, - self.http, self.socks), jf) + pickle.dump(self.current_status, jf) else: - if self.current != "未连接至VPN": - self.current = "未连接至VPN" + if self.current_status.current != "未连接至VPN": + self.current_status.current = "未连接至VPN" with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, self.url, self.auto, self.check, - self.http, self.socks), jf) + pickle.dump(self.current_status, jf) else: raise MyException("服务未开启,无需断开连接.") except Exception as e: raise MyException(e.args[0]) def update(self, url): + """ + 更新订阅 + """ if url: # 如果存在订阅地址 self.subs = Sub2Conf(subs_url=url) self.subs.update() with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, url, self.auto, self.check, - self.http, self.socks), jf) + self.current_status.url = url + pickle.dump(pickle.dump(self.current_status, jf), jf) else: - if self.current in self.subs.saved_conf["subs"]: + if self.current_status.current in self.subs.saved_conf["subs"]: try: self.disconnect() except: pass with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump(("未连接至VPN", None, False, False, - self.http, self.socks), jf) + self.current_status = CurrentStatus(http=self.current_status.http, + socks=self.current_status.socks, + log=self.current_status.log) + pickle.dump(self.current_status, jf) else: with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.current, None, False, False, - self.http, self.socks), jf) + self.current_status = CurrentStatus(http=self.current_status.http, + socks=self.current_status.socks, + log=self.current_status.log) + pickle.dump(self.current_status, jf) with open("/etc/v2rayL/data", "wb") as f: pickle.dump({"local": self.subs.saved_conf["local"], "subs": {}}, f) def addconf(self, uri): + """ + 删除配置 + """ self.subs = Sub2Conf(conf_url=uri) self.subs.add_conf_by_uri() def delconf(self, region): + """ + 删除配置 + """ self.subs.delconf(region) def ping(self): + """ + 测试延时 + """ try: proxy = { - "http": "127.0.0.1:{}".format(self.http), - "https": "127.0.0.1:{}".format(self.http) + "http": "127.0.0.1:{}".format(self.current_status.http), + "https": "127.0.0.1:{}".format(self.current_status.http) } req = requests.get("http://www.google.com", proxies=proxy, timeout=10) if req.status_code == 200: diff --git a/v2rayL-GUI/v2rayL_threads.py b/v2rayL-GUI/v2rayL_threads.py index 2c513c1..560ba5e 100755 --- a/v2rayL-GUI/v2rayL_threads.py +++ b/v2rayL-GUI/v2rayL_threads.py @@ -2,7 +2,7 @@ # Author: Suummmmer # Date: 2019-08-13 -from PyQt5.QtCore import QThread, pyqtSignal +from PyQt5.QtCore import QThread, pyqtSignal, qCritical from sub2conf_api import MyException import subprocess import requests @@ -82,9 +82,9 @@ def run(self): except MyException as e: self.sinOut.emit(("addr", "@@Fail@@", e.args[0], None)) else: - self.sinOut.emit(("addr", "@@OK@@", "订阅地址更新成功!", None)) + self.sinOut.emit(("addr", "@@OK@@", url, None)) else: - url = self.v2rayL.url + url = self.v2rayL.current_status.url if not url: self.sinOut.emit(("update", "@@Fail@@", "不存在订阅地址,无法更新", None)) else: @@ -93,7 +93,7 @@ def run(self): except MyException as e: self.sinOut.emit(("update", "@@Fail@@", e.args[0], None)) else: - self.sinOut.emit(("update", "@@OK@@", "订阅更新成功!", None)) + self.sinOut.emit(("update", "@@OK@@", url, None)) class PingThread(QThread): @@ -176,10 +176,36 @@ def run(self): with open("/etc/v2rayL/update.sh", 'w') as f: f.write(req.text) - subprocess.call(["chmod +x /etc/v2rayL/update.sh && /etc/v2rayL/update.sh"], shell=True) + subprocess.call(["chmod +x /etc/v2rayL/update.sh && /etc/v2rayL/upda.sh"], shell=True) self.sinOut.emit(("vrud", "@@OK@@", "更新完成, 重启程序生效.", None)) except Exception: self.sinOut.emit(("vrud", "@@Fail@@", "网络错误,请检查网络连接或稍后再试.", None)) + +class RunCmdThread(QThread): + """ + 第一次启动时运行cmd线程 + """ + sinOut = pyqtSignal(tuple) + + def __init__(self, parent=None): + super(RunCmdThread, self).__init__(parent) + + def __del__(self): + # 线程状态改变与线程终止 + self.wait() + + def run(self): + # print("start") + try: + output = subprocess.getoutput(["sudo systemctl status v2rayL.service"]) + if "Active: active" in output: + subprocess.call(["sudo systemctl restart v2rayL.service"], shell=True) + else: + subprocess.call(["sudo systemctl start v2rayL.service"], shell=True) + # subprocess.call(["sudo systemctl stop v2rayL.service"], shell=True) + # print("end") + except Exception as e: + qCritical(e) diff --git a/v2rayL-GUI/v2rayLui.py b/v2rayL-GUI/v2rayLui.py index 0c06e2a..b4e9df8 100644 --- a/v2rayL-GUI/v2rayLui.py +++ b/v2rayL-GUI/v2rayLui.py @@ -7,19 +7,15 @@ import base64 import requests import subprocess +import datetime import pickle from PyQt5.QtWidgets import ( - QMenu, - QAction, QApplication, QMessageBox, QFileDialog, - QSystemTrayIcon, - qApp, - QDialog ) -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QIcon, QPixmap +from PyQt5.QtCore import Qt, qInfo, qInstallMessageHandler +from PyQt5.QtGui import QPixmap from v2rayL_api import V2rayL, MyException import pyzbar.pyzbar as pyzbar from PIL import Image @@ -29,63 +25,19 @@ UpdateSubsThread, PingThread, CheckUpdateThread, - VersionUpdateThread + VersionUpdateThread, + RunCmdThread ) from new_ui import MainUi, SwitchBtn, Ui_Add_Ss_Dialog, Ui_Add_Vmess_Dialog - - -class SystemTray(object): - # 程序托盘类 - def __init__(self, w): - self.app = app - self.w = w - QApplication.setQuitOnLastWindowClosed(False) # 禁止默认的closed方法, - self.w.show() # 不设置显示则为启动最小化到托盘 - self.tp = QSystemTrayIcon(self.w) - self.initUI() - self.run() - - def initUI(self): - # 设置托盘图标 - self.tp.setIcon(QIcon('/etc/v2rayL/images/logo.ico')) - - def quitApp(self): - # 退出程序 - self.w.show() # w.hide() #设置退出时是否显示主窗口 - re = QMessageBox.question(self.w, "提示", "确认退出?", QMessageBox.Yes | - QMessageBox.No, QMessageBox.No) - if re == QMessageBox.Yes: - self.tp.setVisible(False) # 隐藏托盘控件 - qApp.quit() # 退出程序 - self.w.v2rayL.disconnect() - - def act(self, reason): - # 主界面显示方法 - if reason == 2 or reason == 3: - self.w.show() - - def run(self): - a1 = QAction('恢复(Show)', triggered=self.w.show) - a3 = QAction('退出(Exit)', triggered=self.quitApp) - tpMenu = QMenu() - tpMenu.addAction(a1) - tpMenu.addAction(a3) - self.tp.setContextMenu(tpMenu) - self.tp.show() # 不调用show不会显示系统托盘消息,图标隐藏无法调用 - - # 绑定提醒信息点击事件 - # self.tp.messageClicked.connect(self.message) - # 绑定托盘菜单点击事件 - self.tp.activated.connect(self.act) - sys.exit(self.app.exec_()) # 持续对app的连接 +from utils import SystemTray, qt_message_handler class MyMainWindow(MainUi): def __init__(self, parent=None): super(MyMainWindow, self).__init__(parent) - self.init_ui() - self.version = "2.1.0" + self.init_ui() + self.version = "2.1.1" # 获取api操作 self.v2rayL = V2rayL() @@ -102,7 +54,11 @@ def __init__(self, parent=None): self.check_update_start = CheckUpdateThread(version=self.version) # 更新版本线程 self.version_update_start = VersionUpdateThread() - if self.v2rayL.auto: + # CMD线程 + self.run_cmd_start = RunCmdThread() + self.run_cmd_start.start() + + if self.v2rayL.current_status.auto: self.config_setting_ui.switchBtn = SwitchBtn(self.config_setting_ui.label_9, True) self.config_setting_ui.switchBtn.setGeometry(0, 0, 60, 30) else: @@ -110,19 +66,21 @@ def __init__(self, parent=None): self.config_setting_ui.switchBtn.setGeometry(0, 0, 60, 30) # 自动更新订阅 - if self.v2rayL.auto and self.v2rayL.url: + if self.v2rayL.current_status.auto and self.v2rayL.current_status.url: try: self.update_subs_start.v2rayL = self.v2rayL self.update_subs_start.subs_child_ui = None self.update_subs_start.start() except: with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.v2rayL.current, None, False, self.v2rayL.check), jf) + self.v2rayL.current_status.url = None + self.v2rayL.current_status.auto = False + pickle.dump(self.v2rayL.current_status, jf) shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL '{}'".format("更新失败, 已关闭自动更新,请更新订阅地址") subprocess.call([shell], shell=True) # 自动检查更新 - if self.v2rayL.check: + if self.v2rayL.current_status.check: self.system_setting_ui.switchBtn = SwitchBtn(self.system_setting_ui.label_8, True) self.system_setting_ui.switchBtn.setGeometry(0, 0, 60, 30) self.check_update_start.start() @@ -130,18 +88,29 @@ def __init__(self, parent=None): self.system_setting_ui.switchBtn = SwitchBtn(self.system_setting_ui.label_8, False) self.system_setting_ui.switchBtn.setGeometry(0, 0, 60, 30) + # 是否启用日志 + if self.v2rayL.current_status.log: + self.a3.setChecked(True) + self.a4.setChecked(False) + else: + self.a4.setChecked(True) + self.a3.setChecked(False) + # 填充当前订阅地址 - self.config_setting_ui.lineEdit.setText(self.v2rayL.url) + self.config_setting_ui.lineEdit.setText(self.v2rayL.current_status.url) # 端口 - self.system_setting_ui.http_sp.setValue(self.v2rayL.http) - self.system_setting_ui.socks_sp.setValue(self.v2rayL.socks) + self.system_setting_ui.http_sp.setValue(self.v2rayL.current_status.http) + self.system_setting_ui.socks_sp.setValue(self.v2rayL.current_status.socks) # # 显示当前所有配置 self.display_all_conf() + qInfo("{}@$ff$@app start.".format(self.v2rayL.current_status.log)) + # self.ping_start = PingThread(tv=(self.tableView, self.v2rayL)) # 事件绑定 self.config_setting_ui.switchBtn.checkedChanged.connect(self.change_auto_update) self.system_setting_ui.switchBtn.checkedChanged.connect(self.change_check_update) + # self.system_setting_ui.switchBtn1.checkedChanged.connect(self.auto_on) self.first_ui.pushButton.clicked.connect(self.update_subs) # 更新订阅 self.config_setting_ui.pushButton_2.clicked.connect(self.output_conf) # 导出配置文件 self.config_setting_ui.pushButton.clicked.connect(self.get_conf_from_qr) # 通过二维码导入 @@ -163,10 +132,22 @@ def __init__(self, parent=None): self.config_setting_ui.pushButton_vmess.clicked.connect(self.show_add_vmess_dialog) self.ss_add_child_ui.pushButton.clicked.connect(self.add_ss_by_input) self.vmess_add_child_ui.pushButton.clicked.connect(self.add_vmess_by_input) + self.a1.triggered.connect(self.show) + self.a3.triggered.connect(self.enable_log) + self.a4.triggered.connect(self.disable_log) + + # print("hahh") # 设置最小化到托盘 - SystemTray(self) + SystemTray(self, app) + + # print("aaaaa") def check_update(self): + """ + 检查版本更新 + :return: + """ + print("hahah") shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在检查版本更新." subprocess.call([shell], shell=True) self.check_update_start.start() @@ -176,12 +157,13 @@ def display_all_conf(self): 列出所有的可用配置 :return: """ + # print("Okokok") all_conf = self.v2rayL.subs.conf lists = [] i = 1 for k, v in all_conf.items(): lists.append((i, k, str(v["add"])+":"+str(v["port"]), v["prot"], - True if k == self.v2rayL.current else False, self.start_conn_th, + True if k == self.v2rayL.current_status.current else False, self.start_conn_th, self.del_conf, self.show_share_dialog)) i += 1 self.first_ui.tableWidget.setRowCount(i-1) @@ -207,9 +189,9 @@ def change_subs_addr(self): subprocess.call([shell], shell=True) self.update_addr_start.start() else: - self.config_setting_ui.lineEdit.setText(self.v2rayL.url) + self.config_setting_ui.lineEdit.setText(self.v2rayL.current_status.url) else: - if url == self.v2rayL.url: + if url == self.v2rayL.current_status.url: shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 订阅地址未改变" subprocess.call([shell], shell=True) else: @@ -224,7 +206,7 @@ def update_subs(self): """ self.update_subs_start.v2rayL = self.v2rayL self.update_subs_start.subs_child_ui = None - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅...... -t 1" + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅......" subprocess.call([shell], shell=True) self.update_subs_start.start() @@ -298,7 +280,7 @@ def del_conf(self, row): except AttributeError: QMessageBox.information(self, "移除通知", self.tr("未选择任何配置.")) else: - if self.v2rayL.current == region: + if self.v2rayL.current_status.current == region: QMessageBox.information(self, "移除通知", self.tr("当前配置正在使用,无法移除.")) else: try: @@ -336,27 +318,34 @@ def alert(self, tp): # QMessageBox.information(self, "连接成功", self.tr("连接成功!当前状态: " + ret)) shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL '连接成功!当前状态: " + ret + "'" subprocess.call([shell], shell=True) + qInfo("{}@$ff$@Successfully connected to: {}".format(self.v2rayL.current_status.log, ret).encode()) self.display_all_conf() elif tp == "disconn": shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL VPN连接已断开" subprocess.call([shell], shell=True) + qInfo("{}@$ff$@VPN connection disconnected.".format(self.v2rayL.current_status.log)) self.display_all_conf() elif tp == "addr": shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 更新订阅地址成功" subprocess.call([shell], shell=True) + qInfo("{}@$ff$@Subscription address change to {}".format(self.v2rayL.current_status.log, ret)) self.v2rayL = V2rayL() self.display_all_conf() elif tp == "update": shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 订阅更新完成" subprocess.call([shell], shell=True) + qInfo("{}@$ff$@Successfully updated subscription.".format(self.v2rayL.current_status.log)) self.v2rayL = V2rayL() self.display_all_conf() elif tp == "ping": - self.first_ui.time.setText(str(ret)+"ms") + if isinstance(ret, int): + self.first_ui.time.setText(str(ret)+"ms") + else: + self.first_ui.time.setText(ret) elif tp == "ckud": if not row: @@ -372,16 +361,26 @@ def alert(self, tp): shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL {}".format(ret) subprocess.call([shell], shell=True) self.version_update_start.url = "http://dl.thinker.ink/update.sh" + qInfo("{}@$ff$@Ready to update, the latest version number is: v{}.".format( + self.v2rayL.current_status.log, row.json()['tag_name'])) self.version_update_start.start() elif tp == "vrud": shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL '{}'".format(ret) subprocess.call([shell], shell=True) + qInfo("{}@$ff$@Successfully updated to the latest version.".format(self.v2rayL.current_status.log)) else: if tp == "addr": - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 地址设置错误" + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL '{}'".format(ret) subprocess.call([shell], shell=True) + if ret == "无法获取订阅信息,订阅站点访问失败": + ret = "Failed to access subscription site, unable to get subscription information" + elif ret == "解析订阅信息失败,请确认链接正确": + ret = "Failed to resolve subscription information, please confirm the link is correct" + else: + pass + qInfo("{}@$ff$@Failed to get subscriptions: {}.".format(self.v2rayL.current_status.log, ret)) elif tp == "conn": shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL {}".format(ret) @@ -401,6 +400,9 @@ def alert(self, tp): shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL {}".format(ret) subprocess.call([shell], shell=True) + elif tp == "ping": + self.first_ui.time.setText(str(ret)) + def output_conf(self): """ 导出配置文件 @@ -422,83 +424,93 @@ def change_auto_update(self): 是否自动更新订阅 :return: """ - if self.v2rayL.auto: + if self.v2rayL.current_status.auto: self.v2rayL.subscribe(False) + qInfo("{}@$ff$@Automatic update subscription is disabled.".format(self.v2rayL.current_status.log)) else: - if not self.v2rayL.url: + if not self.v2rayL.current_status.url: shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 不存在订阅地址,无需开启自动更新订阅" subprocess.call([shell], shell=True) self.v2rayL.subscribe(True) + qInfo("{}@$ff$@Automatic update subscription is enabled.".format(self.v2rayL.current_status.log)) def change_check_update(self): """ 是否自动检查更新 :return: """ - if self.v2rayL.check: + if self.v2rayL.current_status.check: self.v2rayL.auto_check(False) + qInfo("{}@$ff$@Automatically check for version updates disabled.".format(self.v2rayL.current_status.log)) else: self.v2rayL.auto_check(True) + qInfo("{}@$ff$@Automatically check for version updates enabled.".format(self.v2rayL.current_status.log)) def start_ping_th(self): """ 开始测延时 :return: """ + self.first_ui.time.setText("正在测试") self.ping_start.v2rayL = self.v2rayL self.ping_start.start() def show_share_dialog(self, region): + """ + 展示分享窗口 + :param region: + :return: + """ ret = self.v2rayL.subs.conf2b64(region) # 生成二维码 url = "http://api.k780.com:88/?app=qr.get&data={}".format(ret) try: - req = requests.get(url) + req = requests.get(url, timeout=2) if req.status_code == 200: qr = QPixmap() qr.loadFromData(req.content) self.share_child_ui.label.setPixmap(qr) self.share_child_ui.label.setScaledContents(True) else: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,可能原因:调用API发生错误" + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 生成二维码失败,可能原因:调用API发生错误" subprocess.call([shell], shell=True) except: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,请将错误在github中提交" + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 生成二维码失败,请将错误在github中提交" subprocess.call([shell], shell=True) self.share_child_ui.textBrowser.setText(ret) self.share_ui.show() - def output_conf_by_uri(self, region): - """ - 输出分享链接 - :return: - """ - ret = self.v2rayL.subs.conf2b64(region) - # QMessageBox.information(self, "分享链接", self.tr(ret)) - - def output_conf_by_qr(self, region): - """ - 输出分享二维码 - :return: - """ - ret = self.v2rayL.subs.conf2b64(region) - # 生成二维码 - url = "http://api.k780.com:88/?app=qr.get&data={}".format(ret) - try: - req = requests.get(url) - if req.status_code == 200: - qr = QPixmap() - qr.loadFromData(req.content) - self.qr_child_ui.label.setPixmap(qr) - self.qr_child_ui.label.setScaledContents(True) - self.qr_ui.show() - else: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,可能原因:调用API发生错误" - subprocess.call([shell], shell=True) - except: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,请将错误在github中提交" - subprocess.call([shell], shell=True) + # def output_conf_by_uri(self, region): + # """ + # 输出分享链接 + # :return: + # """ + # ret = self.v2rayL.subs.conf2b64(region) + # # QMessageBox.information(self, "分享链接", self.tr(ret)) + + # def output_conf_by_qr(self, region): + # """ + # 输出分享二维码 + # :return: + # """ + # ret = self.v2rayL.subs.conf2b64(region) + # # 生成二维码 + # url = "http://api.k780.com:88/?app=qr.get&data={}".format(ret) + # try: + # req = requests.get(url) + # if req.status_code == 200: + # qr = QPixmap() + # qr.loadFromData(req.content) + # self.qr_child_ui.label.setPixmap(qr) + # self.qr_child_ui.label.setScaledContents(True) + # self.qr_ui.show() + # else: + # shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,可能原因:调用API发生错误" + # subprocess.call([shell], shell=True) + # except: + # shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 服务错误,请将错误在github中提交" + # subprocess.call([shell], shell=True) def value_change(self, flag): """ @@ -506,37 +518,57 @@ def value_change(self, flag): :param flag: true时为http, false为socks :return: """ - with open("/etc/v2rayL/config.json", "r") as f: - ret = json.load(f) + http_port = self.system_setting_ui.http_sp.value() + socks_port = self.system_setting_ui.socks_sp.value() if flag: - new_port = self.system_setting_ui.http_sp.value() - if new_port == ret["inbounds"][0]["port"]: - new_port = new_port + 1 if new_port < 10079 else new_port - 1 - self.system_setting_ui.http_sp.setValue(new_port) - ret["inbounds"][1]["port"] = new_port - else: - new_port = self.system_setting_ui.socks_sp.value() - if new_port == ret["inbounds"][1]["port"]: - new_port = new_port + 1 if new_port < 10079 else new_port - 1 - self.system_setting_ui.socks_sp.setValue(new_port) - ret["inbounds"][0]["port"] = new_port + if http_port == self.v2rayL.current_status.socks: + http_port = http_port + 1 if http_port < 10079 else http_port - 1 + self.system_setting_ui.http_sp.setValue(http_port) + self.v2rayL.current_status.http = http_port - with open("/etc/v2rayL/config.json", "w") as f: - f.write(json.dumps(ret, indent=4)) - - if flag: - self.v2rayL.http = new_port with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.v2rayL.current, self.v2rayL.url, self.v2rayL.auto, self.v2rayL.check, - new_port, self.v2rayL.socks), jf) + pickle.dump(self.v2rayL.current_status, jf) + + qInfo("{}@$ff$@HTTP port is changed to {}".format(self.v2rayL.current_status.log, http_port)) + else: - self.v2rayL.socks = new_port + if socks_port == self.v2rayL.current_status.http: + socks_port = socks_port + 1 if socks_port < 10079 else socks_port - 1 + self.system_setting_ui.socks_sp.setValue(socks_port) + + self.v2rayL.current_status.socks = socks_port + with open("/etc/v2rayL/ncurrent", "wb") as jf: - pickle.dump((self.v2rayL.current, self.v2rayL.url, self.v2rayL.auto, self.v2rayL.check, - self.v2rayL.http, new_port), jf) + pickle.dump(self.v2rayL.current_status, jf) - self.v2rayL.connect(self.v2rayL.current, True) + qInfo("{}@$ff$@SOCKS port is changed to {}".format(self.v2rayL.current_status.log, socks_port)) + + try: + with open("/etc/v2rayL/config.json", "r") as f: + ret = json.load(f) + except FileNotFoundError: + pass + else: + ret["inbounds"][1]["port"] = http_port + ret["inbounds"][0]["port"] = socks_port + with open("/etc/v2rayL/config.json", "w") as f: + f.write(json.dumps(ret, indent=4)) + + # if flag: + # self.v2rayL.current_status.http = http_port + # with open("/etc/v2rayL/ncurrent", "wb") as jf: + # # self.v2rayL.current_status.http = http_port + # pickle.dump(self.v2rayL.current_status, jf) + # qInfo("{}@$ff$@HTTP port is changed to {}".format(self.v2rayL.current_status.log, http_port)) + # else: + # self.v2rayL.current_status.socks = socks_port + # with open("/etc/v2rayL/ncurrent", "wb") as jf: + # pickle.dump(self.v2rayL.current_status, jf) + # qInfo("{}@$ff$@SOCKS port is changed to {}".format(self.v2rayL.current_status.log, socks_port)) + + if self.v2rayL.current_status.current != "未连接至VPN": + self.v2rayL.connect(self.v2rayL.current_status.current, True) def show_add_ss_dialog(self): """ @@ -546,9 +578,17 @@ def show_add_ss_dialog(self): self.ss_add_ui.show() def show_add_vmess_dialog(self): + """ + 显示添加Vmess配置窗口 + :return: + """ self.vmess_add_ui.show() def add_ss_by_input(self): + """ + 手动添加shadowsocks配置 + :return: + """ remark = self.ss_add_child_ui.lineEdit_2.text().strip() addr = self.ss_add_child_ui.lineEdit_3.text().strip() port = self.ss_add_child_ui.lineEdit_4.text().strip() @@ -573,6 +613,10 @@ def add_ss_by_input(self): self.ss_add_ui.hide() def add_vmess_by_input(self): + """ + 手动添加vmess配置 + :return: + """ remark = self.vmess_add_child_ui.lineEdit.text().strip() addr = self.vmess_add_child_ui.lineEdit_2.text().strip() port = self.vmess_add_child_ui.lineEdit_3.text().strip() @@ -582,6 +626,7 @@ def add_vmess_by_input(self): types = self.vmess_add_child_ui.comboBox_2.currentText() host = self.vmess_add_child_ui.lineEdit_6.text().strip() path = self.vmess_add_child_ui.lineEdit_7.text().strip() + tls = self.vmess_add_child_ui.comboBox_3.currentText() # print(remark, addr, port, password, security) if not remark: remark = "vmess_" + str(random.choice(range(10000, 99999))) @@ -595,7 +640,7 @@ def add_vmess_by_input(self): 'net': net, 'type': types, 'path': path, - 'tls': "", + 'tls': tls, "v": 2 } b64str = "vmess://" + base64.b64encode(str(conf).encode()).decode() @@ -614,10 +659,51 @@ def add_vmess_by_input(self): self.vmess_add_child_ui.lineEdit_7.setText("") self.vmess_add_ui.hide() + def enable_log(self): + """ + 启用操作日志 + :return: + """ + self.v2rayL.current_status.log = True + qInfo("{}@$ff$@Operation log has been enabled".format(self.v2rayL.current_status.log)) + self.a3.setChecked(True) + self.a4.setChecked(False) + self.v2rayL.logging(True) + + def disable_log(self): + """ + 启用操作日志 + :return: + """ + qInfo("{}@$ff$@Operation log has been disabled".format(self.v2rayL.current_status.log)) + self.v2rayL.current_status.log = False + self.a4.setChecked(True) + self.a3.setChecked(False) + self.v2rayL.logging(False) + + # def output_ter_result(self, text): + # + # if self.v2rayL.current_status.log: + # with open("/etc/v2rayL/v2rayL_op.log", "a+") as f: + # f.write(' %s - %s: %s\n' % (datetime.datetime.now(), "Info", text)) + + def auto_on(self): + """ + 开启/关闭开机自动连接 + :return: + """ + if self.v2rayL.current_status.on: + self.v2rayL.auto_on(False) + qInfo("{}@$ff$@Automatic connection when booting is disabled.".format(self.v2rayL.current_status.log)) + else: + self.v2rayL.auto_on(True) + qInfo("{}@$ff$@Automatic connection when booting is enabled.".format(self.v2rayL.current_status.log)) + if __name__ == "__main__": import sys QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) + qInstallMessageHandler(qt_message_handler) app = QApplication(sys.argv) myWin = MyMainWindow() # 显示在屏幕上 @@ -625,3 +711,4 @@ def add_vmess_by_input(self): # 系统exit()方法确保应用程序干净的退出 # 的exec_()方法有下划线。因为执行是一个Python关键词。因此,exec_()代替 sys.exit(app.exec_()) + From 8c87647fa7b63652af8370b807dc63da53b6d9c7 Mon Sep 17 00:00:00 2001 From: jiangxufeng Date: Fri, 1 Nov 2019 21:09:14 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0log=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0tcp=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96ws-tls=E7=AD=89=E5=8D=8F=E8=AE=AE=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E4=BF=AE=E5=A4=8Deval=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E5=AE=89=E5=85=A8=E6=BC=8F=E6=B4=9E=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=80=E4=BA=9B=E9=97=AA=E9=80=80bug=E4=BB=A5?= =?UTF-8?q?=E5=8F=8Aservice=E6=96=87=E4=BB=B6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2rayL-GUI/new_ui.py | 154 ++++++++++++++++++++++++++++++++++- v2rayL-GUI/sub2conf_api.py | 56 +++++++++---- v2rayL-GUI/v2rayL_api.py | 73 ++++++----------- v2rayL-GUI/v2rayL_threads.py | 10 ++- v2rayL-GUI/v2rayLui.py | 137 ++++++++++++++++++++++--------- 5 files changed, 320 insertions(+), 110 deletions(-) diff --git a/v2rayL-GUI/new_ui.py b/v2rayL-GUI/new_ui.py index bce436e..4bcef72 100644 --- a/v2rayL-GUI/new_ui.py +++ b/v2rayL-GUI/new_ui.py @@ -345,6 +345,16 @@ def init_ui(self): self.vmess_add_child_ui = Ui_Add_Vmess_Dialog() self.vmess_add_child_ui.setupUi(self.vmess_add_ui) + # 订阅设置窗口 + self.subs_ui = QDialog() + self.subs_child_ui = Ui_Subs_Dialog() + self.subs_child_ui.setupUi(self.subs_ui) + + # 订阅设置窗口 + self.add_subs_ui = QDialog() + self.subs_add_child_ui = Ui_Add_Subs_Form() + self.subs_add_child_ui.setupUi(self.add_subs_ui) + # 托盘菜单 self.a1 = QAction('恢复(Show)') self.a2 = QAction('退出(Exit)') @@ -434,7 +444,8 @@ def setupUi(self, Form): "background-color:transparent;") self.lineEdit.setText("") self.lineEdit.setObjectName("lineEdit") - self.lineEdit.setPlaceholderText("http://或https://,可回车确认") + self.lineEdit.setPlaceholderText("当前无订阅地址") + self.lineEdit.setFocusPolicy(Qt.NoFocus) self.label = QLabel(Form) self.label.setGeometry(QRect(40, 320, 101, 17)) self.label.setStyleSheet("font: 13pt \"Purisa\";") @@ -523,7 +534,7 @@ def setupUi(self, Form): def retranslateUi(self, Form): _translate = QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) - self.pushButton_3.setText(_translate("Form", "更新地址")) + self.pushButton_3.setText(_translate("Form", "地址设置")) self.label.setText(_translate("Form", "当前订阅地址")) self.label_2.setText(_translate("Form", "配置")) self.label_4.setText(_translate("Form", "通过URI添加")) @@ -796,8 +807,6 @@ def add_item(self, args): button2.clicked.connect(lambda: args[7](args[1])) self.tableWidget.setCellWidget(args[0]-1, 6, button2) - - def retranslateUi(self, Form): _translate = QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) @@ -1109,6 +1118,143 @@ def retranslateUi(self, Dialog): self.label_11.setText(_translate("Dialog", "底层传输安全:")) self.pushButton.setText(_translate("Dialog", "确认添加")) + +class Ui_Subs_Dialog(object): + def setupUi(self, dialog): + dialog.setObjectName("dialog") + dialog.resize(652, 350) + dialog.setMinimumSize(QSize(652, 350)) + dialog.setMaximumSize(QSize(652, 350)) + dialog.setStyleSheet("#dialog{background:white; }") + self.tableWidget = QTableWidget(dialog) + self.tableWidget.setGeometry(QRect(0, 0, 651, 260)) + self.tableWidget.setObjectName("tableWidget") + self.tableWidget.setColumnCount(4) + item = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(0, item) + item = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(1, item) + item = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(2, item) + item = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(3, item) + self.pushButton = QPushButton(dialog) + self.pushButton.setGeometry(QRect(240, 290, 191, 41)) + self.pushButton.setStyleSheet( + "#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 18px; }\n" + "#pushButton:hover{ background: #5599FF;}") + self.pushButton.setObjectName("pushButton") + + self.tableWidget.setShowGrid(False) + self.tableWidget.setFrameShape(QFrame.NoFrame) + self.tableWidget.verticalHeader().setVisible(False) + self.tableWidget.horizontalHeader().setFixedHeight(40) + self.tableWidget.horizontalHeader().setSectionResizeMode(2) + self.tableWidget.horizontalHeader().setHighlightSections(False) + #self.tableWidget.horizontalHeader().setVisible(False) + self.tableWidget.setItemDelegate(CenterDelegate()) + self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) + self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.tableWidget.setColumnWidth(0, 30) + self.tableWidget.setColumnWidth(1, 100) + self.tableWidget.setColumnWidth(2, 425) + self.tableWidget.setColumnWidth(3, 30) + self.tableWidget.verticalHeader().setDefaultSectionSize(40) + self.tableWidget.setStyleSheet(''' + #tableWidget{ + selection-background-color:white; + } + + ''') + self.tableWidget.setFocusPolicy(Qt.NoFocus) + + + self.tableWidget.verticalScrollBar().setStyleSheet( + "QScrollBar{background:transparent; width: 12px;}" + "QScrollBar::handle{background:lightgray; border:2px solid transparent; border-radius:5px;}" + "QScrollBar::handle:hover{background:gray;}" + "QScrollBar::sub-line{background:transparent;}" + "QScrollBar::add-line{background:transparent;}") + + self.retranslateUi(dialog) + QMetaObject.connectSlotsByName(dialog) + + def retranslateUi(self, dialog): + _translate = QCoreApplication.translate + dialog.setWindowTitle(_translate("dialog", "v2rayL")) + item = self.tableWidget.horizontalHeaderItem(0) + item.setText(_translate("dialog", "序号")) + item = self.tableWidget.horizontalHeaderItem(1) + item.setText(_translate("dialog", "别名")) + item = self.tableWidget.horizontalHeaderItem(2) + item.setText(_translate("dialog", "订阅地址")) + item = self.tableWidget.horizontalHeaderItem(3) + item.setText(_translate("dialog", "")) + self.pushButton.setText(_translate("dialog", "新增订阅")) + + def add_item(self, args): + # self.tableWidget.setRowCount(self.num) + item = QTableWidgetItem(str(args[0])) + self.tableWidget.setItem(args[0] - 1, 0, item) + item = QTableWidgetItem(args[1]) + self.tableWidget.setItem(args[0] - 1, 1, item) + item = QTableWidgetItem(args[2][:57]) + self.tableWidget.setItem(args[0] - 1, 2, item) + item = QTableWidgetItem("") + self.tableWidget.setItem(args[0] - 1, 3, item) + button1 = QPushButton("删除") + button1.setObjectName("delbt") + button1.setStyleSheet(''' + #delbt{border-width: 0px; + border-radius: 15px; + background: red; + outline: none; + font-family: Microsoft YaHei; + color: white; + font-size: 13px; + margin:5px; + } + #delbt:hover{ background: #FF6347;} + ''') + button1.clicked.connect(lambda: args[3](args[0] - 1)) + self.tableWidget.setCellWidget(args[0] - 1, 3, button1) + + +class Ui_Add_Subs_Form(object): + def setupUi(self, dialog): + dialog.setObjectName("Form") + dialog.resize(312, 278) + dialog.setMinimumSize(QSize(312, 278)) + dialog.setMaximumSize(QSize(312, 278)) + self.label = QLabel(dialog) + self.label.setGeometry(QRect(20, 10, 51, 31)) + self.label.setObjectName("label") + self.textEdit = QTextEdit(dialog) + self.textEdit.setGeometry(QRect(20, 120, 271, 101)) + self.textEdit.setObjectName("textEdit") + self.label_2 = QLabel(dialog) + self.label_2.setGeometry(QRect(20, 90, 67, 17)) + self.label_2.setObjectName("label_2") + self.lineEdit = QLineEdit(dialog) + self.lineEdit.setGeometry(QRect(20, 50, 271, 25)) + self.lineEdit.setObjectName("lineEdit") + self.pushButton = QPushButton(dialog) + self.pushButton.setGeometry(QRect(110, 240, 90, 30)) + self.pushButton.setObjectName("pushButton") + self.pushButton.setStyleSheet( + "#pushButton{border-width: 0px; border-radius: 15px; background: #1E90FF; outline: none; font-family: Microsoft YaHei; color: white; font-size: 18px; }\n" + "#pushButton:hover{ background: #5599FF;}") + + self.retranslateUi(dialog) + QMetaObject.connectSlotsByName(dialog) + + def retranslateUi(self, dialog): + _translate = QCoreApplication.translate + dialog.setWindowTitle(_translate("dialog", "v2rayL")) + self.label.setText(_translate("dialog", "别名:")) + self.label_2.setText(_translate("dialog", "地址:")) + self.pushButton.setText(_translate("dialog", "添加")) + def main(): app = QApplication(sys.argv) gui = MainUi() diff --git a/v2rayL-GUI/sub2conf_api.py b/v2rayL-GUI/sub2conf_api.py index 132ac7a..0132be6 100755 --- a/v2rayL-GUI/sub2conf_api.py +++ b/v2rayL-GUI/sub2conf_api.py @@ -6,6 +6,7 @@ import json import pickle import requests +import ast import copy import urllib.parse as parse from config import conf_template as tpl @@ -49,7 +50,7 @@ def b642conf(self, prot, tp, b64str): :return: """ if prot == "vmess": - ret = eval(parse.unquote(base64.b64decode(b64str).decode())) + ret = ast.literal_eval(parse.unquote(base64.b64decode(b64str).decode())) region = ret['ps'] elif prot == "shadowsocks": @@ -108,7 +109,7 @@ def setconf(self, region, http, socks): "security": "tls" if use_conf["tls"] else "", "tlssettings": { "allowInsecure": True, - "serverName": use_conf["host"] + "serverName": use_conf["host"] if use_conf["tls"] else "" }, "wssettings": { "connectionReuse": True, @@ -211,25 +212,47 @@ def delconf(self, region): with open("/etc/v2rayL/ndata", "wb") as jf: pickle.dump(self.saved_conf, jf) - def update(self): + def update(self, flag): """ 更新订阅 + :param flag: True, 整体更新, False, 添加 """ - try: - ret = requests.get(self.subs_url, timeout=15) - if ret.status_code != 200: - raise MyException("无法获取订阅信息,订阅站点访问失败") - all_subs = base64.b64decode(ret.text + "==").decode().strip() - if "vmess" not in all_subs or "ss" not in all_subs: - raise MyException("解析订阅信息失败,请确认链接正确") - all_subs = all_subs.split("\n") - except Exception as e: - raise MyException(e.args[0]) + error_subs = [] + if flag: + all_subs = [] + for url in self.subs_url: + # print(url) + try: + ret = requests.get(url[1], timeout=15) + if ret.status_code != 200: + error_subs.append([url, "无法获取订阅信息,订阅站点访问失败"]) + raise MyException("无法获取订阅信息,订阅站点访问失败") + subs = base64.b64decode(ret.text + "==").decode().strip() + all_subs.extend(subs.split("\n")) + except: + pass + # raise MyException(e.args[0]) + # error_subs.append(url) + + for sub in all_subs: + self.origin.append(sub.split("://")) - for sub in all_subs: - self.origin.append(sub.split("://")) + self.saved_conf["subs"] = {} + + else: + try: + ret = requests.get(self.subs_url, timeout=15) + if ret.status_code != 200: + raise MyException("无法获取订阅信息,订阅站点访问失败") + all_subs = base64.b64decode(ret.text + "==").decode().strip() + if "vmess" not in all_subs or "ss" not in all_subs: + raise MyException("解析订阅信息失败,请确认链接正确") + all_subs = all_subs.split("\n") + except Exception as e: + raise MyException(e.args[0]) - self.saved_conf["subs"] = {} + for sub in all_subs: + self.origin.append(sub.split("://")) for ori in self.origin: if ori[0] == "vmess": @@ -242,6 +265,7 @@ def update(self): with open("/etc/v2rayL/ndata", "wb") as jf: pickle.dump(self.saved_conf, jf) + return error_subs def add_conf_by_uri(self): """ 通过分享的连接添加配置 diff --git a/v2rayL-GUI/v2rayL_api.py b/v2rayL-GUI/v2rayL_api.py index 7e5841d..70fb9fd 100755 --- a/v2rayL-GUI/v2rayL_api.py +++ b/v2rayL-GUI/v2rayL_api.py @@ -12,7 +12,7 @@ class CurrentStatus(object): """ 保存当前状态 """ - def __init__(self, current="未连接至VPN", url=None, auto=False, check=False, + def __init__(self, current="未连接至VPN", url=set(), auto=False, check=False, http=1081, socks=1080, log=True): """ :param current: 当前连接状态 @@ -40,28 +40,10 @@ def __init__(self): # self.current, self.url, self.auto, self.check, self.http, self.socks, self.log = pickle.load(f) self.current_status = pickle.load(f) except: - # self.current = "未连接至VPN" - # self.url = None - # self.auto = False - # self.check = False - # self.http = 1081 - # self.socks = 1080 - # self.log = True self.current_status = CurrentStatus() self.subs = Sub2Conf(subs_url=self.current_status.url) - # if self.current_status.auto and self.current_status.url: - # try: - # self.subs.update() - # except: - # with open("/etc/v2rayL/ncurrent", "wb") as jf: - # self.current_status = CurrentStatus(http=self.current_status.http, - # socks=self.current_status.socks, - # log=self.current_status.log) - # pickle.dump(self.current_status, jf) - # raise MyException("更新失败, 已关闭自动更新,请更新订阅地址") - def auto_check(self, flag): """ 启用/禁用自动检查更新 @@ -101,19 +83,6 @@ def logging(self, flag): self.current_status.log = False pickle.dump(self.current_status, jf) - # def auto_on(self, flag): - # """ - # 启用/禁用开机自动连接 - # """ - # if flag: - # with open("/etc/v2rayL/ncurrent", "wb") as jf: - # self.current_status.on = True - # pickle.dump(self.current_status, jf) - # else: - # with open("/etc/v2rayL/ncurrent", "wb") as jf: - # self.current_status.on = False - # pickle.dump(self.current_status, jf) - def connect(self, region, flag): """ 连接VPN @@ -160,17 +129,26 @@ def disconnect(self): except Exception as e: raise MyException(e.args[0]) - def update(self, url): + def update(self, remark, url): """ 更新订阅 """ - if url: # 如果存在订阅地址 + if remark and url: # 如果是添加单条 self.subs = Sub2Conf(subs_url=url) - self.subs.update() + self.subs.update(False) with open("/etc/v2rayL/ncurrent", "wb") as jf: - self.current_status.url = url - pickle.dump(pickle.dump(self.current_status, jf), jf) + self.current_status.url.add((remark, url)) + pickle.dump(self.current_status, jf) + elif url: + # print(123) + self.subs = Sub2Conf(subs_url=self.current_status.url) + error_subs = self.subs.update(True) + for i in error_subs: + self.current_status.url.remove(i[0]) + with open("/etc/v2rayL/ncurrent", "wb") as jf: + pickle.dump(self.current_status, jf) + return error_subs else: if self.current_status.current in self.subs.saved_conf["subs"]: try: @@ -194,6 +172,8 @@ def update(self, url): with open("/etc/v2rayL/data", "wb") as f: pickle.dump({"local": self.subs.saved_conf["local"], "subs": {}}, f) + return None + def addconf(self, uri): """ 删除配置 @@ -225,14 +205,11 @@ def ping(self): if __name__ == '__main__': - def ping(): - try: - proxy = { - "http": "127.0.0.1:1081", - "https": "127.0.0.1:1081" - } - req = requests.get("http://www.google.com", proxies=proxy, timeout=10) - if req.status_code == 200: - return req.elapsed.total_seconds()*1000 - except: - print(11) \ No newline at end of file + A = V2rayL() + with open("/etc/v2rayL/ncurrent", "wb") as jf: + A.current_status.url = set() + A.current_status.url.add(("tt", "https://v2ray.dlolb.ml")) + A.current_status.url.add(("asd", "https://sub.qianglie.xyz/subscribe")) + A.current_status.url.add(("test", "https://sub.qianglie.xyz/subsaae")) + A.current_status.url.add(("ql", "https://sub.qianglie.xyz/subscribe.php?sid=5582&token=g6e8VdU6C40H")) + pickle.dump(A.current_status, jf) diff --git a/v2rayL-GUI/v2rayL_threads.py b/v2rayL-GUI/v2rayL_threads.py index 560ba5e..03e0f3b 100755 --- a/v2rayL-GUI/v2rayL_threads.py +++ b/v2rayL-GUI/v2rayL_threads.py @@ -76,24 +76,26 @@ def __del__(self): def run(self): # msg = QMessageBox.information() if self.subs_child_ui: - url = self.subs_child_ui.lineEdit.text() + remark = self.subs_child_ui.lineEdit.text().strip() + url = self.subs_child_ui.textEdit.toPlainText().strip() try: - self.v2rayL.update(url) + self.v2rayL.update(remark, url) except MyException as e: self.sinOut.emit(("addr", "@@Fail@@", e.args[0], None)) else: self.sinOut.emit(("addr", "@@OK@@", url, None)) else: url = self.v2rayL.current_status.url + # print(url) if not url: self.sinOut.emit(("update", "@@Fail@@", "不存在订阅地址,无法更新", None)) else: try: - self.v2rayL.update(url) + error = self.v2rayL.update(None, url) except MyException as e: self.sinOut.emit(("update", "@@Fail@@", e.args[0], None)) else: - self.sinOut.emit(("update", "@@OK@@", url, None)) + self.sinOut.emit(("update", "@@OK@@", (url, error), None)) class PingThread(QThread): diff --git a/v2rayL-GUI/v2rayLui.py b/v2rayL-GUI/v2rayLui.py index b4e9df8..85a607c 100644 --- a/v2rayL-GUI/v2rayLui.py +++ b/v2rayL-GUI/v2rayLui.py @@ -28,7 +28,7 @@ VersionUpdateThread, RunCmdThread ) -from new_ui import MainUi, SwitchBtn, Ui_Add_Ss_Dialog, Ui_Add_Vmess_Dialog +from new_ui import MainUi, SwitchBtn, Ui_Add_Ss_Dialog, Ui_Add_Vmess_Dialog, Ui_Subs_Dialog from utils import SystemTray, qt_message_handler @@ -72,11 +72,11 @@ def __init__(self, parent=None): self.update_subs_start.subs_child_ui = None self.update_subs_start.start() except: - with open("/etc/v2rayL/ncurrent", "wb") as jf: - self.v2rayL.current_status.url = None - self.v2rayL.current_status.auto = False - pickle.dump(self.v2rayL.current_status, jf) - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL '{}'".format("更新失败, 已关闭自动更新,请更新订阅地址") + # with open("/etc/v2rayL/ncurrent", "wb") as jf: + # self.v2rayL.current_status.url = set() + # self.v2rayL.current_status.auto = False + # pickle.dump(self.v2rayL.current_status, jf) + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL '{}'".format("更新失败") subprocess.call([shell], shell=True) # 自动检查更新 @@ -97,7 +97,8 @@ def __init__(self, parent=None): self.a3.setChecked(False) # 填充当前订阅地址 - self.config_setting_ui.lineEdit.setText(self.v2rayL.current_status.url) + # print(self.v2rayL.current_status.url) + self.config_setting_ui.lineEdit.setText(";".join([x[1] for x in self.v2rayL.current_status.url])) # 端口 self.system_setting_ui.http_sp.setValue(self.v2rayL.current_status.http) self.system_setting_ui.socks_sp.setValue(self.v2rayL.current_status.socks) @@ -111,13 +112,15 @@ def __init__(self, parent=None): self.config_setting_ui.switchBtn.checkedChanged.connect(self.change_auto_update) self.system_setting_ui.switchBtn.checkedChanged.connect(self.change_check_update) # self.system_setting_ui.switchBtn1.checkedChanged.connect(self.auto_on) - self.first_ui.pushButton.clicked.connect(self.update_subs) # 更新订阅 + self.first_ui.pushButton.clicked.connect(lambda: self.update_subs(True)) # 更新订阅 + self.subs_add_child_ui.pushButton.clicked.connect(self.change_subs_addr) + # self.subs_add_child_ui.textEdit.returnPressed.connect(self.change_subs_addr) self.config_setting_ui.pushButton_2.clicked.connect(self.output_conf) # 导出配置文件 self.config_setting_ui.pushButton.clicked.connect(self.get_conf_from_qr) # 通过二维码导入 self.first_ui.pushButton_1.clicked.connect(self.start_ping_th) # 测试延时 self.system_setting_ui.checkupdateButton.clicked.connect(self.check_update) # 检查更新 - self.config_setting_ui.lineEdit.returnPressed.connect(self.change_subs_addr) # 更新订阅操作 - self.config_setting_ui.pushButton_3.clicked.connect(self.change_subs_addr) # 更新订阅操作 + # self.config_setting_ui.lineEdit.returnPressed.connect(self.change_subs_addr) # 更新订阅操作 + self.config_setting_ui.pushButton_3.clicked.connect(self.show_subs_dialog) # 显示具体订阅操作 self.config_setting_ui.lineEdit_2.returnPressed.connect(self.get_conf_from_uri) # 解析URI获取配置 self.conn_start.sinOut.connect(self.alert) # 得到连接反馈 self.disconn_start.sinOut.connect(self.alert) # 得到断开连接反馈 @@ -132,6 +135,7 @@ def __init__(self, parent=None): self.config_setting_ui.pushButton_vmess.clicked.connect(self.show_add_vmess_dialog) self.ss_add_child_ui.pushButton.clicked.connect(self.add_ss_by_input) self.vmess_add_child_ui.pushButton.clicked.connect(self.add_vmess_by_input) + self.subs_child_ui.pushButton.clicked.connect(self.show_add_subs_dialog) self.a1.triggered.connect(self.show) self.a3.triggered.connect(self.enable_log) self.a4.triggered.connect(self.disable_log) @@ -147,7 +151,7 @@ def check_update(self): 检查版本更新 :return: """ - print("hahah") + # print("hahah") shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在检查版本更新." subprocess.call([shell], shell=True) self.check_update_start.start() @@ -157,7 +161,6 @@ def display_all_conf(self): 列出所有的可用配置 :return: """ - # print("Okokok") all_conf = self.v2rayL.subs.conf lists = [] i = 1 @@ -176,38 +179,29 @@ def change_subs_addr(self): 更新订阅地址 :return: """ - url = self.config_setting_ui.lineEdit.text() + remark = self.subs_add_child_ui.lineEdit.text() + url = self.subs_add_child_ui.textEdit.toPlainText() self.update_addr_start.v2rayL = self.v2rayL - self.update_addr_start.subs_child_ui = self.config_setting_ui + self.update_addr_start.subs_child_ui = self.subs_add_child_ui if not url: - choice = QMessageBox.warning(self, "订阅地址更新", self.tr("当前订阅地址为空," - "继续则删除订阅地址,同时会删除所有订阅配置," - "是否继续?"), - QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) - if choice == QMessageBox.Ok: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅地址......" - subprocess.call([shell], shell=True) - self.update_addr_start.start() - else: - self.config_setting_ui.lineEdit.setText(self.v2rayL.current_status.url) + QMessageBox.warning(self, "添加订阅地址", self.tr("当前订阅地址为空,请输入订阅地址。")) + elif not remark: + QMessageBox.warning(self, "添加订阅地址", self.tr("当前别名为空,请输入别名。")) else: - if url == self.v2rayL.current_status.url: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 订阅地址未改变" - subprocess.call([shell], shell=True) - else: - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅地址......" - subprocess.call([shell], shell=True) - self.update_addr_start.start() + # shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅地址......" + # subprocess.call([shell], shell=True) + self.update_addr_start.start() - def update_subs(self): + def update_subs(self, flag): """ 手动更新订阅 :return: """ self.update_subs_start.v2rayL = self.v2rayL self.update_subs_start.subs_child_ui = None - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅......" - subprocess.call([shell], shell=True) + if flag: + shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 正在更新订阅......" + subprocess.call([shell], shell=True) self.update_subs_start.start() def get_conf_from_uri(self): @@ -328,18 +322,32 @@ def alert(self, tp): self.display_all_conf() elif tp == "addr": - shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 更新订阅地址成功" - subprocess.call([shell], shell=True) - qInfo("{}@$ff$@Subscription address change to {}".format(self.v2rayL.current_status.log, ret)) + # shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 更新订阅地址成功" + # subprocess.call([shell], shell=True) + qInfo("{}@$ff$@Add a new subscription address: {}".format(self.v2rayL.current_status.log, ret)) self.v2rayL = V2rayL() self.display_all_conf() + self.show_subs_dialog() + self.subs_add_child_ui.lineEdit.setText("") + self.subs_add_child_ui.textEdit.setPlainText("") + self.add_subs_ui.hide() + self.config_setting_ui.lineEdit.setText(";".join([x[1] for x in self.v2rayL.current_status.url])) elif tp == "update": shell = "notify-send -i /etc/v2rayL/images/logo.ico v2rayL 订阅更新完成" subprocess.call([shell], shell=True) - qInfo("{}@$ff$@Successfully updated subscription.".format(self.v2rayL.current_status.log)) + if not ret[1]: + qInfo("{}@$ff$@Successfully updated subscription.".format(self.v2rayL.current_status.log)) + else: + retinfo = "" + for i in ret[1]: + retinfo += "\n{}-{}-{}".format(i[0][0], i[0][1], i[1]) + qInfo("{}@$ff$@{}".format(self.v2rayL.current_status.log, retinfo).encode()) + # print(retinfo) self.v2rayL = V2rayL() + # print(123) self.display_all_conf() + self.config_setting_ui.lineEdit.setText(";".join([x[1] for x in self.v2rayL.current_status.url])) elif tp == "ping": if isinstance(ret, int): @@ -380,6 +388,7 @@ def alert(self, tp): ret = "Failed to resolve subscription information, please confirm the link is correct" else: pass + qInfo("{}@$ff$@Failed to get subscriptions: {}.".format(self.v2rayL.current_status.log, ret)) elif tp == "conn": @@ -686,6 +695,27 @@ def disable_log(self): # if self.v2rayL.current_status.log: # with open("/etc/v2rayL/v2rayL_op.log", "a+") as f: # f.write(' %s - %s: %s\n' % (datetime.datetime.now(), "Info", text)) + def show_subs_dialog(self): + """ + 显示订阅设置窗口 + :return: + """ + subs_urls = self.v2rayL.current_status.url + #print(subs_urls) + self.subs_child_ui.tableWidget.setRowCount(len(subs_urls)) + i = 1 + # print(1111) + for url in subs_urls: + self.subs_child_ui.add_item((i, url[0], url[1], self.del_subs)) + i += 1 + self.subs_ui.show() + + def show_add_subs_dialog(self): + """ + 显示添加订阅窗口 + :return: + """ + self.add_subs_ui.show() def auto_on(self): """ @@ -699,6 +729,37 @@ def auto_on(self): self.v2rayL.auto_on(True) qInfo("{}@$ff$@Automatic connection when booting is enabled.".format(self.v2rayL.current_status.log)) + def del_subs(self, row): + choice = QMessageBox.question(self, "删除订阅", "删除订阅地址,对应的配置也将删除,是否继续?", + QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + if choice == QMessageBox.Yes: + remark = self.subs_child_ui.tableWidget.item(row, 1).text() + url = self.subs_child_ui.tableWidget.item(row, 2).text() + for i in self.v2rayL.current_status.url: + if i[0] == remark and i[1][:57] == url: + self.v2rayL.current_status.url.remove(i) + break + + with open("/etc/v2rayL/ncurrent", "wb") as f: + pickle.dump(self.v2rayL.current_status, f) + self.show_subs_dialog() + if self.v2rayL.current_status.url: + self.update_subs(False) + else: + # 如果连接对象为删除的订阅地址中的配置,断开连接 + if self.v2rayL.current_status.current in self.v2rayL.subs.saved_conf["subs"]: + self.disconn_start.v2rayL = self.v2rayL + self.disconn_start.tableView = self.first_ui.tableWidget + self.disconn_start.start() + + self.v2rayL.subs.saved_conf["subs"] = {} + with open("/etc/v2rayL/ndata", "wb") as jf: + # print(self.v2rayL.subs.saved_conf) + pickle.dump(self.v2rayL.subs.saved_conf, jf) + self.v2rayL = V2rayL() + self.display_all_conf() + self.config_setting_ui.lineEdit.setText(";".join([x[1] for x in self.v2rayL.current_status.url])) + if __name__ == "__main__": import sys From 8c0500472561a5e996fbf013577a2a35c04a5278 Mon Sep 17 00:00:00 2001 From: cbdog94 Date: Thu, 14 Nov 2019 15:48:00 +0800 Subject: [PATCH 5/5] Missing tls in security for tcp and kcp. --- v2rayL-GUI/sub2conf_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayL-GUI/sub2conf_api.py b/v2rayL-GUI/sub2conf_api.py index 0132be6..b785e1b 100755 --- a/v2rayL-GUI/sub2conf_api.py +++ b/v2rayL-GUI/sub2conf_api.py @@ -135,13 +135,13 @@ def setconf(self, region, http, socks): "uplinkCapacity": 12, "writeBufferSize": 1 }, - "security": "" + "security": "tls" if use_conf["tls"] else "" } # tcp elif use_conf["net"] == "tcp": conf['outbounds'][0]["streamSettings"] = { "network": use_conf["net"], - "security": "", + "security": "tls" if use_conf["tls"] else "", "tcpsettings": { "connectionReuse": True, "header": {