From 3e17def62723c5829b825e6dbaf047faf8220971 Mon Sep 17 00:00:00 2001 From: Julian Freeman Date: Thu, 1 Feb 2024 11:27:24 -0400 Subject: [PATCH] dev 02011127 --- daily_check.qrc | 3 + daily_check_rc.py | 312 +++++++++++++++++++++++++++++++++- global_vars.py | 34 ++++ images/browsers/brave_32.png | Bin 0 -> 1406 bytes images/browsers/chrome_32.png | Bin 0 -> 1405 bytes images/browsers/edge_32.png | Bin 0 -> 1654 bytes main.py | 5 +- util_ext.py | 135 +++++++++++++++ wg_extensions.py | 210 +++++++++++++++++++++++ wg_software.py | 8 +- 10 files changed, 701 insertions(+), 6 deletions(-) create mode 100644 global_vars.py create mode 100644 images/browsers/brave_32.png create mode 100644 images/browsers/chrome_32.png create mode 100644 images/browsers/edge_32.png create mode 100644 util_ext.py create mode 100644 wg_extensions.py diff --git a/daily_check.qrc b/daily_check.qrc index 16a56f9..a6a1fa7 100644 --- a/daily_check.qrc +++ b/daily_check.qrc @@ -2,5 +2,8 @@ images/none_128.png images/blank_128.png + images/browsers/chrome_32.png + images/browsers/edge_32.png + images/browsers/brave_32.png diff --git a/daily_check_rc.py b/daily_check_rc.py index eadae6f..5249d0b 100644 --- a/daily_check_rc.py +++ b/daily_check_rc.py @@ -319,6 +319,292 @@ y\xdf\x07d\x81~\x11\xdeQ\xe5\x1d\x81\x9dye\x9b\ \x03[\x14\xb6 \xbcZ\xef\xc1(6\xfc?\x14\xfa?\ \xdce\xbf\xf3&\x00\x00\x00\x00IEND\xaeB`\ \x82\ +\x00\x00\x05~\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x03\x00\x00\x00D\xa4\x8a\xc6\ +\x00\x00\x00\x09pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95+\x0e\x1b\x00\x00\x02\xbePLTEGpL\ +\xd7`*\x17\x0a\x04\xa0G\x1fO#\x0f\x04\x01\x00\xf7\ +q4\xf6n0\xdfd,\x00\x00\x00M\x22\x0f\x14\x09\ +\x04\x02\x01\x00\x9eG\x1f\xd8a*\xddc+-\x14\x08\ +Q$\x10\xb5Q#\x22\x0f\x06\x06\x02\x01\x17\x0a\x04\xa1\ +H\x1f\xf7}E\xdbb+\xf6n0\xf5n0N#\ +\x0f\xafN\x22\x15\x09\x04_*\x12\x02\x01\x00\xa9L!\ +\xe4f-X'\x11}8\x182\x16\x09&\x11\x07\xe0\ +d,\x9fG\x1f\x09\x03\x01\xe3f-\x0c\x05\x02b,\ +\x13\xb8R$m1\x15Q$\x10b+\x13z7\x18\ +\x04\x01\x00W'\x11\xf8}E\xf4\x85S\xf7\x86R\xa1\ +H\x1f\xa7K!\xbeiB \x0e\x06)\x12\x08\xd5u\ +I\xd3_)\xd5`*W'\x11\xcdrH\xc6mD\ +\xf2\x82O\xf7~G\xa0G\x1f\xf7x>\xb6e@\xcc\ +\x5c(\x98D\x1e\xf8\x7fHK!\x0e\xe6|K\x97R\ +4\xf7w=\xcf])\xf6\x83O\xf7|C\xf7v;\ +\xf8\x80I\xee\x82Q\xf7|D\xe8\x7fO\xf7\x7fG\xd0\ +tI\xe4~O\xe8h.\xddxI\xf7u:\xdf{\ +N\xe9i.\xf7y@\xf7y@\xf7o1\xff\xff\xff\ +\xf8}E\xf6n0\xf5n0\xfe\xfe\xfe\xfe\xf0\xea\xea\ +i.\xf7p2\xfa\xf0\xec\xfc\xf6\xf3\xf7x>\xfe\xf3\ +\xee\xe3{K\xdfg0\xf8\x8d\x5c\xfb\xbd\xa1\xf6\x7fI\ +\xfc\xce\xb9\xf8\xeb\xe5\xfe\xfc\xfb\xf4m0\xe6\x83V\xe3\ +\x81T\xe3\x81U\xfb\xf5\xf2\xf9\x9cr\xe2\x89`\xfb\xf5\ +\xf3\xfb\xc9\xb2\xf1\xa8\x86\xefm1\xf0\xd4\xc7\xfc\xf5\xf1\ +\xe5r>\xfd\xf9\xf8\xf9\xf0\xeb\xe6\x82U\xe5\x90i\xfc\ +\xd8\xc8\xf6o1\xe5r=\xf6n1\xf1\xd6\xc9\xf9\x9a\ +n\xfd\xfa\xf8\xf1\xa5\x82\xe4\x91j\xf0k/\xe2\x89a\ +\xf0l1\xfb\xc7\xaf\xe9\xba\xa4\xf0l/\xe3\x97t\xe2\ +|M\xfc\xd6\xc5\xe4g/\xf7\xe8\xe1\xf8\xeb\xe6\xf9\xc2\ +\xa8\xe2}N\xf8\x81K\xe4\x9ay\xf7v<\xf8\x85Q\ +\xe9\xb8\xa2\xe4g.\xfe\xfa\xf8\xf7v;\xfe\xf7\xf4\xfc\ +\xf7\xf4\xf8\xbd\xa3\xf7\xe7\xe1\xfe\xfa\xf9\xe4\xaa\x8f\xf8\xec\ +\xe6\xfa\xae\x8c\xf7o2\xe5\xac\x92\xfe\xf9\xf7\xf7{C\ +\xfc\xcd\xb8\xfa\xa6\x80\xfa\xb7\x99\xfc\xd2\xbf\xf7zB\xf9\ +\xa3|\xfd\xea\xe1\xf5\xe3\xdb\xfd\xec\xe3\xfc\xd7\xc6\xfd\xdd\ +\xce\xfa\xaf\x8d\xfe\xf2\xed\xfc\xd9\xc9\xfa\xae\x8b\xe1\x96s\ +\xe3\xa9\x8e\xfe\xf2\xec\xdfxH\xfd\xfa\xf9\xe1\x97t\xdf\ +vF\xf6\xe7\xdf\xf9\xa4}\xe3\xa8\x8d\xfa\xf1\xed\xfe\xee\ +\xe7\xe2\x8bc\xfa\xb5\x96\xfe\xfc\xfc\xe2\x8ce\xfe\xed\xe6\ +\xfd\xfb\xfa\xf2\xad\x8d\xfd\xdf\xd1\xfe\xf8\xf6\xfe\xef\xe9\xe2\ +\xa5\x8a\xed\x95l\xf1\x82O\xf4\xc2\xab\xf9\x95g\xf8\x8c\ +Z\xed\x97p\xef\x98q\xe2\xa3\x87\xf8\x8c[\xe0\x89b\ +\xfd\xe0\xd2\xf1\x81N\xf2\xac\x8c\xf9\x96i\xf4\xc3\xad\xfc\ +\xcf\xba\xef\x99q\xfe\xf9\xf6\xe0\x89a\x19\x07\xe4\xa6\x00\ +\x00\x00_tRNS\x00\xe1$\xb1c\x08\xfc\xfe\xe9\ +\x12a#\x07\xaf\xe2\xe2\x03d\xa0\x02\x05%\xb2\xfe\xe1\ +\xfd\xfdb\x94$D\x08\x93\xebC\x89\x06\x05\xea\xb0\x02\ +\xea\x028\xa2CeB\x87\x056\xfeY[\x88\x89\x11\ +\x01\x01\x14\xd7\xd9-;\x13\x5c\xcc~\xe3\x10\xce|\xcf\ ++2\x01\xe4\xcf^\xc5\xf6\xcd9\xc37\xca<.\xe0\ +1\xf7-\xdf\xe8\xe7\xfe\x08\x05\xdf\x00\x00\x01\xfbID\ +AT8\xcbc`\xa0\x220\x11O\x84\x02cSl\ +\xf2!\x01\xf1p\x10\x15\x84.\xeb\x1a\x16\x1d\x17\x8f\x04\ +b\xc3\xed-\x90\xe5\x1d\x02\x97d\xc5\xa3\x80\xac\xf9\x1e\ +\xd6H\x0aB\xe3\xb1\x00'$\x051\xf1\xf1[\xb7\xef\ +\xdb\x9f\xb7mcvv\xe1\x89\xbc=\xbbwn\x88\x8f\ +\x8f@\xc8G\xb2\xc5\xc7oI@\x01\xbb\xd6\xc6\xb3y\ +\xc1\x15\x04\xc7\xc7o\x02\x0a\x16\xa7@e\x8f\x00\x19k\ +\xe2\xe3\xfd\xbd\xa1\xf2\x8e\xe9\xf1s\x97&\x1c?\x9a{\ +\xecdQBB\xd1\xc1\xc3\xb9\x07v$,^\x10\x9f\ +\xee\x07U\xe0\x12\x1f?\x1d\xa4\xf1P~\xc1\xe3\x84\x84\ +K\x05\xf9{A\xbc\xb6\xf8x\x1f\xa8\x02\xb7\xf8\xf8\x06\ +\x88\xd9\x0f\x0b\x13\x12\x9e]\x86\xb03\xdb\xe3}\xa1\x0a\ +\xdc\xe3\xe3[\x9b\xc0\x82O\xaf&$\xa4\xde\x043{\ +\xfa\xe2\xe3\x9d\xa1\x0a<\xd9\xe3\x93\xab*@\xa2wA\ +\xc4s\x10Q=!9\x9e\xdd\x0e\xaa\xc0\xd2&>>\ +\xa9\xac9!!\xe5\xc6\x83{\xd7^>:\x97\x90\x90\ +Q\x9e\x14\x1fok\x05\xf3\xa6\x190\xdc\x92Z\x80\xfa\ +R\xef\xc7\xc7\xdfN\x052J\x81\xf2\xf1\xe6\xf0p0\ +\x02\x85l7\xc8\xe4\x17On]\x01\xd1\x95 \x11m\ +\xb8\x02a\x09 w\x12H\xa2\xf8\xce\xf5\x8b \xba\x06\ +(\xc0/\x80\x08k\x05 \xbf\x17(~\xea|I\xc9\ +\x99\x0c \xa3\x16( \x83\x14Y\xfa@\xfe\xc4\x85\xab\ +\xd7\xe5\x9c\xbdpzs\xce\xaaY\x93\xeb\x80\x02\xbaH\ +\x0aT5\x81\x02S\x16-_\x0f2~\xe5\xec\x19\xfd\ +@\xae\xa2\x0ar\x8a\x11\x029*i\xde2p\x10N\ +\x05y!^\x07%\xc9)I\x81\xc4\x92\xe7\xacHH\ +\x989-\x19\xc4\x96\x94CQ\xa0a\xc0\x0e6\xa3\xa3\ +\xbe\xab\xb1\x13\xc4bW\xd6BM\xb5\x86,\x1c\xe0t\ +\x96\x96\x06\xa6\xd4\xf4D0\x92\xbd\xac:;,5\xb2\ +\x8b\x89b\xcb\x18\xf2\xd2\x10C\xe29\x04Yq\xe4-\ +n^\xa0!\xec\xccL\xb8s\x1f\x0f\x17#\x1f\x0b+\ +\xde\x0c\xca\xc9Ij\x96\x06\x00\xfd\x98\xe6y\x07\xbeQ\ +(\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x05}\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x03\x00\x00\x00D\xa4\x8a\xc6\ +\x00\x00\x02\xe2PLTEGpL\xfc\xceI\xfc\xce\ +I\xec\xc1D\xdcJ=\xdcJ=\x0f\x93S\xfc\xceI\ +\xfc\xceI\xfc\xceI\x9e5,\xdbI<\x0f\x98V\xf4\ +\xc7F\x0cvC\x96{+\xdcJ=\x0buB\xf6\xc9\ +G\x10\x9dY\xdbI<\xcf\xa9<\xdbI<\x10\x9dY\ +\xdcJ=\x9f\x82.\x0ac8\x86-%\x9e5,\x10\ +\x9dY\xbc\x996\xdcJ=\x0f\x99V\xbd\x9b7\x10\x9d\ +Y\x0d\x81I\x0f\x98V\x10\x9dY\xb7=2\xdcJ=\ +\x10\x9dY\xb7=2\xf5\xc8G\x09]5\xdbJ<\xdc\ +J=\xb6=2\xdbI<\xaa\x840\xf0\xa0D\xdaJ\ +<\xb6=2\x0bk=\x10\x9dY\xdcJ=\xc3A6\ +\x5c9(\xdcJ=\x10\x9dY\xc3A6\xdcJ=\x18\ +\x98W\x10\x9dY\xdcJ=\xdcJ=\xf1\xc5E\x0f\x9b\ +W\xf9\xcbH\x10\x9dY\x9d4+\x0ac8\xdcJ=\ +\xd9I<\xc3A6\xd9I<\xc3A6\x9d4+\x10\ +\x9dY\xfc\xceI\x0d\x82J\x0e\x91R\x0d\x80H\x0e\x90\ +R\x0e\x95T\x0ah;\x8f\xcc\xae\xeb\xed\xee\xfc\xceI\ +4\xa1hE~L\x13\x9bX\xa5\xa2\x8c}gC\xa0\ +\x83.`rG\xa8E3\xbdA3\x1d\x96V\xe8\xbd\ +C\xd1\xab<\xa7\x880\xcd\xa8;\xea\xbfC\x0f\x9cX\ +\x0f\x9cX\x94y+\x09\x5c4\xbf\x9c7\xdc\xb4?\x0c\ +wC\x10\x87L\x9a\xa6D\xd9\xb1?\xdcJ=F\x87\ +\xf3\xfc\xceI\x10\x9dY\xfb\xcdH\xc7\xd7\xf1\xe9\xb5\xb0\ +\xddTH\xde\xe5\xf1F\x87\xf2\x0e\x94T\x0f\x9bXG\ +\x85P\xed\x96?\xeb\x94<\xb9WA\xee\x9aB\xdbI\ +<\xf6\xde\x97\xf9\xc9D\xdaH;\xf6\xde\x99M\x8b\xf2\ +\x15\x9e\x5c\x9b]A\xb4\xda\xc8\xdfcX_\x96\xf2\xc1\ +\xd3\xf1\xf6\xc2>\xc0\xd2\xf1\xcf\xdc\xf1\xf8\xd9\x81\xef\xef\ +\xf0\xea\x91:\xe5\x96\x8f\xfb\xd0S\xf8\xd8|\x84\xc8\xa7\ +\x16\x9f]\xd9G:\xcc\xda\xf1/\x8eS\xe9\x908\xe8\ +\xae\xa9\xaa\xc5\xf1\xe8\x8e7\xf2\xba7\xe8\xb0\xa7`\x97\ +\xf2\xcdA5\xe1\xe7\xf1\xf1\xef\xea\xfb\xcfO\xe6\x91I\ +\xe7\x8b4\xad\xc6\xf1\xc2\xd4\xf1\xf0\xef\xed\xef\xd5\x9b\xee\ +\xb45\xe8\xa5\x86\xd8\xe2\xf1\xd0\xdd\xf1J\x89\xf2\xd8F\ +:\xdb\xe3\xf1\xf0\xb73\xf4\xbf>\xe7\x8c5U\x90\xf2\ +\xa1\xbf\xf1\xe6\x8a3\xed\xd2\xd0\xf6\xe0\x9f\xef\xb41\xf8\ +\xc7Bn\xa0\xf2x\xa6\xf2\xf0\xe0\xbd\xc66-\xd7E\ +9\xcc<1\xde\xa6\xa2[\x94\xf2\xd9\x8b\x86\xbf\xd2\xf1\ +G\x88\xf2h\x9c\xf2\xe7\xed\xea\xa0\xbf\xf1\xed\xee\xf0\x1e\ +\xa2b\xf1\xee\xe5\xd9^T\xdd\xa3\x9f\xc6\xd6\xf1\xc99\ +/\x91\xb4M\x1c\x95Q\x0e\x95T[\xb8\x8b\xe8\xc9I\ +\x15\x8cSy\xc3\x9f\x0f\x98V\x10\x8cN\xe1\xe9\xe5\xb7\ +\xcd\xf1{\xc5\xa1\xd2\xdd\xf1\x0d\x8bN\xdf\xe6\xf1L\x9b\ +L\xf2\xdc\x92Z\x93\xf2p\xb4\x93o\xa9M\x0f\x9cX\ +R\xa8~\xd2\xc2J\xb4\xcb\xf1~\xa9\xf2\xda\xe3\xf1\xb4\ +\xb8J\x0e\x8eP\xf7\xcdJ\xbf\xd3\xb3y\xa6\xf2\xf6\xcc\ +I1\x9fS\xb3\xbdMN\xa9TT\xbb$\xa6\x00\x00\ +\x00qtRNS\x00\xb4\xf6l\xde||\xa5\xcc\xdb\ +\x09aZQ\x11\x04\xbe\x10Q\xe6e\x14j\xd6\xe6\x04\ +\x09\x02\x0c\xd5\x0b\xeaa\x0b\xb0\x1be\xb4\x17\xb4\xe1\x11\ +U\x06\xbe\xc4\x17k\x06\xe1Z\x10\x0c\xea\xd6\x22\x02\xd5\ +\xc4\x1b\xb0k\xfc\xc9\xfcUk[\xc9\x11\x11\xfdkH\ +jE\x14\xfd\xb9HkEj[\x14\xfe\xfe\xf8\xfd\xfe\ +\xfe\xfe\xfe\x07\xfe\xfe\xfe\xfeZ8\x096[\xb9\xf8\x07\ +\x096l8\x5cmZFt#+\x00\x00\x01\xd9I\ +DAT8\xcbc` \x05\xf8xz\xb0\xb2zy\ +\xbbb\x97\x95\xd1\xb7w/\x04\x03\x07]\x11.\x0ci\ +i1\xf9B$ \xc1\x8d&\xaf&P\x88\x06\xd45\ +\x91\xe5\xcdY\x0a1\x80\x995B^\x0f\x8b|a\xa1\ +\xa51L\xdeB\xa7\x10+\xb0\x81)\xb0mh\xc2\xae\ +\xc2\x08\x22obZ\xdb\x0a\x15\xe9\x9e2w\xe1\xba\xe5\ +;Wl\x9b?gr}]\xa3\xa1\x01X\x81SI\ +I\xe7L\x90tE\xf9\xa4\xb2y+7\xac^\xb3v\ +\xcf\x96\x05}-\xa5\xa5\xc5\xce`\x05.%%\xb3\x17\ +o\x02\xca\xef*\xeb)\x02\x82}\xeb\x0fl\xdd[Z\ +\x0c\x02~ \xf9\x10\xdf\x92\x92\x92\x98\xc3\x87\x0a\xcb/\ +n,\x02\x83\xed\x9bw\x17C@x\x0aP\x81?P\ +\xbe$1\xe1\xd2\xf93G\x8b\xa0`\xff\xd2\xa9P\x15\ +\xc9@\x05\x01 \x05%q\x07\x8f\x1d\x87\xc9\x17UU\ +N\x84*H\x03*`\x03+\x88\x8a?r\x1a\xae\xa0\ +\xa8\xbf\x0d\xaa\x80\x19\xae\xa0$\xf2\xc2\x0e\x84\x82\xdef\ +$\x05A\x10\x05%\xd1\xab\xe0\xf2'\x96L\x83*H\ +\x02*\x08\x84*\x88\x08[\x04S\xf0\xfa\xdc2\xa8\x82\ +T\xa0\x027;\xa8\x8a\xd0Y\xed\x10\xf9'\x0f\x1fC\ +\xe5\x99bA\x01a\x05U0\xa3k\xc2I\x90\x17>\ +V~x\x0fU\xc0\x08\x0eI\x15\xa8\x82\x92\x8e\xe9\xa7\ +\xee\xbf\xba\xfb\xe6\xce\xd3\xb7P\xf9b-\xb0\x02)a\ +\x98\x8a\x92\xb3\xd7\xee\xdd|q\xe3\x11L\x9eS\x12\x12\ +\x9d\x0a%H\xe0\xd6\x83\xdb/a\x0a\x84`\x09B\x15\ +I\xc1\xd5w\xcf`\xf2\xec\xf0$\xc7'\x8ePP}\ +\xe5:T\x9eC\x11\x91(\x95ea\xf2\xcf\xab/\xc3\ +\xe4E\x91\x93\xb5\xa0\x12TA\xcd\xe7OP\xf3\xe5\xd0\ +r\x06\x8f\x06D\xc1\x17\x88\xfby1\xb3\x9evpz\ +FI\xc9WP\xf81:\xf2c\xcf\x9eY\xb9y\xf9\ +9\x05\xd9\x99$\xe5x\x00\xfe\xa1E\xc6\x0b}\xa2(\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x06v\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x03\x00\x00\x00D\xa4\x8a\xc6\ +\x00\x00\x00\x09pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95+\x0e\x1b\x00\x00\x03\x00PLTEGpL\ +C\x96\xbbs\xdeB'W\x9e#[\xa70\x81\xd51\ +\x84\xdb!U\x9f9tr%H\x8c)E\x8c*G\ +\x92`\xbb>'H\x8f.~\xda(E\x87f\xc5D\ +1\x83\xdb5\x83\xbcP\xb1\xe20WuV\xc0\xf03\ +}\xb0 J\x8a N\x94s\xdeBS\xa4Rs\xde\ +C/\x80\xd2,z\xdar\xdaD,E\x901\x84\xd8\ +\x22C\x86o\xd7K+s\xcfX\xc2\xe4,x\xdaY\ +\xb9\x95h\xce{h\xd2\x83d\xcd\x97g\xd0\x8b-y\ +\xc9Z\xbe\xad#O\x96'L\x95P\xb3\xe4$K\x92\ +p\xdaU_\xc8\xbeZ\xb0NU\xa48e\xc39$\ +O\x99d\xcd\x9e,y\xcd0\x81\xdbq\xdaG\x22Q\ +\x9at\xdeAt\xdfBq\xdbO^\xbaZo\xd9Z\ +`\xc2yk\xd4v'K\x92b\xcb\xab-y\xd1V\ +\xbf\xf3(P\x9f!J\x8a)>~t\xdeA(G\ +\x8bV\xbf\xf2'I\x8c+P\x9b*r\xc8\x22J\x8c\ +)H\x91#Q\x9d)H\x91/}\xd1'g\xa6&\ +d\xb4/|\xd1(i\xb8a\xc5~-M\x9f&c\ +\xb1,v\xce0J\xa26\x85\xba&U\xa2^\xc7\xc4\ +!\x5c\xa2V\xbf\xf1?\x90\xa1c\xca\x8e+t\xc1+\ +u\xc1\x5c\xc3\xb9E\x9b\xafV\xbf\xef^\xc3\x9c.|\ +\xdaF\xa1\xcb1j\xb7=\x90\xba-v\xb8V\xbf\xf2\ +K\xa6\xba<\x8e\xc1m\xd6jU\xbb\xdcK\xa8\xd3W\ +\xc0\xed.w\xbaX\xc1\xe71}\xbb,x\xd4W\xc0\ +\xeb>\x93\xc6V\xbc\xe0Q\xb4\xd9 P\x960{\xba\ +T\xb4\xbc$a\xa0n\xd7cY\xc1\xe3%h\xbaS\ +\xb8\xd9[\xc4\xd47\x87\xbfq\xdbM-/qp\xda\ +ST\xbb\xe9Z\xc3\xd83\x86\xdaW\xc0\xecW\xc0\xe9\ +Y\xc2\xdcP\xae\xc3L\xab\xcc;\x8e\xc00~\xc74\ +\x82\xbf,y\xda%Z\xa9(m\xc4!S\x9a'g\ +\xbe'h\xbe+x\xda*r\xd1+x\xda+w\xd9\ +2\x86\xdb3\x88\xdb0\x83\xdb3\x87\xdb\x1fK\x8c2\ +\x85\xdb\x1fN\x93/\x80\xdb\x1fQ\x99t\xdfA L\ +\x8f M\x91\x1fI\x8a\x1fO\x95q\xdbPX\xc1\xe5\ + J\x8b1\x84\xdb\x1fP\x97.\x7f\xda R\x9an\ +\xd8dq\xdcLo\xd9\x5cp\xdaV-|\xdar\xdc\ +J\x1fK\x8dY\xc2\xe1Y\xc3\xddk\xd5tl\xd6n\ +g\xd1\x8aa\xcb\xb0b\xcc\xabd\xce\x9d_\xc9\xb8i\ +\xd3\x82h\xd2\x86m\xd6jc\xcd\xa5`\xca\xb4s\xdd\ +Cp\xd9Y3\x88\xdap\xdaSs\xddFX\xc1\xe9\ +/}\xc8,z\xda3\x87\xdak\xd5rj\xd3|j\ +\xd4xi\xd3~W\xc0\xeff\xd0\x91g\xd0\x8e_\xc8\ +\xbcf\xcf\x94m\xd7ge\xcf\x96c\xcd\xa1s\xdcG\ +^\xc7\xc1n\xd8an\xd8_W\xc0\xea]\xc7\xc7\x5c\ +\xc6\xcc]\xc6\xc9W\xc0\xec\x5c\xc5\xd1\x5c\xc5\xce[\xc4\ +\xd53\x86\xd5Z\xc3\xda[\xc4\xd3/~\xceZ\xc4\xd7\ +1\x83\xd12\x85\xd8e\xce\x99s\xddD R\x9ca\ +\xcb\xa7_\xc8\xbf0\x80\xcd,w\xc4^\xc7\xc4/|\ +\xc5-x\xc01\x83\xd53\x88\xd9.{\xc2|7\x1f\ +\xe1\x00\x00\x00\xa1tRNS\x00\x16\xc1\x1a\xfd\xfe\xfe\ +\xfd\x04\xc12\x8d3\x80\xf8I.\xb7\xfd7\x09\xfd\xfd\ +\xfe\xfc\xcd\x0d\xa8g\xa0\xd9g\xbe\x0fX\xfd\xc9\xfe%\ +H\xfcXhG;\xd8\x9f?\xe3\xf7\xa5B\x1b$\xcc\ +\xf4B\xect\xe7\xb0\xec\xf4P\x84\x91\xe7\xae\xb8N\xac\ +\x91\xf5)\x80_\xe4v[:\xf4\x9a\xdf\x87\xfdF\xfd\ +\xfd\xfc\xf7o\xfc\xfdh\xfd\xbb\xf3\x12\xf2\xf9\xfd\xa2`\ +\xfd\xfc\xec\xfd\xe3\xfdm\xfd\xfd\xb6\xfd\xfd\x89\xfd#\xcd\ +\xfe\xf3\xfd\xfdz\xfc\xfe\xfd\xfd\xfdC\x1f\xf0\x86.\xe8\ +y\xfc\xca\x1f\x9b_\xa2\x9d\x93\xb6\xbdG\x8a\xbc\xcf\xd5\ +\xf5\x99Q\xfd\xba\xfc\xb2\xc9\x8b\xc8u|\x80\xe1\x00\x00\ +\x02oIDAT8\xcbc` \x010\xf6\xf4O\ +PQ\x99\xd8\xdb\xde(\x82MZ\xbf@\xf4\xc5\x85\x0d\ +{\xf7}x\xf7\xeay\x82\x91\x0e\x86n7\xd1\x1bO\ +\x80\xd2o_\xbf|\xf6\xf4\xe1\xed\xa3\x87\x5c\xd4P\xe4\ +\x85}n\xc0\xb5\xff\xfa~\xf4\xd4\xa1\xc3'\xcd5\x91\ +\xe5\x93P\xb5\x1f>y\xff\xc8\xbd\x9bZp\xf9\xd2L\ +t\xedG>\xdf\xb9uPC\x1d*\xcfQ\x86\xac\xfd\ +\xd4\xe1o \xed\x07\x8f\x1f\xbb\xea$\x05QP}a\ +CIN\x91\x90P\x5c^a:H\xfb}\x90\xf6c\ +\xd7\xaf\xed?P\x0c\xf1@EmM\xf9\xff\x8b?~\ +\xfc\xb8\xf8'?\x03\xae\xfd\xda\x95\x03w\x9b\xc1F\xb4\ +\xd4U\xfe\xfe\xf8\xe6\xdf\xbf\xb3\xff>\xfd}\xffS,\ +\x0b\xa8\xfd8H\xfb\x89m\x8fv(\x81\x5c\xd0\xd6p\ +\xf1\xcd\xd9E@\xb0d\xc9\xc2O\xac?\xc5R\xa0\xda\ +\xb7=>\xb3\xb3\x1b\xa8`R\xc7o\xb8\xfcBA\x99\ +\xb4\xd4d\xb0\xf6\xbb\x8fv\x9c9\xb7\xbe\x13\xa8`\xf2\ +\x9f\x8fg\xa1\xd2\xac\xb9 +C#\xc1\xdaw\xec<\ +\xb7~\xb7\x1c\x90?\xe5\xe2'\xa8|H<\xc4W\x8e\ +`\xed;\xd7o\xdf}^\x12\xc8\x9d\xf6\xe3\x1fXz\ +\x19+{\x14D\x81=D\xfb\xf6\x07_NK\x03\xb9\ +S?^\x06\xcb/\x8b\xde\x1a\x0bQ`\x0c\xd1\xfe\xe0\ +\xcb\xaaU _(|\x02Ko\x0a_\xf9\xd5\x1d\xa2\ +\xc0\x01\xa4}\xf7\xf9\xd3\xabV\xc9q\x00\xb9}\x0b\xc1\ +\xf2\x8b#V~\x0d\x02\xf1\x19\x04\x0c\xc1\xda\x81\xf2\xb6\ +\x02 \xbe\x0cHz\xd3\xe2\xc5,[\xb7n\xf5\x03\x09\ +\xf0\xd8A\xb4\xafb2\x05\x1b\xd8\xc4\x06\xd4\x0e\x04\xec\ ++\xb7n\xb5f\x06\x89\x98XI3I\xdax\xf1\xc0\ +\x22[\x01,\xbf\x02\xa8`\xe5J3E,\x89Q\x9b\ +\x0d(\xbdb\x05\xcb\xe6\x95+7o\xd6\xe5\xc2\xa2B\ +\x10$\xbf%l\xdd\xe6\xcd\x9b\xd7\xad\x93\xd0\x83)\x11\ +\x91\xe7\x86\xb2\x98-\x81\xf2[b\xd6\xac\x03\x82\xe5\xcb\ +\xd7\xac1p\xe6\xe6\x0d\x0e\xe4\x0c\x10\xe7\x87\x19a\xc1\ +\xb7e\xcb\x96],k\x96/\x07\xc9\xafY\xbdz\xcf\ +\x9e\xa5\x1b7\x8a\xfb\x22,q\xe5\xdb\xb5kWU\xfd\ +\x9a5\xcbA\xd2\xab\xf7,\x05\xc9\xf3\x22;\xc3?\x1b\ +\xa8BYb\xcf\xea\xd5P\xed\x1b=\xbcQ\x1d\x9a(\ +\xbb\xeb\xd2%e\xf6\x8dKA\x00\xa8\x9d\xd3\x13\xc3/\ +\xad\xb2\xd3UU\xe7\xcc\x12_\xbbv\xad8'?\xd6\ +\xcc\xab8s\xfe\xdc\x05\xf3f\xcf\x90\xef\x22%\xcb\x03\ +\x00/\xab\x8c\xa71\xf3a\xb4\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ " qt_resource_name = b"\ @@ -334,17 +620,41 @@ qt_resource_name = b"\ \x0a\xf7\xc1\x07\ \x00n\ \x00o\x00n\x00e\x00_\x001\x002\x008\x00.\x00p\x00n\x00g\ +\x00\x08\ +\x09n\x91\xb3\ +\x00b\ +\x00r\x00o\x00w\x00s\x00e\x00r\x00s\ +\x00\x0c\ +\x0fTN'\ +\x00b\ +\x00r\x00a\x00v\x00e\x00_\x003\x002\x00.\x00p\x00n\x00g\ +\x00\x0d\ +\x01\x97\x91\x87\ +\x00c\ +\x00h\x00r\x00o\x00m\x00e\x00_\x003\x002\x00.\x00p\x00n\x00g\ +\x00\x0b\ +\x02\xb2\x0c'\ +\x00e\ +\x00d\x00g\x00e\x00_\x003\x002\x00.\x00p\x00n\x00g\ " qt_resource_struct = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x01\x8db\xb8\xc9\xc8\ +\x00\x00\x00P\x00\x02\x00\x00\x00\x03\x00\x00\x00\x05\ +\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x002\x00\x00\x00\x00\x00\x01\x00\x00\x01\xbc\ \x00\x00\x01\x8da\x99\x15\xcb\ +\x00\x00\x00\x84\x00\x00\x00\x00\x00\x01\x00\x00\x18\xc4\ +\x00\x00\x01\x8dd\xf15i\ +\x00\x00\x00\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x1eE\ +\x00\x00\x01\x8dd\xf1K\xbc\ +\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00\x13B\ +\x00\x00\x01\x8dd\xf1\x5c\xbe\ " def qInitResources(): diff --git a/global_vars.py b/global_vars.py new file mode 100644 index 0000000..817e417 --- /dev/null +++ b/global_vars.py @@ -0,0 +1,34 @@ +# coding: utf8 +from PySide6 import QtWidgets + +SoftwareStatusRole = 0x0101 +ExtensionStatusRole = 0x0102 +ExtensionIdRole = 0x0103 + + +def accept_warning(widget: QtWidgets.QWidget, condition: bool, + caption: str = "Warning", text: str = "Are you sure to continue?") -> bool: + if condition: + b = QtWidgets.QMessageBox.question(widget, caption, text) + if b == QtWidgets.QMessageBox.StandardButton.No: + return True + return False + + +def get_with_chained_keys(dic: dict, keys: list, default=None): + """ + 调用 get_with_chained_keys(d, ["a", "b", "c"]) + 等同于 d["a"]["b"]["c"] , + 只不过中间任意一次索引如果找不到键,则返回 default + + :param dic: 目标字典 + :param keys: 键列表 + :param default: 找不到键时的默认返回值 + :return: + """ + k = keys[0] + if k not in dic: + return default + if len(keys) == 1: + return dic[k] + return get_with_chained_keys(dic[k], keys[1:], default) diff --git a/images/browsers/brave_32.png b/images/browsers/brave_32.png new file mode 100644 index 0000000000000000000000000000000000000000..551243eb94e0e0135e966a42b82ea230f81bca11 GIT binary patch literal 1406 zcmV-^1%djBP)A5S9>1OWi|aWwXBFyCY>0000@A`cV^ z1Ofp7o<|?pVJh8YD=icVQ6vzxQ6nM`1_lBF7YYQSNFVooMcZO4_HHosZZJ+G53f!l z6$u1iDiQ(#0I5tNHN;MCgA{_kTywbV=lXPv~ea-FQj&bvoaBPU&eb_jy3~c|iAXG5`Pn_V^q4s}C{LZ=f>*fCZ z`}A!v=7Uz_fmGvxRr~ew`J8g%iD3Kn^ZUuN@u-IHZ87lF$Ncs2<#Im#`S|(p>*j)0 z<&bIo*vR&8G39bS_HHrp*2(#rZvFcB@uh;~k!tX3FXD+|@N6;r$FJ$Sr0{Gne zd`*o2wsN#K2_<>90ntAtjJotrC>A0fgXDj zTJ4u`@0fAoqlfs6Ti}Ue{ovB^fll(QjQN&n^uw+E&${oKasK)C;E7=w2jr#z003Wf zQchC<;UuwR2>kx(5@90;uj1kZWS{~CC9?kE{rzH;Bt!_4>qChK1?sQ@>H;{TLuEpT z1vdU!TZoAf0Ra@(*)2O0T+Dvs5YBwhD>4D(&tAp$%{jw2$~-RMG50Os=;!_j1>XPw z0sBcrK~y-)V_={nFcD9L0%KEb@*x57a1a%QF6$Z=9T$%TV#Dn%kmVf$mt@7GfUNl* z)<_CMV|I4oiM&0#cW#J}k3h&rvc<=5Nk9R+*2c4W!4(9@ z$8Q1(7N|{|wLeas)xd(P6EM5?w?gv%i5+6Su zr14OG!Q+I4O9lCRLAU>GbnG7JK7APQt6j}N}{=|i=*X5kOaC&pj zs#%lkfC9Qm3RE?L0#g<(Uk@^V#K(3++a*7cGIVx%1GH?)11$iM}2PMfVNrA+Y z=d4Ibm^m#O#FmspQJ`VK4mP8szN@hl#9~+0LQS=XEF9o;mX-#l)bvC!lD(`tJ1jM_ zi-}^(81m8(L*h9ES#jhoZeE}v?95EKa~}^EBOePZ-VDmgNotk`0R5Qec?Z5xC;$Ke M07*qoM6N<$g6w<6>i_@% literal 0 HcmV?d00001 diff --git a/images/browsers/chrome_32.png b/images/browsers/chrome_32.png new file mode 100644 index 0000000000000000000000000000000000000000..f00d8a82c15920dd772ef31d8b255dfcc622e024 GIT binary patch literal 1405 zcmX9+2~<-@6b*p@2}^>N(rRrv7F~H;xrh;K))zpg`CJ zqYzLwZD}g8h!!ItUuAd2%>#p|T3_w&sB#^m*Km-=Jj#(zlTo7n>k!fBWXkO04#rYHs zv@EIAz;dmPqo=b!6Nc8;6{H=din1K}iHs(?O9$CyTSf4E2ld%)Qe*Dk!6u(YDHVS$ z6hv8yJvP!+dd*`(;%k~jCFHQLHqaEm`GVd3a^8cQVAIt>X5UMA;jVa+$DC5aemRJF z84oWEW&FrdRroHIk=jGZ>Z=rR@s+77IvH(@>9aCb<)4pOR!0h>$da)quVX{Mt3W!R z!z*3;9}UCu30SMhSYFmF3cQsSMXN(x?W@%6Cyk%iL@5~F17omLA$OtJbH1hdRu4R1 z=z-k#m=`zluONeDx0yK@US3?4ADk6HbNQ~n^uk>)V9l~>c|@p|xXssJdi4e#nuJ-G zkyn1+<8NV2o6863{<#9Tpkky}3eV=bsD_7DrfZfZL~S4CqzFlFKs3`Gz3vV#>0SdQ z;#5OjL>Z#Uza$-o8M#Q@HDpe;D3Zb|wdU)(7+9@T9Og~lsOcSrWnNAKiT_vShrnNm zxR*aKgDeZUDcv>e9a0c5v!HAngfH~e4o|{n`2nwIVYZNsOq7I|BBv##r<(o2#}8~u zAMRpF`r-6uWb7?0xyLduYrqq7EBwCRY+Vzh~P>k)~SWX2-M@Zn>tc*B`+g6ZPu zcJ#>Ot>XBPvX4RW5i$#lPw~v-^v$2znep`F5u4Z6w4c_%W3&$8o>=#YcC8WCJ;5{b zM7ZDCZ|zYiJq1J~^E~@w`PLY1@_F%{^C=Hvu9QD(OL8*MH;S@wG(C2VsH50ecmA~F z*BzW(!xwG>@$wm^6p_`ktqJjH9h>tJNv= zpUZYjyV03xOZbGzh;UPMy0wn*x}>C}vO5RY?{snxI!h%?vZ5YH#&I%yIS*}!Gs8L; zM!tyu-*7X>Qg#5NXN|EvV^p*)M`jfi6p(Gl)VJg7+vn!_CT?jFHA&5GUHq~|WP@~> zXJ<}*Z-XHdFt*QbPa``k*jaW=w4HIbU9NG20ax?ygmY6SPlWdHY04W4QxSAK@!wFD zRWW3}XiU)F8}VIGvDN zM1I?;`!Bkn^sZ(SlxSFFNb9u9T}>gjR-Ze4-Vc(6aI{2kBvxJ>)}EsC e!YHb2l*y>IT6(WZTHP+tVJL_mKx?7!Gyexht*cZ3 literal 0 HcmV?d00001 diff --git a/images/browsers/edge_32.png b/images/browsers/edge_32.png new file mode 100644 index 0000000000000000000000000000000000000000..f8507c4fa24dae61a00bc231cdbab75073216ed1 GIT binary patch literal 1654 zcmV-+28sEJP)1HxeXt-(iXcvubKXKzq*8Okxs_1Fxkh(2)xK_XNOQyO!j=^o# zYE`@3OQ_RV!0j$~x>&*IF@3u%c+^+G>pqjlR=nU*wAmm~mN0v|RJ6P#VW4i;V_CuD zC1|=+xY=98)HjE}aobHTFL7|%Q&hX@TEo~ghT2!a>{r0)S;E{(}HNyhHchrW6h;t z%CvLcLvYzyGl<%7+Ea7gMp(h=FMY@?dfGFG+H2KvYSVmb)OcyrepkTnX3&vm(2ifo zyk^goZP#aI&z57&p>y0vUdO?1*kNwiUsu5DUB|~<#>`#D$ydPaT*c8`#m-yA)iZ|G zTEp5~#M3W+&RWFRF@w=Eh1g}znRDGlAX1!R%coz+zc7H!EO*3S$HXsu#VvTiF@x1J zh}kZC!hAO$;Q#;tp>$GCQveph8vXwM{RF`>jWdAwNiMhjHwpdy{`}1isAr(rXTA?u z{mK3%Nc>o6M?2V`KjZhMLK`H^^g`@(=dkSbP=t}^uDDLDk@YEnU*vXMI`o>~hy6zV z{rvZD{QYSCyYmwA`Te3`{rv3xh@d-r{K_v(3qkq(A#H_;nUfYNVYzkK%e;q9Y`T+~qHg?(ALmev5QHHGj)ZuR+?FWw^yXqtbQBZwh zLrG4NkWgG+VLC|reUSEJ*RSnAPz>=vMQu&_|BF9<{J8ihKLf7)+LZ%$o54o3)K&ie z_~zfgv;RK*eg9J|3#k1#Nc)Lxk9H`5T+r5V@y*#qK(J)V!cVL}#ZsW!w>>_yvm2;j zQpaz&{6ayQwA5s<_PdXEoH@6?6R2SFpO0su(yX~4D?=l}+IQ?cw|=)AkU!<(C#ZZ# zJj7RyVC_5CZ{K}h63CtQ;~!Xh87q4fM1eg-`}PN4PD=y1Q$Jn?%P))G8VgZi4AK7J z%c@l%UnqP6OK%Ea`PChwzyYLv_xaPSR>^SyxqS=4@{1!@ehmWg83YW$+JW-60wBH& zNP5%a#VlL5ZuJ8R@Yq4LuVOL<8`#7K)D8sfE4Oa7W(J9vSV=QU+IaE6B3l70zXGUW zrY}<-(#u5G21PY~AL~o7b(A(1R%vk>`f8m@R?wTVmF% z1A^tt*Q_yc=H?9!=W%i9rK>( ProfilesData: + local_state_path = Path(USER_DATA_PATH[browser], "Local State") + local_state_data: dict = json.loads(local_state_path.read_text(encoding="utf8")) + info_cache_data: dict = get_with_chained_keys(local_state_data, ["profile", "info_cache"]) + + profiles_data: ProfilesData = {} + + for profile_id in info_cache_data: + profile_data: dict = info_cache_data[profile_id] + profile_node = ProfileNode( + gaia_given_name=profile_data.get("gaia_given_name", ""), + gaia_name=profile_data.get("gaia_name", ""), + name=profile_data.get("name", ""), + shortcut_name=profile_data.get("shortcut_name", ""), + user_name=profile_data.get("user_name"), + ) + profiles_data[profile_id] = profile_node + + return profiles_data + + +@dataclass +class ExtensionNode(object): + icon: str + name: str + # path: str + profiles: list[str] = field(default_factory=list) + + +ExtensionsData = dict[str, ExtensionNode] + + +def get_extension_icon_path(ext_icons: dict[str, str], ext_path: str, profile_path: Path) -> str: + if len(ext_icons) == 0: + return "" + + if "128" in ext_icons: + icon_file = ext_icons["128"] + else: + icon_file = ext_icons[str(max(map(lambda x: int(x), ext_icons.keys())))] + # 如果路径以 / 开头,会被认为是根而忽略其他,因此需要检查一下 + if icon_file.startswith("/"): + icon_file = icon_file[1:] + + full_path = Path(profile_path, "Extensions", ext_path, icon_file) + return str(full_path) + + +def scan_extensions(browser: str, is_chrome_compat=False) -> ExtensionsData: + profile_data = scan_profiles(browser) + extensions_data: ExtensionsData = {} + + browser_data_path = USER_DATA_PATH[browser] + if browser == "Chrome" and is_chrome_compat: + pref_file = "Preferences" + else: + pref_file = "Secure Preferences" + + for profile_id in profile_data: + profile_path = Path(browser_data_path, profile_id) + secure_pref_path = Path(profile_path, pref_file) + secure_pref_data: dict = json.loads(secure_pref_path.read_text(encoding="utf8")) + ext_settings_data: dict = get_with_chained_keys(secure_pref_data, ["extensions", "settings"], dict()) + + for ext_id in ext_settings_data: + ext_data: dict = ext_settings_data[ext_id] + # 这里插件有几种情况 + ext_path: str = ext_data.get("path", "") + if len(ext_path) == 0: + # 如果 path 是空,则该插件可能是一些内部插件,不予收集 + continue + elif not (ext_path.startswith(ext_id) or os.path.exists(ext_path)): + # 如果 path 以插件 ID 开头,则为商店安装的插件, + # 如果不是以插件 ID 开头,但是是一个存在的路径,则为离线安装的插件 + # 否则,可能也是内部插件,不予收集 + continue + ext_manifest: dict = ext_data.get("manifest", {}) + if len(ext_manifest) == 0: + # 如果 manifest 为空,则该插件可能是离线插件,需要额外找它的 manifest + ext_manifest_path = Path(ext_path, "manifest.json") + ext_manifest: dict = json.loads(ext_manifest_path.read_text(encoding="utf8")) + + if ext_id not in extensions_data: + ext_node = ExtensionNode( + icon=get_extension_icon_path(ext_manifest.get("icons", {}), ext_path, profile_path), + name=ext_manifest.get("name", ""), + # path=ext_data.get("path", ""), + profiles=[profile_id], + ) + extensions_data[ext_id] = ext_node + else: + ext_node = extensions_data[ext_id] + ext_node.profiles += [profile_id] + + return extensions_data diff --git a/wg_extensions.py b/wg_extensions.py new file mode 100644 index 0000000..7044832 --- /dev/null +++ b/wg_extensions.py @@ -0,0 +1,210 @@ +# coding: utf8 +import json +from pathlib import Path +from PySide6 import QtWidgets, QtCore, QtGui +from util_ext import scan_extensions, ExtensionsData +from global_vars import ( + ExtensionStatusRole, + ExtensionIdRole, + accept_warning, +) + + +class UiWgExtensions(object): + + def __init__(self, window: QtWidgets.QWidget): + self.vly_m = QtWidgets.QVBoxLayout() + window.setLayout(self.vly_m) + + self.hly_top = QtWidgets.QHBoxLayout() + self.vly_m.addLayout(self.hly_top) + self.cmbx_browsers = QtWidgets.QComboBox(window) + self.cbx_safe = QtWidgets.QCheckBox("安全", window) + self.cbx_unsafe = QtWidgets.QCheckBox("不安全", window) + self.cbx_unknown = QtWidgets.QCheckBox("未知", window) + self.cbx_safe.setChecked(True) + self.cbx_unsafe.setChecked(True) + self.cbx_unknown.setChecked(True) + self.cbx_chrome_compat = QtWidgets.QCheckBox("谷歌兼容模式", window) + self.pbn_export_unknown = QtWidgets.QPushButton("导出未知", window) + self.hly_top.addWidget(self.cmbx_browsers) + self.hly_top.addWidget(self.cbx_safe) + self.hly_top.addWidget(self.cbx_unsafe) + self.hly_top.addWidget(self.cbx_unknown) + self.hly_top.addStretch(1) + self.hly_top.addWidget(self.cbx_chrome_compat) + self.hly_top.addWidget(self.pbn_export_unknown) + + self.lv_extensions = QtWidgets.QListView(window) + self.vly_m.addWidget(self.lv_extensions) + + +class ExtensionsListModel(QtCore.QAbstractListModel): + + def __init__(self, browser: str, parent=None): + super().__init__(parent) + self.all_extensions = {} # type: ExtensionsData + self.names = [] # type: list[tuple[str, str]] + self.icons = {} # type: dict[str, QtGui.QIcon] + self.safe_info = {} # type: dict[str, dict] + self.blank_icon = QtGui.QIcon(":/images/blank_128.png") + self.update(browser) + + def update(self, browser: str, is_chrome_compat=False): + self.all_extensions.clear() + self.names.clear() + self.icons.clear() + self.safe_info.clear() + + self.all_extensions = scan_extensions(browser, is_chrome_compat) + for ext_id in self.all_extensions: + name = self.all_extensions[ext_id].name + icon = self.all_extensions[ext_id].icon + self.names.append((ext_id, name)) + if len(icon) == 0: + self.icons[ext_id] = self.blank_icon + else: + self.icons[ext_id] = QtGui.QIcon(icon) + self.names.sort(key=lambda x: x[1].lower()) + + with open("plg_db_v2.0.json", "r", encoding="utf-8") as f: + self.safe_info = json.load(f) + + def rowCount(self, parent: QtCore.QModelIndex = ...): + return len(self.names) + + def data(self, index: QtCore.QModelIndex, role: int = ...): + row = index.row() + ext_id, name = self.names[row] + + if role == QtCore.Qt.ItemDataRole.DisplayRole: + return name + if role == QtCore.Qt.ItemDataRole.DecorationRole: + return self.icons[ext_id] + if role == QtCore.Qt.ItemDataRole.BackgroundRole: + is_safe = self.data(index, ExtensionStatusRole) + + if is_safe is True: + return QtGui.QBrush(QtGui.QColor("lightgreen")) + elif is_safe is False: + return QtGui.QBrush(QtGui.QColor("lightpink")) + else: + return QtGui.QBrush(QtCore.Qt.BrushStyle.NoBrush) + if role == ExtensionStatusRole: + if ext_id not in self.safe_info: + return None + else: + return self.safe_info[ext_id]["safe"] + if role == ExtensionIdRole: + return ext_id + + +class BrowsersListModel(QtCore.QAbstractListModel): + + def __init__(self, parent=None): + super().__init__(parent) + self.browsers = ["Chrome", "Edge", "Brave"] + self.icons = [ + QtGui.QIcon(":/images/browsers/chrome_32.png"), + QtGui.QIcon(":/images/browsers/edge_32.png"), + QtGui.QIcon(":/images/browsers/brave_32.png"), + ] + + def rowCount(self, parent: QtCore.QModelIndex = ...): + return len(self.browsers) + + def data(self, index: QtCore.QModelIndex, role: int = ...): + row = index.row() + + if role == QtCore.Qt.ItemDataRole.DisplayRole: + return self.browsers[row] + if role == QtCore.Qt.ItemDataRole.DecorationRole: + return self.icons[row] + + +class WgExtensions(QtWidgets.QWidget): + + def __init__(self, parent=None): + super().__init__(parent) + self.ui = UiWgExtensions(self) + + self.browsers_list_model = BrowsersListModel(self) + self.ui.cmbx_browsers.setModel(self.browsers_list_model) + + browser = self.get_current_browser() + self.extensions_list_model = ExtensionsListModel(browser, self) + self.ui.lv_extensions.setModel(self.extensions_list_model) + + self.ui.cbx_chrome_compat.clicked.connect(self.on_cbx_chrome_compat_clicked) + self.ui.cmbx_browsers.currentTextChanged.connect(self.on_cmbx_browsers_current_text_changed) + self.ui.cbx_safe.clicked.connect(self.on_cbx_safe_clicked) + self.ui.cbx_unsafe.clicked.connect(self.on_cbx_unsafe_clicked) + self.ui.cbx_unknown.clicked.connect(self.on_cbx_unknown_clicked) + self.ui.pbn_export_unknown.clicked.connect(self.on_pbn_export_unknown_clicked) + + def get_current_browser(self) -> str: + return self.ui.cmbx_browsers.currentData(QtCore.Qt.ItemDataRole.DisplayRole) + + def show_all_rows(self): + # 在 update 之前调用 + self.filters_clicked(True, True) + self.filters_clicked(False, True) + self.filters_clicked(None, True) + + def apply_rows_hidden(self): + # 在 update 之后调用 + self.filters_clicked(True, self.ui.cbx_safe.isChecked()) + self.filters_clicked(False, self.ui.cbx_unsafe.isChecked()) + self.filters_clicked(None, self.ui.cbx_unknown.isChecked()) + + def update_model(self, browser: str): + # 切换浏览器时 + self.show_all_rows() + self.extensions_list_model.update(browser, self.ui.cbx_chrome_compat.isChecked()) + self.apply_rows_hidden() + + def on_cbx_chrome_compat_clicked(self): + self.update_model(self.get_current_browser()) + + def on_cmbx_browsers_current_text_changed(self, text: str): + self.update_model(text) + + def filters_clicked(self, safe_mark: bool | None, checked: bool): + for row in range(self.extensions_list_model.rowCount()): + idx = self.extensions_list_model.index(row) + is_safe = self.extensions_list_model.data(idx, ExtensionStatusRole) + if is_safe is safe_mark: + self.ui.lv_extensions.setRowHidden(row, not checked) + + def on_cbx_safe_clicked(self, checked: bool): + self.filters_clicked(True, checked) + + def on_cbx_unsafe_clicked(self, checked: bool): + self.filters_clicked(False, checked) + + def on_cbx_unknown_clicked(self, checked: bool): + self.filters_clicked(None, checked) + + def on_pbn_export_unknown_clicked(self): + dirname = QtWidgets.QFileDialog.getExistingDirectory(self, "导出未知") + if len(dirname) == 0: + return + + browser = self.get_current_browser() + ex_file = Path(dirname, f"未知插件_{browser}.json") + if accept_warning(self, ex_file.exists(), "警告", "文件已存在,确认覆盖吗?"): + return + + unknown_ext = {} + for row in range(self.extensions_list_model.rowCount()): + idx = self.extensions_list_model.index(row) + is_safe = self.extensions_list_model.data(idx, ExtensionStatusRole) + if is_safe is None: + ext_id = self.extensions_list_model.data(idx, ExtensionIdRole) + name = self.extensions_list_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole) + unknown_ext[ext_id] = {"name": name} + + with open(ex_file, "w", encoding="utf8") as f: + json.dump(unknown_ext, f, indent=4, ensure_ascii=False) + QtWidgets.QMessageBox.information(self, "提示", f"已导出到 {ex_file}") + diff --git a/wg_software.py b/wg_software.py index 31ce898..7f8819f 100644 --- a/wg_software.py +++ b/wg_software.py @@ -4,8 +4,7 @@ from util_func import ( get_win_installed_software, extract_win_icon_from_file, ) - -SoftwareStatusRole = 0x0101 +from global_vars import SoftwareStatusRole class UiWgSoftware(object): @@ -41,10 +40,11 @@ class SoftwareListModel(QtCore.QAbstractListModel): super().__init__(parent) self.all_software = get_win_installed_software() self.all_software_wz_qic = {} + blank_icon = QtGui.QIcon(":/images/blank_128.png") for s in self.all_software: self.all_software_wz_qic[s] = extract_win_icon_from_file( self.all_software[s], - QtGui.QIcon(":/images/blank_128.png") + blank_icon ) self.names = sorted(self.all_software.keys(), key=lambda x: x.lower()) self.filter_dict = {} @@ -76,7 +76,7 @@ class WgSoftware(QtWidgets.QWidget): self.software_list_model = SoftwareListModel(self) self.ui.lv_software.setModel(self.software_list_model) - self.ui.pbn_export_unknown.clicked.connect(self.on_pbn_export_unknown_clicked) + # self.ui.pbn_export_unknown.clicked.connect(self.on_pbn_export_unknown_clicked) def on_pbn_export_unknown_clicked(self): unknown_software = {}