From 9a3cf3620a437fb8049eba3c83da1c0fb1f04b78 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Sun, 24 Dec 2023 02:07:03 -0800 Subject: [PATCH 01/13] Prog collection about right --- .../apps_interface.cpython-311.pyc | Bin 0 -> 2622 bytes backend/src/__pycache__/main.cpython-311.pyc | Bin 3883 -> 3953 bytes backend/src/apps_interface.py | 56 ++++++++++++++++++ backend/src/main.py | 3 +- 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 backend/src/__pycache__/apps_interface.cpython-311.pyc create mode 100644 backend/src/apps_interface.py diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1a5711a452166db3c6bc46d1e3e446157fb63a1 GIT binary patch literal 2622 zcma)8O>7%Q6rNdoy|%Ni&VI%k@rNr}nOy zS(B!6z=9kCiUbExBLPw_l%jydkt1p^9PMfk)<~8rp&pPp7zHH6i8t%miBlnUcJ|He zyf^R7%=_lEzw`OL2+BQajV!tl`b~Vai?q|(PC(}#(vXJpXuj3rd2D}6^OD^<<{ftH zoOePi<>hNga}d{pq&Zie^NJ>~qItLG0_oB4RTNQv7tRwA%<(DiNEeG4yc10{PIw*g zZvO$|9y0gn7CZg>pxc7CRWy9tUN1dn%Iz_P7KInPXi^z{g}#wA#}Cf?V7b-a9m24q ztIroh-ie`@?zUfqKEltnKG*-u?eY{+5?r~S(=}s>-E}cDP0jE%I3$_fBKbU*<2k)_ zhr6cIbSbAtWbQ8J)8>Lf3*5z)nMn%V%Stzk)W{IVxO~|t5}gMVhIz42%##AqO`^pp zHE0A=amY-ZX87OP=&Q*&Mkq_NTSn~e*gfUBAH|T9vOlL?kY7`1-UAvsl6VoKg zo9WDLqH9S;Gf7AvOXa{fT>#7R;xhNxOHYA<_Cf@Y2=yL_3Ti0+)emb*zoqnVTzssI zR+Z6)yU%j>Zwzj}xG}gf_`s~X&)3}NE%$lov}(f>SU+C#3|O9ljqGF3Xw@^?I51Ro z4?W$Zxb@gGR`rav6o0#re;5q5At=fS(Jtb=ZERJba}Q}c?QMH20cnZC9Y6Z}< z*+E|0m1y^(tt^$L1+3v!tVusO?n9KV4*u@L?5&OmjMh(=0d)~);u+uv!Rz)Ih;HC( zxCP*DN8mzA+zn2Vo26{Hz_MWgLm2X(4GYv1DP#pYPCZ>9tz1(-c>dQtkxv(HYUvAi z2eZUXWm>sq;}dx!lg_gX&}r$9gEcz>qJn-Lt)7klqJe-~J2hjSnyDZf03AmW2bay9 znI~M?@x^;I!ZI{hG;@Xy>cb*VP6u4%^F&LDCDL?(aTj0&aKx1ze1WOEy&SOtBg?qc zZp2ZB!(n>^T;02DIs~f2@Ukq33aa}9mFcJIa7`Vt)R9W;N$;U1Qe>|!)|_>B?`Qr` z{S7r(KRi$$Ia%$WY1MjYu&FqG2mVAL{u1I(hs)#r3xR0vie981tjyXw&p^CR5xgT7 zx&15TxjT8gtfe8dGXeOi!$RGh)Q!nq7Ape>@3X{!V969WY^QXwQHKl5xEnZ{bhJGI zb!!0cAFnvNyudF*tCVrsu`j-}%d&%au-wP9E3&8SrY$NRud=q!>)zF^xXRMz9+qs+ zLa6NOUOC*VcV9$i->$_7qDNO0^U&@{S=m452Jrui@`18mZnaBz3EgqrKufqR**Em= zxvR5pUb{Z|RxCM_%TR+E3ubbMq?6GSrNESL19<|_lXLnG#;M3G@0rRmpy=fU+`mW< zcL8ftlI8xWq3a}LzD2S*X3}NeH+?B~BNd;GPR3I+lhNsmi5ORA;bL4O%Uq^uJxjQH zr9|lRMSZ~#gGx*<6-1{s{B}BDBC$I(B%*p^cQVAjc=RBAN8H>YqB?GqWhyQ-$2lGl zyI4w7l7i8c;0Lbm9pb*um#xHvi5$QMw2OTU=`k3w#YyO}ryzjfy}gzVZn^sju(Lm#yH~CaVQcTfx&0CavIj&3DG~ zooOPs47V1<8g7J!zfL?j_YU`rw@D1zA+&^obX1E%a2_C=z#oaK!$$AOI2|!;(rx) zE;2*slELVR`1HZA<#?u`rOf3bVcY?^w|^gP0<#bD`xc|NS-N0or98PnUxC@;$Ahhc zfYo4JM}ca)){(zrKXueov7b8fRP3jYd=>ktqhr-}ZMwX8`15m31kHXiZ~z}{qK+8p M#epVzR@m(KA0^;nF#rGn literal 0 HcmV?d00001 diff --git a/backend/src/__pycache__/main.cpython-311.pyc b/backend/src/__pycache__/main.cpython-311.pyc index 291653a72af8b2f76265bae80499278779d58a7f..949a77a20a0a3bde374c94ad5b8aa3297d873478 100644 GIT binary patch delta 409 zcmZ22_fd{-IWI340}yciZ%<|5p2#P`xMZTbe_Sq46b~aq3PTEO4sR4MnC6S(1JnFb z{9sxjN&rZ+Iy~% zph}I+HB66~7^OGIv)V8*T5q1i{(zCub#pBz6C-2b9R;-JY-dGdsAFlb*uMU#Vh`&f7Q NUEufL{Djw?5dg?BT`~Xw delta 400 zcmew;w_1*GIWI340}%MXtWQ16HIYw(aoR+6|N1EI6owSm9G)m1FwGmq3#R#^_<%H9 z4u7sdlprGmSd2ecD4B_oAy+s`1S%pLCCbE*Dw-t*G8hDaY;g#Sfng2%GDZf5)j$jZ zj0`}v66p+UIFQ73fpSrjsT`>ysa)yIQBo Date: Sun, 24 Dec 2023 03:00:59 -0800 Subject: [PATCH 02/13] pywin32 being stubborn --- backend/requirements.txt | 3 +- .../apps_interface.cpython-311.pyc | Bin 2622 -> 3046 bytes backend/src/apps_interface.py | 83 ++++++++++++++---- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index ddd7010..4001ad7 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,4 +3,5 @@ uvicorn pydantic spotipy pyinstaller -pillow \ No newline at end of file +pillow +pywin32 \ No newline at end of file diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index b1a5711a452166db3c6bc46d1e3e446157fb63a1..f0eedfb115fe4d9709b0030d9fbaee25e6855275 100644 GIT binary patch delta 1609 zcmZ`(U1%d!6uvi^WM=XoH*I5EWLjy8a_6=& zG1=6mV3D%hZVD}{V)UUReevhX7r}QQk|kgn2#8SMgov()Po6s=Nq^YGz2BTU=iGD7 zz4zSnW9;{V&{x5r4*|U#-d01Qy-*y#y*KgV1YYziZk1nhDclyfC@7vSv?wZGK%c?` z`W3u@WZ~Pw9~cL}&$@oV`1#5x_cb1@{LFpm^MXe=%)fry`{IQg^OR#D+O)#t4pq-c zJ2HgM*?KCw-dI_2D_G%C6Gk-L;SnmLi&_OOAR6ge+{3c> zHep%Y{x{}de|3IlZg%Fa8C#swGNf!;<`OOV7HFDKX-+lP3quQei>j+qPb9r!8dNo? zbzK$-J5l0i-dv_-b8BguwYe2jGi+WpHZ)=yB*wJ;x@OUol{E>?tWztM(+tJTStJBX z8QcD4-F!E#ms8Av#Wp7qwk|G*S~rNED)Fx(5wEJWS`VcR+f8DExM4rE6Dpb!2D17cYiQqYk z@(s?ScOI1RQ$lrXU4ZV-xSlDqfYA39ZUM0=dkQI^1T?Q^ z)`_KUsCr&vMQ2HAU6;C*CuxR6v#PWV7KWr~M9ol>@-**psgUdaTLdNhmivcv0UP9?UW z8j6+EXx4cbvfCEBIoMno!mrxm(z>p%xsUGN zySuB^d8xrmEhKoRo3Z}=TZj32Om4*F7UIQe2exOM@xf2|&jXJF)jQvC_4u_${Mz=- zW;nW&dr>#Eu%orT&?bnwYzHlpNoH1+6wR-xmhE55g95sVs_VAfqJ*#pM@ASp4MVQn7YBXp vnAjt;9#y^*U%JxrB0O9}L+$Zv=$TD_$zqb delta 1189 zcmZWpOH30%7@pbf?zY=4Pzshu5mX`~28<>~4JOu71F@8#fPtV*(;Y|=wq$m()NE~y z9I7EvQjH!waPbvAXuRmf16(+^$;OyXnsCvZNfR#|oq?7|{de}C|MC5gnLoR~-QVk~ z-Z~s2AoPOCpc#IpssRR9I?lDQh@WC)%aUb6khvu=VU>BpZ8E$8I)%?me;~Ad!OS-Z zTeGJu@8O~B56c5PNAj@b{ks8IJ~o|{fkrczXoe|EZ;13^S4#p7u58w&bRfeD51a-` zMzfXU0734_Gql<^>F^D)f7tcOOk>#v4tn~)t%phWKo-6PbpsvhY()c>QfpARRCqYs z!DfVVMV*r?vg~gs$?MFjwT#`r>-@o1Ay~eB3~03klPclb0d7nRTK!&_E>s4M0~sWR z`@*umv&Uf^EVAPu4t0i%&Jw(L_QufFk(=J@{>i~;1jp3als0)Ys>rdpI_X=$7%AEq z!fG_8OpYp1Mb*No*;!1-W!n3q>ildt5e(0vPP@s+qY6gTX0X&_WhzFEf?rvfqr#4PEIhk_ z{EOH_tJ+fj#R!_$NJqGi7&@&cOUtr%Py)A71dN(adX9J)qY$x%6w;t6?Khc@U2WS5 zkxIwVRw7(Y6x>{9Cy1c_A}kFGVok~r+g9sd-!(ce=S8m}dec5)UG9GA${ilbi=&1( zn)Yo>uC8}p!_}R4^caqwBCvAx+bjbMo};gV@6LXLdCwWc zb0%kPEVx^q-CvF5x_a~OONRSWMk+F#t)2i4PfzNT^mQ)p2^gNh#<1ZT$_#Ew&L@G# zftM|Lsl$*ua@%XGP~A`j&_=41SwV6>J@Tl3xj(b-*& Date: Sun, 24 Dec 2023 03:45:38 -0800 Subject: [PATCH 03/13] Run cmd working --- backend/requirements.txt | 3 +- .../apps_interface.cpython-311.pyc | Bin 3046 -> 3589 bytes backend/src/__pycache__/main.cpython-311.pyc | Bin 3953 -> 4053 bytes backend/src/apps_interface.py | 78 +++++----- backend/src/extract_icon.py | 136 ++++++++++++++++++ backend/src/main.py | 4 +- 6 files changed, 186 insertions(+), 35 deletions(-) create mode 100644 backend/src/extract_icon.py diff --git a/backend/requirements.txt b/backend/requirements.txt index 4001ad7..ddd7010 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,5 +3,4 @@ uvicorn pydantic spotipy pyinstaller -pillow -pywin32 \ No newline at end of file +pillow \ No newline at end of file diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index f0eedfb115fe4d9709b0030d9fbaee25e6855275..dbd3805144572a9f3188843055c84f6de34337c4 100644 GIT binary patch literal 3589 zcmbtWVN4^(6`u9(de_F->jR7rNNS)Q_{wn&O`06sor0htkRBkQLP4&O&%1!{)?Rma zeH3J;R&-UZ6eaX@5>}Ha{GvqU$F*vkR%)gEjQY!q_H?V2sF14s$Uh_Qr0P`Z`eu!7 z7ILbh&d$DlJMYcBd2eREH@~Z|cOxjDpU(^9K7{^-e=5as%pCm~m`%hZo)FP|`Af_b z*1u!kVSUMY621H*{|`?6Jf$6E}CRC zm5LF^w(y67mxT9d4ag=^Psnan`E8Kx!dospaod`${37qjpbudVACp-^r7LoTZs8HF zk|B8NGkP2JcPkzu0=$|*DqGP(=v^Z0oG_WWc#@Y^lr^`aMrAeB(Glu+N_5P^%xPs= z5XCTUvMDjDE=qF3bSkTgDkMy|lD?UerI?^7COsvkgrwOpA*o}Dlqe*Gq$=<~l4VH_ zJ7nyt$;6~YBAVnCJe^SJ>E!T0!zWl_eqj`r1Ct4Ef`4EmJ7o5H^&s0$FlrG-jlppmWc3f==D8Fni&Jy^3M zkORN)Y*WTTv8MbNScPx5={5-Y zs)|?gxYV}V)``}N6d{{#b&K+j^Hmj|6qUj| zw;|NK)gwnB6$kyeWRP8IlM!|qKFC=IYy%QrD&rVIKaFMS49zpO`D~pyd=(oA**8ko znQ>;wJ4ilnnN=mXJ&<7lvhbciE&y5H3E2sMX5G~E z`@&*68VUav)?qW%XC}wF7vMHP$rGTtC(Cm8UI;YsM#>tijK*Ekwik^O)8e)6$Kxp&~dR~ zQf|b4U=oB^c^^)64t;eB`ZcC&KM=gXv?<;b_dVYA@r|tR=`uXchNsE!1P@(wv->du zlCOK>L7+Le8MzlJYp&{^_LG_ctQm0U-A}y8W!~H z9GEgZfkV&h9loQ|qM z3yXx)0zNTE|Aba6gAy*JLPMq0ZbDntTTDfUzo*ZX{14kbstPNo^#IELlyyAEQZ*Ub zEoB_`+f`K@Q`?FW<=d_@nw zk{{dm`PWxI$i9~?ysi1Vbze6=AA<*h^AFqlA6(JeuIX*pw7|6ua)UJdO`9$ET7J`Z zzwMF#!k+)a)*a2?r~CU#h^!xc@ZHZZ{^8>9d;i$`sDE^?fAmYI)<3EDPg+8wxwSA{ zm@W+8U)`V%xQ4C9oz-IS3a-rq8aJ>({`;WLn;W-IVc0F#Vb3x21K2B4(u%%W4_E6x zfY$|eH3e`?0({f_5LI`OpA@ONZxGoQY2p@0d*FZ`nK@bM=7c133 zzMSdXY CObgHe literal 3046 zcma)8?Q0uH8lPFMw7Zh5m&SHfC-^pL?407@q+I$XErAf1yOi3=#VKu#4Pn(~{joApRVJG1OLa9-9V$R*IQt#1dSb(MH>TqKZvRg zT*Ncz59c-jJ(V8vx~_QLkG7&zl$No9H?bjo>)VAWdp-Q^!qR@43|T5<^&HFU00tfQKdW=p4x)tqK2g2+_ICyd$hC8`^>ec& zMB||IP}1l5UDLIQr*wRIA%n0CHS@0N*q|QeaZDR0Vm5mSyX%}bzK(|@WTDWrl&wP28Crolu#oYFa2&me7Y7sQxBghz4>Eoq=giJ zP>3zLp~fCWKZ!QO#~Pz!jWfTlCT82YadNz+$U~7Q2*l4^Jn;pz;Liv|>rli{c(`;! z%sd0}dMN-cKFEWgKn!l)|7ERPEUcfoG2 zXA3yz=dhCu{~(Ysayc?Osfdnh1xNC;+)4LwGB8K^l)cc+TQ`1t>+0;x)!SD+ZPv_C zhdImcdT?Iw*DSFM>nG*~J?V#C`ZWg_132b#QltDjq9N`~a`=9y-4##1BU3f?d|f?XRnIro z@$#Q)>Qr5ws&@0fAgDG}Y0cIxVQeRNmoUk7d;?p36t zcEDf{Ht-7~_3+XCn%6~C#DllwhV+8(d-xmpUK+#?jrC)%cti_q`1>NR@RfNk4f1iN z@3uTNHSm#WBQ7V=PAcLr;NPSuf%JVP4_ZRxPYrn<@sjkfr-DE*`Yn44H0S<{SX9_PuybitWdk`h`R7GlVbM@m7?|=BvtSNe3(OXFMUu+JK zK60$*!X0l{C+KZ zwjMp(LjIwP`?1N&S}pc!J@#r<CH(U3%%sEEEB3CG1IewVg z=O9`>jBx|StKHl{(UN!?C{z+p18F7kG|*5dM8_#r$H znF5xd9KazpIe^7n)wYHqm_d`tuS(LTv>+!lIk6;FAvr%cH!&|op(L{;C)G+plVkEk z79XBlTw#ehrKzq(MfpXO@3M4ri33fO2IArylZ#mU4Ov)KKQI7^4)z<|LOr%K+%9k{ z0nuF{nG3QWJH+?M0x2N6DCBuX$nye^=Y!4iY`Tn$;*)*Zouq|9<}d*X1%)CpAXNl% zF_^HOyoxZh9(o3y2Y4`Y}zeupvHKZ zMm3;1_sQLy^^6jexw%s0RY02MKm^DZu=TfC3QIFfiZp=y;K_Yl3P6?XxlD|}iqKTD zWuzwNlw=fX1JyTZlX-=wLk@sdkUIj*eK}P-$3~)kgvn`(iBMZo#02_IB!~g&Q delta 476 zcmcaA|51*2IWI340}yciZ%<{|$g9uH=rh@t`H!r6En^L178^(k1ZtSe7>k*_7@8Ps zm=`clHe?i@Jefs~OQVJ%m_d`#Pm_J}H5Q-AdaT`CVnEf>KwNxu@?q9~IVM)s4-7z} zgZ&1-SWoQ&w+sAQaCBpHGn+0WquAt)>`u}`AWcj_LP4QO6i5~E!wBoid>n});y`W{ zkA6{Uo^En}Zf;^;N|Dg!W)4$EbC8uqoFD>Zh9(o3y2Y4`Y}zeupvHKZMpd9X*U9fW z>lwu-=X0evD1$W1f(Vc;VC!$O6qaU|6sZIGfk0dgl4xLf%EEbpOW`7m;uRLf3ouk9 zK3SOC#0ab(%@DSX)Wn>Uj3O|kRaaPxPV6p3%HEArZ0#H+x_&(FyJfdNjaZC=P{z{mn}DF9apXLA4m diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index b68de1b..77fb6eb 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -2,23 +2,13 @@ import platform import json import os -import winreg - -# import win32com.client from src.cmd_types import * -pywin_shell = None - def start_app(app: str): if platform.system() == "Windows": - subprocess.Popen(f"start {app}", shell=True) - elif platform.system() == "Linux": - raise NotImplementedError - # subprocess.Popen(command.command, shell=True) - elif platform.system() == "Darwin": - raise NotImplementedError - # subprocess.Popen(["open", command.command]) + command = f'start "" "{app}"' + subprocess.Popen(command, shell=True) else: raise NotImplementedError @@ -26,21 +16,12 @@ def start_app(app: str): def get_commands(): commands = [] - progs = get_installed_programs() - - for prog in progs: - commands.append( - Command( - title=prog, - command=lambda name=prog: print(f"Debug msg for prog {name}"), - description="", - ) - ) + commands += get_prog_commands() return commands -def get_installed_programs(): +def get_prog_commands(): # opens start menu and gets all programs if platform.system() == "Windows": global_path = os.path.join( @@ -49,14 +30,50 @@ def get_installed_programs(): "Windows", "Start Menu", ) + local_path = os.path.join( + os.environ["APPDATA"], + "Microsoft", + "Windows", + "Start Menu", + ) print("System Start Menu Contents:") - list_shortcuts_windows(global_path) + shortcuts = list_shortcuts_windows(global_path) + list_shortcuts_windows( + local_path + ) + + # print(shortcuts) + + # Clear out shortcuts with duplicate names + names = [] + unique_shortcuts = [] + for shortcut in shortcuts: + trimmed = shortcut["name"].strip() + + if trimmed in names: + print("WARNING: Duplicate shortcut name found: " + shortcut["name"]) + else: + names.append(trimmed) + unique_shortcuts.append(shortcut) + + cmds = [] + for shortcut in unique_shortcuts: + cmds.append( + Command( + title=f"Run: {shortcut['name']}", + command=lambda path=shortcut["path"]: start_app(path), + description="", + ) + ) + + return cmds else: raise NotImplementedError def list_shortcuts_windows(directory): + out = [] + """ Recursively lists all shortcuts in the given directory and writes them to a log file. """ @@ -65,16 +82,13 @@ def list_shortcuts_windows(directory): full_path = os.path.join(directory, item) if os.path.isdir(full_path): # Recursively search in directories - list_shortcuts_windows(full_path) - elif item.lower().endswith(".lnk"): - # Log the shortcut name and path - print(f"{item}: {full_path}\n") + out = out + list_shortcuts_windows(full_path) + elif item.lower().endswith(".lnk") or item.lower().endswith(".url"): + name = item[:-4] - # if pywin_shell == None: - # pywin_shell = win32com.client.Dispatch("WScript.Shell") + out += [{"name": name, "path": full_path}] - # shortcut = pywin_shell.CreateShortCut(full_path) - # print(shortcut.Targetpath) + return out # Courtesy of https://stackoverflow.com/questions/75040757/how-do-i-list-all-the-installed-applications-using-python, Florian EDEMESSI diff --git a/backend/src/extract_icon.py b/backend/src/extract_icon.py new file mode 100644 index 0000000..3c0378a --- /dev/null +++ b/backend/src/extract_icon.py @@ -0,0 +1,136 @@ +from ctypes import Array, byref, c_char, memset, sizeof +from ctypes import c_int, c_void_p, POINTER +from ctypes.wintypes import * +from enum import Enum +import ctypes + + +BI_RGB = 0 +DIB_RGB_COLORS = 0 + + +class ICONINFO(ctypes.Structure): + _fields_ = [ + ("fIcon", BOOL), + ("xHotspot", DWORD), + ("yHotspot", DWORD), + ("hbmMask", HBITMAP), + ("hbmColor", HBITMAP), + ] + + +class RGBQUAD(ctypes.Structure): + _fields_ = [ + ("rgbBlue", BYTE), + ("rgbGreen", BYTE), + ("rgbRed", BYTE), + ("rgbReserved", BYTE), + ] + + +class BITMAPINFOHEADER(ctypes.Structure): + _fields_ = [ + ("biSize", DWORD), + ("biWidth", LONG), + ("biHeight", LONG), + ("biPlanes", WORD), + ("biBitCount", WORD), + ("biCompression", DWORD), + ("biSizeImage", DWORD), + ("biXPelsPerMeter", LONG), + ("biYPelsPerMeter", LONG), + ("biClrUsed", DWORD), + ("biClrImportant", DWORD), + ] + + +class BITMAPINFO(ctypes.Structure): + _fields_ = [ + ("bmiHeader", BITMAPINFOHEADER), + ("bmiColors", RGBQUAD * 1), + ] + + +shell32 = ctypes.WinDLL("shell32", use_last_error=True) +user32 = ctypes.WinDLL("user32", use_last_error=True) +gdi32 = ctypes.WinDLL("gdi32", use_last_error=True) + +gdi32.CreateCompatibleDC.argtypes = [HDC] +gdi32.CreateCompatibleDC.restype = HDC +gdi32.GetDIBits.argtypes = [HDC, HBITMAP, UINT, UINT, LPVOID, c_void_p, UINT] +gdi32.GetDIBits.restype = c_int +gdi32.DeleteObject.argtypes = [HGDIOBJ] +gdi32.DeleteObject.restype = BOOL +shell32.ExtractIconExW.argtypes = [LPCWSTR, c_int, POINTER(HICON), POINTER(HICON), UINT] +shell32.ExtractIconExW.restype = UINT +user32.GetIconInfo.argtypes = [HICON, POINTER(ICONINFO)] +user32.GetIconInfo.restype = BOOL +user32.DestroyIcon.argtypes = [HICON] +user32.DestroyIcon.restype = BOOL + + +class IconSize(Enum): + SMALL = 1 + LARGE = 2 + + @staticmethod + def to_wh(size: "IconSize") -> tuple[int, int]: + """ + Return the actual (width, height) values for the specified icon size. + """ + size_table = {IconSize.SMALL: (16, 16), IconSize.LARGE: (32, 32)} + return size_table[size] + + +def extract_icon(filename: str, size: IconSize) -> Array[c_char]: + """ + Extract the icon from the specified `filename`, which might be + either an executable or an `.ico` file. + """ + dc: HDC = gdi32.CreateCompatibleDC(0) + if dc == 0: + raise ctypes.WinError() + + hicon: HICON = HICON() + extracted_icons: UINT = shell32.ExtractIconExW( + filename, + 0, + byref(hicon) if size == IconSize.LARGE else None, + byref(hicon) if size == IconSize.SMALL else None, + 1, + ) + if extracted_icons != 1: + raise ctypes.WinError() + + def cleanup() -> None: + if icon_info.hbmColor != 0: + gdi32.DeleteObject(icon_info.hbmColor) + if icon_info.hbmMask != 0: + gdi32.DeleteObject(icon_info.hbmMask) + user32.DestroyIcon(hicon) + + icon_info: ICONINFO = ICONINFO(0, 0, 0, 0, 0) + if not user32.GetIconInfo(hicon, byref(icon_info)): + cleanup() + raise ctypes.WinError() + + w, h = IconSize.to_wh(size) + bmi: BITMAPINFO = BITMAPINFO() + memset(byref(bmi), 0, sizeof(bmi)) + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) + bmi.bmiHeader.biWidth = w + bmi.bmiHeader.biHeight = -h + bmi.bmiHeader.biPlanes = 1 + bmi.bmiHeader.biBitCount = 32 + bmi.bmiHeader.biCompression = BI_RGB + bmi.bmiHeader.biSizeImage = w * h * 4 + bits = ctypes.create_string_buffer(bmi.bmiHeader.biSizeImage) + copied_lines = gdi32.GetDIBits( + dc, icon_info.hbmColor, 0, h, bits, byref(bmi), DIB_RGB_COLORS + ) + if copied_lines == 0: + cleanup() + raise ctypes.WinError() + + cleanup() + return bits diff --git a/backend/src/main.py b/backend/src/main.py index 8af8323..f278b1f 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -36,7 +36,9 @@ def build_commands_list(): # Assert that command titles are unique titles = [] for command in out.commands: - assert command.title not in titles + if command.title in titles: + raise ValueError(f"Duplicate command title: {command.title}") + titles.append(command.title) return out From b7a5a94d06c7792cdc2b6dc08ca8cab6be7c50d2 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Sun, 24 Dec 2023 03:56:39 -0800 Subject: [PATCH 04/13] Icons partway --- .../apps_interface.cpython-311.pyc | Bin 3589 -> 4242 bytes .../__pycache__/extract_icon.cpython-311.pyc | Bin 0 -> 6782 bytes backend/src/apps_interface.py | 16 +++++++++++++++- backend/src/extract_icon.py | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 backend/src/__pycache__/extract_icon.cpython-311.pyc diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index dbd3805144572a9f3188843055c84f6de34337c4..f165c7dc73a5881be61e26f901b1903553a7dbf8 100644 GIT binary patch delta 1271 zcmZWoZ%kWN6u>@u?uTr1Vh1Jts9vfeqOkrDFAEST6^I7I~^lmc8 z3dn4-_pHfOb$^>@SzUPUsTCnp+oxzs+T&C59$TXjx(iy99B(Rn99H7jFnB?0g3;31 zws~Z>>=_VhN5=7-<6w|3LIQj}fm=wIvW{D5g?VwF8 z61(tXnYkJ@-_Nx48%E0T$7f9=8Z#%86aL9qD(N2wTZRhzGwjfZ$*`Zi$KAjY@;ld! zRpR5{>XDxYjM=lxjyC{@Co}OfJN)MPNTe+E&&J|c%}Ma_eW^qun$!)u(gBfNXIx9#JBDjV(FIE^{iQp}Fo8>+ zII<&kxXOZU7_%*D4ifPbRo1vGz|v1hm)!1=*xoIzd3mrnSJDEO7N{Um=mj9-au7QR zkwf#W(tEG{LD#ok_k-UBAB6@Uh6aA(N}-N}F2SZi%QXC^ibM5duLdM)6Sa5ISu|nnZT%A!Q?? zDGC*_z8-QA)RPwv)|l3wyo$F-K`Cm*izgwVH!n_FQxE>({dgbDypM;8g?;8T!_W!W zTKCT&;dFtUL;JIlBOvWtKx*rq$-(En9Qj%g;QI(aT`EINx#Qi3`iRy8)C%Koy|mvA^W-FObR=;{;P&$i z;Nw6B-r@Iv4UKv@I0kmar;Oihf&N>kBKqKqzQDvsbOz@6JQcXNitENf&~VE*O>GH> z%z+-S{qgo-J7W#-&}}2j&HIXFvp=p$sn5}_cSSV!?l=y>ul^tH$?7=*!Uk^ixMv_AmKiD&=- diff --git a/backend/src/__pycache__/extract_icon.cpython-311.pyc b/backend/src/__pycache__/extract_icon.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f889ece0d51345b5b3b48f5fa0547195745bc8cc GIT binary patch literal 6782 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 Array[c_char]: byref(hicon) if size == IconSize.SMALL else None, 1, ) + print(extracted_icons) if extracted_icons != 1: raise ctypes.WinError() From 069045a2b5c8b4c78cf6dda8dda63577f67b82a7 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Sun, 24 Dec 2023 04:09:23 -0800 Subject: [PATCH 05/13] .lnk files being resolved --- backend/requirements.txt | 3 ++- .../apps_interface.cpython-311.pyc | Bin 4242 -> 4987 bytes backend/src/apps_interface.py | 14 +++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index ddd7010..4001ad7 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,4 +3,5 @@ uvicorn pydantic spotipy pyinstaller -pillow \ No newline at end of file +pillow +pywin32 \ No newline at end of file diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index f165c7dc73a5881be61e26f901b1903553a7dbf8..e3e49cc103feb9ba80fe0c92e3d7656b2b4e8a93 100644 GIT binary patch delta 1339 zcmZuvPiWg#7=KTaZ7Kd2M^;j&F-l0b#7-JAXI-gNxx~c259i=XDZnmtH)gYC{W1TILr5knN+Hk)l5~e z;|kmWbsJ(f-VH&lLdf3c2>^xCzj3h+;^zAbS)g@;^y`mYUdUKlw?MQ8XNKEJZwh29 z3H_#!0uZP#iNIkQ)uzDY!PG2M!P&@eYYkNG?b+K}KtaV`a9KBd?WLQk4#kmlH%qE? zYPmgKuC3V_IoJ%bh9THnhkG2TjLg4aBs-hB!o1XdhteT9{&!^>4*`EI`Eme)lyeZg z0W#DY@hN!;tU(AaVJpPNRazkwn?eipHn9lT@m*9xgEzlWhJyBs zNc!WS{r&xQ_lNUwlvt6+=a)oDG9fZa>qd z(`1AM6610jWlg$KBE4*qX&gxnxaT$axiUYexoT|BR&*nJ>(u6{3OiC_M~dABJ5gpQ>VWoztNdt% zkCyo8y^{uiw#=W^=BoW8<^DivtPwa?4jiikf}6PG+u;Ydvqj(6%C?fHszW11?%Q|rOx=c^2aE&QwYYy;@WS>g zZEtU3?^X7?9SnqVd!1~-U(7x}a?)BoQKK`ek$=^kp6ll`UHAzeoDR+m+a3;6Go#GI kafmVGLe|?L#)4T4`VH?#@B{s+ccYJo-a2Rp9qg?Ay!?}Au!xGKFrpe% ztd6c^@_!xyl_H}WrWA%0=2j+2xFxkLXzqH*BjDa86cs_HMxl2E{UTkH8H0ku_Qy2 z?G|TBW>IQ#Nq$k~E#~~vl3Og9C8@c$IMYgVa^gWsz?yGyf^>lS#cV)x6u_W}A7ri? zj0giVioXJh28J7x>jh+_J}_{zTHh5Bo8mR2@}h|56%ox33|ySnKw|P%0Ubt;$&Un# z>REZMH>hma+N8BnXN%5tBlk;2?kCtT8u?!_^8dg9l)ND>J;QlM&P5Nk(TyCL2I3EZIFA!N#c|pPwgv6~b3Rq2G{_%jDufeNG5a=9Do+56L r8##f*Etag}{JbJ15L2MIC|PfEjZgri*W|N8lVpS$`9CmV5@0g`KFp}) diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index 0325780..3ebad77 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -84,6 +84,8 @@ def get_prog_commands(): def list_shortcuts_windows(directory): + import win32com.client + out = [] """ @@ -98,7 +100,17 @@ def list_shortcuts_windows(directory): elif item.lower().endswith(".lnk") or item.lower().endswith(".url"): name = item[:-4] - real_path = os.path.realpath(full_path) + pywin_client = win32com.client.Dispatch("WScript.Shell") + + if item.lower().endswith(".lnk"): + shortcut = pywin_client.CreateShortCut(full_path) + real_path = shortcut.Targetpath + + elif item.lower().endswith(".url"): + with open(full_path, "r") as f: + data = json.load(f) + real_path = data["url"] + real_path = full_path out += [{"name": name, "path": real_path}] From 25e82a8d59cc710ff74c4c6994041f579bd0ae8c Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Tue, 26 Dec 2023 01:45:16 -0800 Subject: [PATCH 06/13] Icons showing for programs --- backend/.gitignore | 1 + .../apps_interface.cpython-311.pyc | Bin 4987 -> 7030 bytes .../__pycache__/extract_icon.cpython-311.pyc | Bin 6782 -> 7003 bytes backend/src/apps_interface.py | 91 +++++++++++++++--- backend/src/extract_icon.py | 11 ++- 5 files changed, 86 insertions(+), 17 deletions(-) diff --git a/backend/.gitignore b/backend/.gitignore index 68eedbc..cab15ae 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -6,3 +6,4 @@ dist *.spec *.egg-info .cache +public/icons/programs/* diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index e3e49cc103feb9ba80fe0c92e3d7656b2b4e8a93..289dd007eae8912ca2c8badbe21f5315b0f39dcd 100644 GIT binary patch delta 3685 zcmb7HeQX>@6`#G```EkP`?k+#pL6HhNprU27$+olV%j8*=Z}7N`*vwSP#| z4@D{6+@Fe#i*H;sQIuJIk(xAnV1ZLjrrR}c80p-&D38W7sZ=a2NH_pfe)W&0T_G=w z;?On<3nLoZU)vb9f!;^`U44h{d2^9|i*k;mtW{x-qp#h|nq^+yYYsgkuvwd87VNLTjWFZb+?ltl&+=8X+;s|}*PvAjCi=GQ%GzXN?Rrtjs&!MxbrZ-! ztCX+8D9dK;GJk&tA;q>`KfGl&>rm{tS7GxULhA47u=-m%!kaf`CU~`huiho_+tmx~ z_TQk!nrGD_UB%aL3JXp(W~r**;=&juxSpp3R&l)UzRrNbjC$D;tocZ>jv#r*rsQ)} z)H8&w>U)-F>X6PP;+eG0OvdE1pD=CWc~Q4aPJKO@h^wOYSgjw#q7)`y8M-iZ5_yFY z>{fqm?HGKLOn`T24!s+_b^M*ZzuG&8F!|U~MmNg|IVoZn{L&qQD8+GNQch&jnAj3^ zV{&G(gRGh|5)P}~wxa_!5zftYLO!d9lbM(>N{kyF!YgwaB4>|84fFk$gj`{xF^hXxNdvnnu7t**;feYOUHvpDg z)n!EU-cn=hO5^_J#{H}Q>V>|mgI5NBUBLEW{wrL#r4Z7qdl zg8|pYEtMXjywwOBa>&-pgpI`94uKKOf<>VO>jhZ24ng_aMqp-XwZR318EOk!ufG$9-YGTW+vQ_#yu;1#n7i|x?NGql>6I?N_p%a*}8;%fY0MN}OY$e?`i8JFkmXafBNv#6oQZrwwVhUbtqtKoFUBw%&rHs2+ZN^V9#E?)w(u;DlBet< zKsXIQOrRY=XQbFUQD=M4$3=szbc=L$N=_tohA50$jQzTKEFDXUxSNO_CO{}nr$mXI zyb5ttIHQd$5)hzCx8YpRp^|gfH`iBkd*}K~R>!xp&t-E@6s;l68Y-g(b645qWV=^= zVa?aNbo#AsZQn_4-%!yvtoerLj}vZbdST$w!2AFNeX#Bo>-V|uaI0?b0(;r9;8^@h ze)@*}X78;N?+igm2MJ9Co%8*`L%ipu{)_#&M3IYVvs|RWMecH*g10Fz7riY@V@2=& z8#6`kkz4zU-V;TxPviOuTwkfWcD`@E@8i3!8ek;0dlSHrg7?sp3;^}UH!6LRJFIbs z3lG$fp_FM*Kza+BwL4#`zgV9;S7di+?2a;Wo4Yp1X_a$c>L)IiIDd)rmAL8>IDO)U zp*4r4%6<<4%(G>;mF-&Pys*pd2Wnh1(}b1)2<16}!2)%L<*OCTyNKl#0Ly0!-h)fW z0ifPETIq{ir^a;_9;o+C@2Z&2RprJDj`{*qFOl=|^0CmdPV`=rV1$tR@o3;0L(6@b6y?f}}d(1G78_Y(oQ&PxjF_^KWe$R1=dQV+*w9u~u|GeP{ zs0TQm+O2x{!BCEZY=wrbjrpc&GbhQK1#{K{8@_ObcbV`(RpJ}fX2xZK?8%zv3~uF} z9*<42O>878SvI`@B42c+hAuuEaJ2%kDSe5IX)%p zGzjQ6LKkC+q!^_!xxXuMZ)75V-32+tz79xR+v3TDn3gx0LAQwK6Os(sMiM$9feoqv zYLdv<-3*#^b22k6;tIL^)V25Xobjab$rzSItaCl&x#N#lelb{#2|6Pr;xZ_a#gt@F z5o9ZCuGXgv!wf3nCTGB=(RFhUlE<(hnyjQQmdX=APfU*ehirwIh6%g~P5MAhx!ddO z%>5LJv&R-^ivFnPkCss_+fS_-sJ`uPW@`9;O8v9DeYOe)H=5pV{#o-Itv_#F>F8VT z==+_e*fFei46h-wS_;%H_AH)S>{*z}F?Rw%0AJwhp(}?jcQ16W_;xS*b}v;IeeIgB zy^Nfm_EKZZwMSPP4=*IbAzRD%S!mra`@2Au41@X3-{(umLj{gh{y?$5Q>*XHohbQhuMS)p$g@R%v*vFu_?z$6h4YqIxgT%lnsR zZZ#EZ`bZ6&EV@tT=zrXqbvWl9t0c1?OfE$uW7U}n336C+cj#^j$022waV#zy=_5Wy zwvntD_mK_sz!n=HT1#WwU~N;Iw22SJ^byP@(rsvX@xeYcN2HDYlh|(-IH2w9&Tqb% z@7tMgX1?FIzwiC4IQ3(vGX+8UC3jSQnl_&5$1jhUyimo2tuG>pm2Kx4iMz_3<0Z>g zbWV`0fGLs<&@SN)6yTrGk5AnBiouv8_i(>OguXy3m^r@-B*W1yYn{Qq-rlZ=q$=q! zagH0LAk<{xL1LjkeUByWY%Tum?g;w~$8_!pdNmTY2uSlKZBdWuGwtgZNytnp zG;dNwJ@yT`$FU*ZVi39wb*)5gnsY;jnQXT(XrZnZjIJFltV3GX#sES^NZA$Tw|*q0 zfCIKo2i`}LUFXSbuFOLI5`V2*LQ!Z3@BkWQ&!Pd`jiQ3NrTtNhE)db1PDGn?kK^C7 z++enE>^zp7SFx1(sp}RT(ruQUvSs=kbfE)jJ2w5hf&=0S#Y!&QE?Kz#F6{y_Tm>YX zKb++OqX1UuC~ueiute9f_TIzUfp1br8{f8?90Z>vU-Lhr~x0oJf+AuTK` zskCpGWHqRS`n6DBBp@mRj4AXs45m+2a>y!wm}HAPvJK(rU{E%#3|rIM{VVje11V%g zIK3wn?28y!Hn?D~q^fj+{A5vR0bSo~2 zzWMaF*?sq#6knL$4Ao@8@%E9tkUlLnvLZPRT%NhU@4 zNbkm+v1Tn7d|NL%Ct0^Muny>;Y)K@7G2oBW;8{IytBnK9(;vo`+OKh?1g9# zQn>+!5-v7?F*=JdJUSQUbzqgwOZ+s`jfcVEC^Lqyy{$8l$b=ILJ*Q!=G8}mW@S+la z^7r%S&xf5ab(rB^*>OP*hYhS4Ea(D^k`3l2lLBtTH4uu_)rsIxT)3}G z3fRf#8Re(wvWBC72v+P|U#yl635whm?xdtQ@Oguiy0k8pX22w?@`3EoHdUj4(o`g^X0yw-SL>zHHN zlQG%wZo~M2i35*3r4K!&GwBPS>bR#mfp~WrY02Cd=V4a@{TIxX{tq27>gE6d diff --git a/backend/src/__pycache__/extract_icon.cpython-311.pyc b/backend/src/__pycache__/extract_icon.cpython-311.pyc index f889ece0d51345b5b3b48f5fa0547195745bc8cc..cdd371cee62543430ab39a6859ad7e2732877920 100644 GIT binary patch delta 2123 zcmZ`)O>7%Q6yCMH>%U#wiIX@<+tkE_)cuoCN~)GNZ5$hCD>n|dX-HaO9Pc=_<=A1o zraw5X5tS$$kmvxa#DQ=LNUcz%Tq-0aIB`ROdT19R_1aqnHzW}6jpOD=>9gnU?DxL+ z=FNLE_J2qI81;OksxATKz*irW_k5pv%0XQ&D~sx!Dhj$V=Yiq12ysET-w<>M5lX&W zV!+$MAtykt7Syi*#SQODo8spb6(G+#biK01yhf>IlJq6drJDY7?L+GuwVyPux-okYR`zg1>f*~ zfiF1N=56u_fybcGUO&6x@*7_PjQ}}@`_(t?09;c%n zdwu-{3kj5lcL)>Of^P}~`wEoyJ;!4Mf9AnE-vyoTChk=$Yzzj-UKn#>y@%}A10<~X z615=feK%}#2XrYdg!|c;b;cPMO-WnKEs(X(t%IV=3qBN;kNsqe$$R)KQ>1Gnc#LQd zssJd{7Mab&C^m^=9+Wc&a29NYL1asm{E4u@uhf-O*$W4h=M6>c@jig z4ChvQ3Ld7EK0BUBm{MXq6_152bR0oft$3AWjof^h)bJ_=gA})JN=7w%>Ecz7makUQ zKsn6cr`fV=|11`0D(s}&vVIL<8b@cPX5@@wevud#t2%_LFO`XDEmjPwA!l2aF*%-L z;+FgTtGxO=Uq_)>CY9VG;hEA1(DKi2JG|hJ4<3)dk4u8^q57eRec}E38oZ8Qrn$YS>F%ZEFep%4OMY zOtj~M09SHd8m@-akPDWELw7b@x~N-M?Qm>iv}9RfZ~M9>1x~4(UG*)?#{j31wSRqz zIDqTWlb}o`O3K6_$@vn=8x-@SC)jZ3L2-a(IuD2y*6dsw;-$fkKCz*XEftT9n^u~; z1kJ?iOoc=ABy;&+&K*a)CwAGOxx9fbj9s4LCFcF1=uWX+rnE${E&Q&WK)@X*+(D}p zs;zGE<0hjAZi=5@dX#~rKyUb zt3a!Z^Yuc3(6E=DMHhYyEqdPcl2*P%&lVx&8qKg!*I>r9Emn16I_HZ1;+H`uv}%4}ANY+Mi0t z17-K^%*y!6J1e8#9=X?j-*!LuW5;jG!~oAYRWUkN;Hw6*f5F>XdMUZWORbQ zi3CTEE*MvqNX@iUQo|X=|0UD*hBlQk<;3*4q!y()fbyn&G9J~Eku!7}1N9=!o|vAP zNoP{@JVF;xy@l#^RQ#4%YX;?&*>)}eah!m<*UMy#Hh_$OFtt1EQQ%7FW1;)kt>3j& c?_c8RF|p(J$Ws9xD;N3W-Yfj^T*b@xKbpF delta 1870 zcmZ`(OKclO7~b{UyM82&9q0X|O`NuZC`}(UggCM5xR^M#)0#L4E6q+qjO#?dH_)Qc z6cN$%f)vdKiRURGl>^8iICA2E#FfHUNIfC-hB$IS{QoS@OYW@Zn}5Ig-!r?9&i#AV z_l4K1NH`w1@33F}yS`E|@6n{Z*CHh(&9N)(^H3pmYDkHduQELeP|rgJEvjwAem_kQ8Wf5_MjPS+Ovj<`~QmXdyPJHM0RN%w%SzLovs)Lu=W#O2KHLTZAOG1FcXWtX9lk;{OtKU3e672fa3BEIE$5%GJ^hTXNJ?5KR$dq!4y z!Q1aw^!*=!B;8l5yWXS)w;uUFXtPM0wmppf0H--*Y14(*!p$ExyyLil97L=6MvvS@ zK41*N@Wfb&=`4Mt$d+^n5Pykp2TseK{F}fjxy1hnteYiJGoN6x*jv*2T4iOGKy%CJd701nAj>}v_FaUlq?JLaco2H_$HWBCcVT$`w-Z*df>e%_pQ{E! z{g&3pHuY}e-Q;%iZt^$PfA=NvK}Ck(I^PN>w%}D%g#x*8XLZya4G}k9)Jr`u z9J$qUER_Oku4tBdUe@W@6kH<(*CMRwx7S(4a00841oT@lY}b-AbA}@|dp(_u!D~c0 zrxUScdh!a)r^#DbaH-j;Ty`!4uMjm)us|?NAg-&mqJt>!*aiQ`HFk4N+bFSd&{3Iw qzm-q;mo2xeBdveS&!3eWJ{ diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index 3ebad77..4210247 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -3,6 +3,9 @@ import json import os import src.extract_icon as extract_icon +import configparser +import shutil +from PIL import Image from src.cmd_types import * @@ -57,24 +60,35 @@ def get_prog_commands(): names.append(trimmed) unique_shortcuts.append(shortcut) + cmds = [] + for shortcut in unique_shortcuts: + print(shortcut) + # Get icon - try: - icon = extract_icon.extract_icon( - shortcut["path"], extract_icon.IconSize.SMALL - ) - print(icon) + icon_path = "" + if shortcut["icon"] != "": + icon_path = shortcut["icon"] + elif shortcut["path"].endswith(".exe"): + icon_path = shortcut["path"] + + dest_path, success = load_icon_from_resource_windows( + icon_path, shortcut["name"] + ) + print(icon_path) - except Exception as e: - print(f"Error extracting icon for {shortcut['name']}: {e}") + remote_icon_path = "" + if success: + remote_icon_path = dest_path.replace( + os.path.join(os.getcwd(), "public"), "" + ) - cmds = [] - for shortcut in unique_shortcuts: cmds.append( Command( title=f"Run: {shortcut['name']}", command=lambda path=shortcut["path"]: start_app(path), description="", + icon=remote_icon_path, ) ) @@ -83,6 +97,47 @@ def get_prog_commands(): raise NotImplementedError +def load_icon_from_resource_windows(path, fname): + success = False + dest_path = "" + if path.endswith(".exe"): + try: + icon = extract_icon.extract_icon(path, extract_icon.IconSize.SMALL) + print(icon) + + # Store icon in public/icons/programs + dest_path = os.path.join( + os.getcwd(), "public", "icons", "programs", fname + ".ico" + ) + icon.save(dest_path) + + print("Saving icon for " + path + " to " + dest_path + ".") + success = True + + except Exception as e: + print(f"Error extracting icon for {path}: {e}") + + elif path.endswith(".ico"): + dest_path = os.path.join( + os.getcwd(), + "public", + "icons", + "programs", + fname + ".ico", + ) + + try: + shutil.copy( + path, + dest_path, + ) + success = True + except Exception as e: + print(f"Error copying icon for {path}: {e}") + + return dest_path, success + + def list_shortcuts_windows(directory): import win32com.client @@ -100,19 +155,25 @@ def list_shortcuts_windows(directory): elif item.lower().endswith(".lnk") or item.lower().endswith(".url"): name = item[:-4] - pywin_client = win32com.client.Dispatch("WScript.Shell") + icon = "" # If empty, means we extract from executable if item.lower().endswith(".lnk"): + pywin_client = win32com.client.Dispatch("WScript.Shell") shortcut = pywin_client.CreateShortCut(full_path) real_path = shortcut.Targetpath elif item.lower().endswith(".url"): - with open(full_path, "r") as f: - data = json.load(f) - real_path = data["url"] - real_path = full_path + config = configparser.ConfigParser() + config.read(full_path) + + data = dict(config.items("InternetShortcut")) + print(data) + real_path = data["url"] + + if "iconfile" in data: + icon = data["iconfile"] - out += [{"name": name, "path": real_path}] + out += [{"name": name, "path": real_path, "icon": icon}] return out diff --git a/backend/src/extract_icon.py b/backend/src/extract_icon.py index 0253daf..1a37917 100644 --- a/backend/src/extract_icon.py +++ b/backend/src/extract_icon.py @@ -6,6 +6,8 @@ from enum import Enum import ctypes +from PIL import Image + BI_RGB = 0 DIB_RGB_COLORS = 0 @@ -84,7 +86,7 @@ def to_wh(size: "IconSize") -> tuple[int, int]: return size_table[size] -def extract_icon(filename: str, size: IconSize) -> Array[c_char]: +def extract_icon(filename: str, size: IconSize) -> Image: """ Extract the icon from the specified `filename`, which might be either an executable or an `.ico` file. @@ -135,5 +137,10 @@ def cleanup() -> None: cleanup() raise ctypes.WinError() + # My code + mode = "RGBA" + bit_count = bmi.bmiHeader.biBitCount + image = Image.frombuffer(mode, (w, h), bits, "raw", mode, 0, 1) + cleanup() - return bits + return image From 9f389ae074dd0ee66b51d7d866e8210ddede3166 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Tue, 26 Dec 2023 01:52:45 -0800 Subject: [PATCH 07/13] Mysterious pywin32 bug w/ COM errors --- .../apps_interface.cpython-311.pyc | Bin 7030 -> 7032 bytes backend/src/apps_interface.py | 32 +++--------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index 289dd007eae8912ca2c8badbe21f5315b0f39dcd..6e5c25fb03b99b8c99a8ac883f8946a60d6cd435 100644 GIT binary patch delta 136 zcmexn_QQ;CIWI340}v!G>PjsW*~llyq|6KC7JCAT28O%r{4Mnt*(I*9OU&@R#IAaQ zUG)P8gRt6UU#2~b5}Uc0=dm)zZeGbz$H=I#S%a&YjWJ{M8i8hZ#)+E^B=#~fKA8MY XN?w|SQE7tl2L>R8PKZs`koE%r6(S|F delta 134 zcmexi_RWlMIWI340}xm(=t|usxsgwfNs$N0Ep`VI4GeeL`CIBQvP)cHmzd#siCy&q zyXp-g)ye)$dl*GF^Dxh2WsKasnxl@9QD(CiS2G)9%I0+f&FqZ5n~fy)GBMtm{6R`y Vnw3#$g7F6iAcal{OxBY20|4_fC5iw5 diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index 4210247..93645b6 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -5,12 +5,16 @@ import src.extract_icon as extract_icon import configparser import shutil + from PIL import Image from src.cmd_types import * def start_app(app: str): if platform.system() == "Windows": + # import pythoncom + # pythoncom.CoInitialize() + command = f'start "" "{app}"' subprocess.Popen(command, shell=True) else: @@ -176,31 +180,3 @@ def list_shortcuts_windows(directory): out += [{"name": name, "path": real_path, "icon": icon}] return out - - -# Courtesy of https://stackoverflow.com/questions/75040757/how-do-i-list-all-the-installed-applications-using-python, Florian EDEMESSI -# def get_installed_programs(): -# reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) -# key = winreg.OpenKey(reg, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") - -# for i in range(winreg.QueryInfoKey(key)[0]): -# software_key_name = winreg.EnumKey(key, i) -# software_key = winreg.OpenKey(key, software_key_name) -# try: -# software_name = winreg.QueryValueEx(software_key, "DisplayName")[0] -# try: -# # Try to get the executable path (DisplayIcon) -# software_path = winreg.QueryValueEx(software_key, "DisplayIcon")[0] -# except FileNotFoundError: -# try: -# # If DisplayIcon not found, try InstallLocation -# software_path = winreg.QueryValueEx( -# software_key, "InstallLocation" -# )[0] -# except FileNotFoundError: -# # If both are not found, set as not available -# software_path = "Path not available" - -# print(f"{software_name} - Path: {software_path}") -# except Exception as e: -# print(f"Error retrieving information for software index {i}: {e}") From 07b62482362b14e4b869b3e3abc186b33c17af79 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Tue, 26 Dec 2023 02:00:20 -0800 Subject: [PATCH 08/13] Colors no longer flipped --- .../apps_interface.cpython-311.pyc | Bin 7032 -> 7144 bytes .../__pycache__/extract_icon.cpython-311.pyc | Bin 7003 -> 7407 bytes backend/src/apps_interface.py | 5 +++-- backend/src/extract_icon.py | 11 ++++++++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index 6e5c25fb03b99b8c99a8ac883f8946a60d6cd435..6d995e16767efef70f367835b4db559fc4ae6346 100644 GIT binary patch delta 833 zcmZ8fOK1~87~a`UvYU3(rZLT{kZxNdrY%h$(pu17+DklCyx4SNWli#Oe&tkDQ zMasFyw=ugL`=Gv1_`Ei1(n(vj6-Qk|tB4;YQe1>O;z+TzRpo(VhaPHcDvRng}y*s+gtLfDp_7ZxnW%x4X4T$Jz5tLf5m6GE~1SUcG+`l zUR{{8UfCs#R2^~+^E;@R(`Z=QQgAJFd8J-;ZC{f+i+@Cw*&}illfRE8Y$#v#&Kv7H zdS*}0+%NCyg>Aj?#mHCh3-gY6B5*3PU>expYmR3=q3NK9U9vn60J;MM8wFZv|29t6J^QMIXQ;QWp`8l8SR5g zi-@2n>s+Lwhl+?GUUDk8t~qW(3{ee^KXa;f7tJz`SyQ&^Uv%|{h7?X%w*a% zW$xAkdTS%|Szpn_dt+h*N}j%|J<&Ylsd1Zd6Jwe-Z?smc#7n$3g-d$QrCOY(-62l$ zqO`8~kyl@2EA5QCz{{0Nxw1zpmmMNaHb;ZNF=c8YaQSQ)_YLQVNn^CZ?{j{=o*S=cz56gZ$^UWIkVh!%5w0%S3gZ{$KsSugI zL3y=R1`3L@#i}txx*N}p!)9Mz5e@+?tK5Kcj3_8WPVA(PPF+VLtzcby=X^Z7J^prl zU4w2#I0iHvFxhbc*|=~?M1{ife~o04F8PczX$8u8KE$1+=`&o9s5EH? z^+gIWDz*zQaYWHRvOg{tD@ZRaFZxln7Wg#cFeRux);v6>#`znKE&Dq;8PBi_aJGPt zPbTJ>8XXFeIR>>cy2Qb)@v2`_^cf7hif~@M>8niOeZ`mA9BO<>?C`32Iq}i=Qqy7G eyBW~a5I!od6;q`0@Yt@V-2c!kHj5V$sOT@>QMfk% diff --git a/backend/src/__pycache__/extract_icon.cpython-311.pyc b/backend/src/__pycache__/extract_icon.cpython-311.pyc index cdd371cee62543430ab39a6859ad7e2732877920..d5bee32d5a73819941eb4b5581ce4b9feb5601de 100644 GIT binary patch delta 724 zcmca@_TG|jIWI340}zNT=}Kh~+Q`?*%^0$I2KN+3M()kJymri7Eey3>Obj(#H5`-s z`Nhqd7#LOqF$6F&q;S@9mpH@t3=COHFm?@h4QmZs8dD8N3ReyH z3oJ$txCI+rCcBASFvd-;7Y|}f0h&^jKKZ)1J!9--aS1JlG>}c~9_}ul{!U&+0U&7! zu#iuHb9it_P*DO%C=o;?gNQ&7VGks3F&CE<6%|cxl#u0W0P;a`Uc6=V3W-`KdnZOl X!5PLM7(n!b3<$l!6hh-C!C?XbY#67W delta 291 zcmaEFdE1O{IWI340}yD>?@Fx}*vQw(&1kiG2KN+3My}1eymrhIwOlpqSt1}MAdn>q zp&1w^&lQm5<*H$=VM}AG;Yi`E;hKC{K$nqw^GAUerp;$Xk{JbWvE=5bq~798$}EXb z&M(a?nQSd)$(08*Argp-6F1k1sW1t!G4KdAxZDsBp2B-YK%>EP@mNBl0j_o zKxIWqlanOv86zeylGI{MoP1VNK|UHJ90MZaKtv#jum=*in2Sq_it;A&NXc^50QrnS jT)cX-u~aRSnI)s(4C4 None: raise ctypes.WinError() # My code + # Map from BGRA -> RGBA + # Function to swap red and blue bytes in BGRA to get RGBA + def swap_red_blue(bgra_buffer, size): + for i in range(0, size, 4): + # Swap the red and blue bytes (first and third bytes) + bgra_buffer[i], bgra_buffer[i + 2] = bgra_buffer[i + 2], bgra_buffer[i] + + # Call the function to modify the bits buffer + swap_red_blue(bits, bmi.bmiHeader.biSizeImage) + mode = "RGBA" - bit_count = bmi.bmiHeader.biBitCount image = Image.frombuffer(mode, (w, h), bits, "raw", mode, 0, 1) cleanup() From c5774b45566028a5988889287d721efde8306114 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Tue, 26 Dec 2023 02:01:09 -0800 Subject: [PATCH 09/13] Increased icon fidelity --- .../apps_interface.cpython-311.pyc | Bin 7144 -> 7144 bytes backend/src/apps_interface.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index 6d995e16767efef70f367835b4db559fc4ae6346..3c770131ba452d414dce40a94ee6489c34d84592 100644 GIT binary patch delta 30 kcmaE1{=%GVIWI340}%Kv*~m3VfYrw_$lZ1GY5`kr0E)y2lK=n! delta 30 kcmaE1{=%GVIWI340}zBQ-pDmafHm0H(Z^@=Y5`kr0E@E-r~m)} diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index d1d9beb..262525b 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -107,7 +107,7 @@ def load_icon_from_resource_windows(path, fname): dest_path = "" if path.endswith(".exe"): try: - icon = extract_icon.extract_icon(path, extract_icon.IconSize.SMALL) + icon = extract_icon.extract_icon(path, extract_icon.IconSize.LARGE) print(icon) # Store icon in public/icons/programs From b5806004e04335743997a1237bd655ec2ac5153a Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Tue, 26 Dec 2023 02:12:14 -0800 Subject: [PATCH 10/13] Windows icon --- backend/public/attributions.txt | 1 + backend/public/icons/default_prog_windows.svg | 1 + .../apps_interface.cpython-311.pyc | Bin 7144 -> 7149 bytes backend/src/apps_interface.py | 6 +----- 4 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 backend/public/attributions.txt create mode 100644 backend/public/icons/default_prog_windows.svg diff --git a/backend/public/attributions.txt b/backend/public/attributions.txt new file mode 100644 index 0000000..1380cf5 --- /dev/null +++ b/backend/public/attributions.txt @@ -0,0 +1 @@ +default_prog_windows.svg: https://iconoir.com/ (Windows Icon) \ No newline at end of file diff --git a/backend/public/icons/default_prog_windows.svg b/backend/public/icons/default_prog_windows.svg new file mode 100644 index 0000000..e96d626 --- /dev/null +++ b/backend/public/icons/default_prog_windows.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index 3c770131ba452d414dce40a94ee6489c34d84592..9d5aa9f8aabd569991cd014a9c5ec2c20f56b874 100644 GIT binary patch delta 889 zcmZ8fT}TvB6rQ`Y^Rw>G{yDmXtGnx%!i2VBkP#(>{rV78S`fst?9A%M?#yy$Op+BP zq@q8zZbaCR;DZ)P1p5$yMnMlf_@L!O4D=}YA}Bo+K6URb>q7_bcg{WMo^$T`?%nL& z>J=6Qp^m~bH-A?fc_e&ujZ^3o-Mt^iR9A_5PO0=udIDGsIa8Hubj>OO)xF4|G-x=* z*L)Op6DaICo4cITRb$K?uQRPQv=XwMkavMRr~sQWN3?uiVQjvbPg_|77HrnMVOm-N z*+3CpkTkQoj8Q10b=7p?9cpo<+Ctb6@G?(hn4*4hf$7OLPFm-rXJ@~2?W zkb*dD`-U|uRVx7_*wuhabVoFdtqfj!;=vY~=ywq<(>=5cO6V`!Rf)TQ0KN>aiW7__ zpcSSWiR@P#=@eB#^{at-obz^%31qVd2%-V*BnY80*X9XN1JzUVF4m(ZE+&=1{l*e^ zk+NcYZ3#^x+UB0@+XYFR$z=@P9G6rrn;y+usUkFnQ)5*I517}7(KJ8MB;sTRKtcmn zajZ;$ApwJElke%hLL5E}Q`EBZ`NYTMQgVudWM0K>Ggi*ZYcPO!wpZ2649pd+oT0-Q zI`0ud9A!PXfFEsnUWtT0*ieHOY?#@~Y+WY+J7~pwyKy7l`eJOhG*f!r|30#MWNqQqmwXSz9f}kD+hHmw-tlhuTUYs37_R*BiF7?F0}}+=2*?;vBKQF|7@s%e?98}3v%5B{;~ML%qG*VJR?rS5NOe%diwD6_S$4*n*qxC#6K1Sv zA{Am_eGx(Q2NJCCw$}~@9qP84Pz(ij2)YD&D1r{rdt-a)W9Ik0_x-+q@B6+FejAjx zWx19iGQPELdUxcv>`MmDbG-^4Fj#}hvrDLmo^o)#UWJ~7asn|1S7aVCM$IE`iS;BJ zdL)Z!;Y{7wk^P$Ctn+8AaPbqeFYFlpI_v*2QuFffafU4k#p=d<3NgA0DhU35LO z;7jw1-X+J*IR!s*#u>3K&&uYlYbI=<&LoFvB}`NM0&yv1ZNufrO^0`AcK8@M$>euU z0xQgCA-cA_Cuj$P_F!aRXe$eCA4RQP{SL+W2QjrTruIbrK-9~keh6diwpK@i{m4br zo2^#Bjl|RlwOl(=p^s}s-7v;wID~&aTjAKy^p7RplVOTK4qN;i){x+N$U!n_!a1bC zjL<4Y*N`Clj*WH7$1vS54pWwOT%3j}=?03y7wNG| zUx^#45LB5cL2Va871#~mHXIF7c2SDL-O{%99*M!s z;#h&zWPA85B`;7Ifl{)imySEw^U+X(c4+Ji2Gh8p$=~9sf@56Cn?u+EzG=BhLM@y9 Q6^5w)L4zkvqct@0FW%eFM*si- diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index 262525b..78194e6 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -12,10 +12,6 @@ def start_app(app: str): if platform.system() == "Windows": - import pythoncom - - pythoncom.CoInitialize() - command = f'start "" "{app}"' subprocess.Popen(command, shell=True) else: @@ -82,7 +78,7 @@ def get_prog_commands(): ) print(icon_path) - remote_icon_path = "" + remote_icon_path = os.path.join("/", "icons", "default_prog_windows.svg") if success: remote_icon_path = dest_path.replace( os.path.join(os.getcwd(), "public"), "" From 34ec7819a73152605fba28c288e22daeb62b80a9 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Wed, 27 Dec 2023 20:16:29 -0800 Subject: [PATCH 11/13] Apps working on windows release --- .../apps_interface.cpython-311.pyc | Bin 7149 -> 7321 bytes backend/src/apps_interface.py | 19 +++++-- backend/uvicorn.log | 48 ----------------- frontend/src/main.js | 50 +++++++++++++----- 4 files changed, 50 insertions(+), 67 deletions(-) delete mode 100644 backend/uvicorn.log diff --git a/backend/src/__pycache__/apps_interface.cpython-311.pyc b/backend/src/__pycache__/apps_interface.cpython-311.pyc index 9d5aa9f8aabd569991cd014a9c5ec2c20f56b874..9b426ee93e1113e55c5feda58a359600737da220 100644 GIT binary patch delta 734 zcmX|;%WD%s9LHypNldocWFpNw#60NBYKjR}OD!rbT2Z|Cf`EpQ-Nuc{Zk%1{1BD0OfE4~4tS`y}v%<_5Gdq=f+~beDfZPhA(`M|e;AMucXHkuQ~rYM7vu@M2=Wtc4AulP@6DFz}S*WZ$Q9-0Ne02l|1@jQKk zOMHtKB1I6U0eQd~z%2hv$1Z|zdsg&XTV+>4z5;-@HdalqcxN4B6!h{Dw04%4EUp3o7vM@maempaIXugIq>5Cwipfi?r1o4LLTA6{CUHY z&Z?YIOX7eX zX7HQw7#6W>&Q|A1xIpL-x`Zp}nCC3YQ)M%BhMu@V@*1IyuS`q3Od-KbzMGXIwD6a8 zwbUdvE7QhG&00Avn-;xYKZ>JtB9IIBMRrfB_z}g_oKy)$JDw1WxKVRp9>3O>>RGuq zZI-v6?D%2d4SX+*#VRWGWsO{!^?7ouehe$7D3d~;mln37QJB3dH6r0&@^)k4xN+Zi yrF@%?*@U~e-k8feoHZ+ZkXXRO#u+V?`yglZ8$UN5pwYZOrNH!Gb_xUS=FR`CA%(#J diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index 78194e6..4492c46 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -5,6 +5,7 @@ import src.extract_icon as extract_icon import configparser import shutil +import pythoncom from PIL import Image from src.cmd_types import * @@ -19,10 +20,12 @@ def start_app(app: str): def get_commands(): - commands = [] + pythoncom.CoInitialize() + commands = [] commands += get_prog_commands() + pythoncom.CoUninitialize() return commands @@ -64,7 +67,7 @@ def get_prog_commands(): cmds = [] for shortcut in unique_shortcuts: - print(shortcut) + # simple_print(shortcut) # Get icon icon_path = "" @@ -76,7 +79,7 @@ def get_prog_commands(): dest_path, success = load_icon_from_resource_windows( icon_path, shortcut["name"] ) - print(icon_path) + # simple_print(icon_path) remote_icon_path = os.path.join("/", "icons", "default_prog_windows.svg") if success: @@ -154,8 +157,6 @@ def list_shortcuts_windows(directory): # Recursively search in directories out = out + list_shortcuts_windows(full_path) elif item.lower().endswith(".lnk") or item.lower().endswith(".url"): - name = item[:-4] - icon = "" # If empty, means we extract from executable if item.lower().endswith(".lnk"): @@ -174,6 +175,14 @@ def list_shortcuts_windows(directory): if "iconfile" in data: icon = data["iconfile"] + name = item[:-4].encode("utf-8", "replace").decode("utf-8") + out += [{"name": name, "path": real_path, "icon": icon}] return out + + +def simple_print(str: str): + # Sanitizes to ascii first + str = str.encode("ascii", "ignore").decode("ascii") + print(str) diff --git a/backend/uvicorn.log b/backend/uvicorn.log deleted file mode 100644 index be6b3f1..0000000 --- a/backend/uvicorn.log +++ /dev/null @@ -1,48 +0,0 @@ -2023-12-16 18:57:48,126 - INFO - Starting server on port 8000 -2023-12-16 18:57:48,134 - DEBUG - Using proactor: IocpProactor -2023-12-16 18:59:27,491 - INFO - Starting server on port 8001 -2023-12-16 18:59:27,495 - DEBUG - Using proactor: IocpProactor -2023-12-16 19:01:04,537 - INFO - Starting server on port 8002 -2023-12-16 19:01:04,541 - DEBUG - Using proactor: IocpProactor -2023-12-16 19:02:17,176 - INFO - Starting server on port 8003 -2023-12-16 19:02:17,180 - DEBUG - Using proactor: IocpProactor -2023-12-16 19:07:26,670 - INFO - Starting server on port 8004 -2023-12-16 19:07:26,679 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:04:06,858 - INFO - Starting server on port 8005 -2023-12-17 01:04:06,858 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:05:35,106 - INFO - Starting server on port 8006 -2023-12-17 01:05:35,113 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:11:51,107 - INFO - Starting server on port 8007 -2023-12-17 01:12:50,500 - INFO - Starting server on port 8007 -2023-12-17 01:12:51,000 - DEBUG - Using selector: SelectSelector -2023-12-17 01:19:14,458 - INFO - Starting server on port 8008 -2023-12-17 01:19:14,878 - DEBUG - Using selector: SelectSelector -2023-12-17 01:20:52,850 - INFO - Starting server on port 8009 -2023-12-17 01:20:53,233 - DEBUG - Using selector: SelectSelector -2023-12-17 01:27:24,874 - INFO - Starting server on port 8000 -2023-12-17 01:27:25,266 - DEBUG - Using selector: SelectSelector -2023-12-17 01:36:53,794 - INFO - Starting server on port 8001 -2023-12-17 01:36:54,202 - DEBUG - Using selector: SelectSelector -2023-12-17 01:41:31,775 - INFO - Starting server on port 8002 -2023-12-17 01:41:32,222 - DEBUG - Using selector: SelectSelector -2023-12-17 01:44:37,284 - INFO - Starting server on port 8003 -2023-12-17 01:44:37,292 - DEBUG - Using selector: SelectSelector -2023-12-17 01:45:34,851 - INFO - Starting server on port 8003 -2023-12-17 01:45:34,859 - DEBUG - Using selector: SelectSelector -2023-12-17 01:48:38,036 - INFO - Starting server on port 8003 -2023-12-17 01:48:38,043 - DEBUG - Using selector: SelectSelector -2023-12-17 01:52:06,128 - INFO - Starting server on port 0 -2023-12-17 01:52:06,134 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:52:34,098 - INFO - Starting server on port 0 -2023-12-17 01:52:34,103 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:53:25,391 - INFO - Starting server on port 0 -2023-12-17 01:53:25,397 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:54:59,647 - INFO - Starting server on port 0 -2023-12-17 01:55:08,636 - INFO - Starting server on port 0 -2023-12-17 01:55:08,641 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:55:15,396 - INFO - Starting server on port 0 -2023-12-17 01:55:15,400 - DEBUG - Using proactor: IocpProactor -2023-12-17 01:55:40,485 - INFO - Starting server on port 8003 -2023-12-17 01:55:40,490 - DEBUG - Using selector: SelectSelector -2023-12-17 01:56:45,982 - INFO - Starting server on port 8003 -2023-12-17 01:56:45,988 - DEBUG - Using selector: SelectSelector diff --git a/frontend/src/main.js b/frontend/src/main.js index c25ecb4..ee1a96e 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -79,7 +79,12 @@ function tryStartBackend(win) { let portUsed = 8000; - if (app.isPackaged) { + let packaged = app.isPackaged; + packaged = true; // Force packaged mode + + if (packaged) { + + log.info ("Running in packaged mode, using portfinder to find open port"); portfinder.getPort(async (err, port) => { if (err) { @@ -92,30 +97,47 @@ function tryStartBackend(win) { let backendPath = ""; let root = path.join(app.getAppPath(), "..", ".."); - backendPath = path.join(root, "backend", "backend_0p1.exe"); // Use double backslashes for Windows paths + backendPath = path.join("backend_0p1.exe"); // Use double backslashes for Windows paths + + // If in a devmode test, use the devmode backend + if (!app.isPackaged) { + root = path.join(app.getAppPath(), ".."); + backendPath = path.join("dist", "backend_0p1", "backend_0p1.exe"); // Use double backslashes for Windows paths + } // Use execFile // Define the backend executable and the arguments - const backendExecutable = backendPath; // Make sure this is just the executable name or path const args = ['--port', portUsed.toString()]; + // const command = backendPath + " " + args.join(" "); // Define the options, including the working directory const options = { cwd: path.join(root, "backend") }; + + log.info(`Backend path: ${backendPath}`) + log.info(`Backend port: ${portUsed}`) + // Use execFile to run the backend executable - backendProcess = execFile(backendExecutable, args, options, (error, stdout, stderr) => { - if (error) { - log.error(`execFile error: ${error}`); - return; - } - log.info(`stdout: ${stdout}`); - if (stderr) { - log.error(`stderr: ${stderr}`); - } + // Use spawn to run the backend executable + const backendProcess = spawn(backendPath, args, options); + + // Handle the stdout, stderr, and error events + backendProcess.stdout.on('data', (data) => { + log.info(`stdout: ${data}`); + }); + + backendProcess.stderr.on('data', (data) => { + log.error(`stderr: ${data}`); + }); + + backendProcess.on('error', (error) => { + log.error(`spawn error: ${error}`); + }); + + backendProcess.on('close', (code) => { + log.info(`child process exited with code ${code}`); }); - log.info(`Backend path: ${backendPath}`) - log.info(`Backend port: ${portUsed}`) log.info(`Backend process PID: ${backendProcess.pid}`) }) From 1db21965e9b5f8c6c8d3c68b51271dd73df6ff66 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Wed, 27 Dec 2023 20:19:44 -0800 Subject: [PATCH 12/13] Cleanup --- backend/src/apps_interface.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/backend/src/apps_interface.py b/backend/src/apps_interface.py index 4492c46..3f7ee68 100644 --- a/backend/src/apps_interface.py +++ b/backend/src/apps_interface.py @@ -180,9 +180,3 @@ def list_shortcuts_windows(directory): out += [{"name": name, "path": real_path, "icon": icon}] return out - - -def simple_print(str: str): - # Sanitizes to ascii first - str = str.encode("ascii", "ignore").decode("ascii") - print(str) From 2e9ae5696b62b915e601c32c3ba463a016a13d73 Mon Sep 17 00:00:00 2001 From: D0rkKnight Date: Wed, 27 Dec 2023 20:25:30 -0800 Subject: [PATCH 13/13] Removed build.sh file --- build.sh | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 build.sh diff --git a/build.sh b/build.sh deleted file mode 100644 index 8a1067c..0000000 --- a/build.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cd backend -source .venv/Scripts/activate -make onefolder -cp -r dist/backend* ../frontend/dist/backend - -cd .. -cd frontend -npm run build - -cd .. -rm -rf dist -mkdir dist -cp -r backend/dist/backend* dist/backend -cp -r frontend/dist/Palette*.exe dist/Palette.exe