From c8bbbebb48f922b771cd7fc2b5552fd4b0d92486 Mon Sep 17 00:00:00 2001 From: ske087 Date: Thu, 31 Jul 2025 16:37:54 +0300 Subject: [PATCH] updated solution --- .env.example | 21 +++++ .gitignore | 1 + __pycache__/app.cpython-311.pyc | Bin 37825 -> 43623 bytes __pycache__/models.cpython-311.pyc | Bin 6351 -> 6419 bytes app.py | 31 +++++-- clear_db.py | 14 ---- create_default_user.py | 20 ----- instance/dashboard.db | Bin 61440 -> 40960 bytes models.py | 77 ------------------ models/__init__.py | 5 ++ models/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 450 bytes models/__pycache__/content.cpython-311.pyc | Bin 0 -> 1020 bytes .../create_default_user.cpython-311.pyc | Bin 0 -> 1359 bytes models/__pycache__/group.cpython-311.pyc | Bin 0 -> 1448 bytes models/__pycache__/player.cpython-311.pyc | Bin 0 -> 1976 bytes models/__pycache__/server_log.cpython-311.pyc | Bin 0 -> 1058 bytes models/__pycache__/user.cpython-311.pyc | Bin 0 -> 2471 bytes models/clear_db.py | 21 +++++ models/content.py | 8 ++ models/create_default_user.py | 18 ++++ models/group.py | 13 +++ init_db.py => models/init_db.py | 0 models/player.py | 18 ++++ models/server_log.py | 10 +++ models/user.py | 33 ++++++++ templates/add_player.html | 11 +++ test_api.py | 60 -------------- .../group_player_management.cpython-311.pyc | Bin 8126 -> 16047 bytes utils/__pycache__/logger.cpython-311.pyc | Bin 5018 -> 6714 bytes utils/__pycache__/uploads.cpython-311.pyc | Bin 17468 -> 18302 bytes utils/group_player_management.py | 28 ++++--- 31 files changed, 199 insertions(+), 190 deletions(-) create mode 100644 .env.example delete mode 100644 clear_db.py delete mode 100644 create_default_user.py delete mode 100644 models.py create mode 100644 models/__init__.py create mode 100644 models/__pycache__/__init__.cpython-311.pyc create mode 100644 models/__pycache__/content.cpython-311.pyc create mode 100644 models/__pycache__/create_default_user.cpython-311.pyc create mode 100644 models/__pycache__/group.cpython-311.pyc create mode 100644 models/__pycache__/player.cpython-311.pyc create mode 100644 models/__pycache__/server_log.cpython-311.pyc create mode 100644 models/__pycache__/user.cpython-311.pyc create mode 100644 models/clear_db.py create mode 100644 models/content.py create mode 100644 models/create_default_user.py create mode 100644 models/group.py rename init_db.py => models/init_db.py (100%) create mode 100644 models/player.py create mode 100644 models/server_log.py create mode 100644 models/user.py delete mode 100644 test_api.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..703e8a4 --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +# .env - Flask environment variables + +# Flask secret key (change this to something secure in production) +SECRET_KEY=Ana_Are_Multe_Mere-Si_Nu_Are_Pere + +# Flask environment: development or production +FLASK_ENV=development + +# Database location (optional, defaults to instance/dashboard.db) +# SQLALCHEMY_DATABASE_URI=sqlite:///instance/dashboard.db + +# Default admin user credentials (used for auto-creation) +DEFAULT_USER=admin +DEFAULT_PASSWORD=1234 + +# Flask server settings +HOST=0.0.0.0 +PORT=5000 + +# Maximum upload size (in bytes, 2GB) +MAX_CONTENT_LENGTH=2147483648 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1e4e193..fd38011 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ digiscreen/ +.env diff --git a/__pycache__/app.cpython-311.pyc b/__pycache__/app.cpython-311.pyc index 20f006d525609da87f421226d7ba1095b5e97635..b0108ecf0e68eec5c8714cea0ac93d374da369a6 100644 GIT binary patch delta 15356 zcmb_@3v^V+mEe2buRl_&yCtFjj}{0?4f=n;2#`P@1QL?WFF^BYT6#}Xi*8BOYa!H@ z+%i@Iwy}*lv2v1i@W41^aVE^{eDLIq*OTL9y4jwfeP_Jm#AdUZoy^EPj+0I1 z?Cibu`YVK!%BwYhy=RR#%aP^Hc4T{V962n{;K}voIr3QA=*jmMI0|@9!$}3N11C44Y;mkS z#Z%}lauj)s9f4wRiKE23*0I)WbJ)D4j#7q@>RIPq?^y3GbCh{EI5v389pxTPqhd3OdJJ6T1uXP39#(eCYVba*=*o!%}-mv^^gH7W?T_)mfH= zJZ^E|;jG6oD^uG4IuEVGKlKr_=#`B{O^ySMiG$H>=>RaAoy-QG*leVU03hZhqd5+V zxzeFmxK}kveCHwGVt!Iy0m~~)$}7UW70mRE9gZ@#f~^4LGbQ21ehu$HGHdK}Q8H3b zvKSbR>|ax!^Kki|C_xzKV#g8bNK#))SpU{Kj!H+9@@<%(G+xJWa5|+U$y~{qD+F<5 zMfX|nA0`x#j{X`#M^_^xmP$EJN3=)Kv{~wrvQ~`laVcl)L=;C{x4Gyq*D??F~;@Z{8N*kn8VjaDrtJaB8-0b?5t6vc7MPVitF;PMyhqH^Xg?;dXxp?jDA_ zC5l_P_mLd*FwF57X73|0_c6?g7-ruiG5Zd{5krSqN0sFH(s@#lP;BU zq|EUQRD4#PPDW=Feb(4>G-Do+o#KQ~8b4>(E5cz}B8vWTk81{gb=_oQYEm(D zOpHUhPf0l_k#iE+H!-55jbM>;k|ENA))rn*U$Aa2SJFKbBhD$H;uIy1y&JlC_3F<*9dJ+AFKdA>>o!+6Y^68S_thyz_qgnHjSop6AER{M zE&H4kXZ&fROCBAZa1pUa9Q3EkXFYD8)KXhpOEWT#(Q_Gp&A&l+W@ho(^l;{0-EJ6= z&)xoN<~p7)q3^7z<-6#oYdZMjv?Z%+OE(k}3jn)@3}E_k05Xp1X&dQ+AI0Jlz3y=* zk;96j9pdQS-mKf{*@HaaO68mbnK@WbcXmo5Gm6&b@sM14 zKgV9N7P5(e0HakIQ#N>5GXz%&3=GnW+|TpfxBoErpLkxR*YYdUN3d)Z0j?HE8~rc& zebBkmf-1dQL=P5JW-)G!L>d7pNra$}KSN(EcoS%~7P>UKBG!$Ovh!-Xm;O<4C-D8N z;`Ve8Vt5f`BREd`O6mfv-9r4))5XmU9lW70c8lGrWinlU_Pn-e*!Y zZc%0a68*;w`JglU@<#qTZ7Hwjy8)wr(=%9E7nRZCp!88qR%GV#m04)k+TS5nhxsKi5aN8ig8?;c9IE?l*EE!8X9$tk4UV9{5~N5IStl0ft9*i z7av&31-XXwMi9grRrIQVR2yH9Wvg<)s;y`|;#twnldnL{cj2Er4q%@9nUPDQ#r1Ar z5AKw{jINz7w%D#KKqRb7I)GqDmZmjTG2 zz$G|pk#Pf|u?dJxPvonB5vF-f-vrb8Zc~Hi1&+E33+Qrh4y|ck%cszcKrwHkx6YOa z($#6sIq4danBBagA*g+dA!5IFeO#`D*$Fd(D&#}}VAm=s5gK6OSe3jEV1-l@S|Qh= z_(Me54imd~bJ2R+Vt&Z7DQwv^-~GO+AXr#^YxwR+(9|C?^@mOUL7|^?)9-Z_-~M3p zIZYsLT|0mVwm$6kNKWQAo`xLqbp+VP8$3(fa}&&#DGM`xM(2x&+kt?ot{GB_5$Q2< zajQCwD9jD?4kC^qyC7J|!`#(|F+PGul^rIQ80U(BO^}_A1$kXS zTS;T#Mbuq?3$lJi+jdL>C%1NV^MN=MsVkXaqK>z^2vr}p_uH6@D#6%D)(cC-+M@ZMfg^XqZ6`^4b&)ozP*^gL2tAd z1^xq~Mzz{C>PCEjy7Qgp2MW4E9uU z%pk%E1ZWP&?1qFPX9O7qq@|dXt1~+>KmoJks3m7aY-M=LT1Pd^ z1z-tVjX0IP%=vgQHDTNzterSH|v8VDA-v@;5XH(?bNz%|FqkvDZo)t zJyyKm%qji}wf5BTy|lHbC_654CTP_u(utl;RU=qEUK1s@BD6sk34(h1PR|cOL0|2C zO%qtHSvGWU1LiMt*V)w2TC~YiLqn1*qhM!h$X_C{>j==o*j(*bBW<-;s@|yHPz@U_ ztLl(m?Gb&uf5Rb+VMreWrcbC4N&2u#7a3`Jr4|r75<$>*yJcVvj(h*(KRL!GmXw5v z1r?LLiy&@>$;J;}5X%$BT}g{fu%OBiY<(9>VMkR~{$oG(1j5#umpG+>1374_S#Qz&|7`G5uC8i)FPV~=8ria>& zdci@zaCC$2J~T5%?;QO4ztp`3p4vWEqTI z>&XFr2Yun>sgA#dVwK(W1aHdR2ZbUoG8A5^Q;78#nSA^ev!{z*cq`u+xY28rHa3SEOA4^+Hu{!x_V&$P#D&M7L zBkP%0CpT!{$mLzw2SqzYJS=dAbq?zk&Too*urJRG^?@xhKYD+n_W&K_r_zL45A6!hXsSW|5UTx zGcn}y$gPk7#T=UOPPTpu+{opCI?n~0MsKqNHhNOxbTx@?-~Y^|1Cs6$AjFFdHUt@j7N_F$pCi#y(0ew zb|oV6rs1Ia9&_Asy(Qk+^{2h>X1rGrY(50}0kuqx;WCm0gc~Q|^EOD?A4 z>yAzM$!01&Y90i^805U6je0#;TEuE6nV*stZOMunO$veCe@2$SM0<%n@IXijrZz_O z!8r_xQ1eI_pfDO1MuWoW`tsA)J1P#6gd zBSB$=EhuYlNq3I~O?@F#U)acbMD(<+f z@x9n+fXMobbBEosQd($-3ENCxq~G_I?O{_<+;`~+T;AYRsWD#}cVAeY&FAXg3=S?q z@GF$tTOhY$dSvRAs3&`gAm+qvSR7|SsCtQf;9^s(v6@_4HjB&Su|Qd_FP zmJp=SFHFOJ2VI`t#vi2_+4Zz$%UU{n{#DpWNTW~eN~QUGt+a49n=UqI&?hF+=;oRI zMX?}&JPS0E-3R^ZjGfP=-rDsOEG~C(nZf?-DKV;e;Hti1z`(cyfAN?Kly^Iw8BkK1w z(i?j$^zZjr_RVtW-jkjCoOV`AGLj|^m|Rv)665Jf7=oz7c_=e!?$FP2Hw}0*B>)qK zv7ZG>{<1ziCa{T9#D$Z^XRLtyC0&F}5ETRF5gUuGVl`{XITSw{D+Gn8GT;(c>^;;X zMhX`IsK=xM00aj7&F_2PQgZeD^@3X`LYDThrG37;<^zLenbYc8-Zz?qmZn?oka1_& zxHHJ!2bfoL%NR0l4I8%xjawhkz>|flTzbDCKV z3=`T$U$ z(RwD)P1lcwj9bFSEkX7&iEdatA2Qa3jdej|9g`?Tej6_QH$K3Y@eh#K|Bm3t2>u6x zpCI@L1pgBNteh}BCjT4Lk!9v@7r0|>7GY}>BWw(=;vS8@xKN;j6Hl(0zPYfh4tg1G zCYfs^r*6!r(B;k?{snMyg&XB8ANtC!e6QtNNfA+;YGnd5Z_`S-0k zi_O8t-eB%Ncth5{u(j`9IcPl)G#+?hUUT`xeRJ78bJ^mtkhwN&uASe#WH2wxVcjwQ z)uM3z#_J>Z@|%PC%@IO_!Q3HuLsl_t6@$a>pw%4+8r=_|#eMhFiteQqT|Kfm7)rB; z)9mwI57zCt<9;_gv~D20ZXhJAo$tPM_)2fYaCb81n(ml_xt;KatX*MiSJ2q?09n4( z9L(JcZ^+sbwzk~q2wK~M#x^xFS{ILud@Un{zDA5`D@{f7DLYko>^HX+JVA{N+Rw{{inHZtu_>EBJNP!r%5 zYs4qT6yp%7gG^8wXfD8!dcNtqfW1VXHl8wEr3< z*vR=SYDH;qYNR&Gd~{JLW5kSlf!8G5Vk`ag-W)zF5*=p3{Sa6H^Dw}rj(jaFj_EfK z7xG$a{alrP7RJSwLC+KwRKXBJ-{Y5i=LAuJdcw+;_ngkRh8E8k2;iA?bYSMOKs>tq zClFk6bO{;$O^oa4wF_sA>($n&lPLcJCjVeYjrv}#zf$_8ve>6;5hDk?_H}g}vbjZ9 zl?}HC*@;tCY&{Msc3%YKVcUIRppUKCanX2WPuGF2gu=c3ww4&GY#TVx*>%9yek?|a zXnEQ{l9$#AOhs1i#Sg9AQM^#j9+A5yz&gGJGRI}@(Imp)%e6VLMF~bB?jW1(x-30rmXFZBuJ#B}LZuRPo6Ykx( z9`g0MkI)mc6A0l&B>835M>zI1XI`dXo?a6`4O!HE%?v-GjGAyp&3w<#^qk2Ww4M*8 z&V*BE<~!auTQ58Ao7dhmuU*tGdcM_lXJe?TE8Nr-+^{=j?hc!~=XWm)NmART>5hmH z))^p9SZ4s7-}k_jd1ZUZWDA>YLBR&H8UgK2Q+Lj)*)1A>YIQZ9RI_a_HWOQR;q4k* zG}Eq845Bpbg1ZN>kt%s6vBsoJmZxE6^GA@Q->{R?4qYG8P=06DIvXV6KQln=nXl4M8p>6pTep@h?U-1Y#CgHeba z3$k;3#t^~0F#r!~S!BGNt~MMPvKyr8N8NfIkimXZIbBguC`^2p*ufi|y~W-SavHA; z)83}SjY}r$rKYQS*9Y!a1Wi35Q%~5`GvE2XpkL^@*ngq_N>fP43k!KcA&=hLpB=#6 zk(R4@0Ms`mYzqt9g3(tsKVDfA*^P~83cEz`6JV6{w|0W%47y~=hT)xi9QGa|Ec8pF z4FWr_Tb6OH0vK?IqsDeXs*Yhmeof7`8pY^zdR?$9?(`e#YS^Fq5?D6u*uwR2m|V!u zpczG&guNvD^XkEeZph;vIzzwv`I!Le0xnc1z{H%1$ap`^h*d|6sU~7LZ z7T6G!B3OrDJ%Td&7hhN(*nmm+&phhh3@OG`1?H{AR3)aqgTRj99RwQ@`~?EGomPb@ z3|9$87lf^+>ma3=RX)@sGMUt4X#;{r1WgE<*}D68G5LD{P~PYaoKAKEpy=Vq0Iol( z$8mUkq!?U-@}$c*N=~ycIAB72tjA_GC+_L1R|K6T?F4pa38^k2=Fk3ooT>P0XqHjaQ%{=yp$5aLkYHjL2&bj z?JW>o_YzzX5)AAK24c+VD_T%Jy!qmVy-HEhPfX(O3A0(HKy@57tC$Mu1_Fu+B%lTz z=Y3=w=ICX~->AzkY5|_BbVDh`fl?0RDWj)8I z&6}PwFPP`e__3D{a(jOBmp=E5J#aS2{TA=C#>~T1_x}&<_DNrr!3vC~GPW zd_TX;A^cd&Y0?);5`H#?0Z-2`GO6KBX0Y1#{U)D2}9GXi@3`T}+ zq}yqco~5(sDOwlM=q~18$Pb$8@9e(w*nED7?+o*uOT2YiV`UW(CXnv}x}D1cD_h4! zE7#sBzmtcR+rxZ2RA%*4S$%{F^&{+c0Gak>eJUS-F671NG==!)Fy9=fP`b>e@}&>g z-waq-y_8j6$i2As!rCh(A>J0|ZA-j)K6|0)l4r3y*w7y2J3@R%nD2;X2TSXNd_#zD z2=fig0Zj_O9fi0Z8)<|r{OttxIxtZR^iYc1mvcCNTaar>d_T|`FFG$cL%N)>E@zoD z;)o&;1YO>eG4*olePh8rW5Ly}R~xQvT|D%=ZP(iF7uMY?th<#FDr^iFHinE%fv~YD z$lhNpnX*75ba`>GWHevW1ugv{<3QLr5Ht=f8M7~GE}NgRUbbFJ`1=Js%Q~ovzeY|6 z@kZ`d&DGAu@{nK;3wT3lIU~j%^7t`=PXGiWJTt~u0Z4I`Y!HW#cFBKFn;+EXFA0W= z-50vAlwCb`{m^`ONN5NP4Z-NUtmT9Pz{2Z=+>h#bu~GY@hHXPNnh&cXy-E9FjZVzf heb`taX6imN>!A3fOw9QxHyzWBg1E)-Q40_7{{j(I62Jfe delta 9812 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 diff --git a/__pycache__/models.cpython-311.pyc b/__pycache__/models.cpython-311.pyc index 153008321bf96cc64ddbdca03876a7b4c2c3cd63..30219bd24a8d3030ec0f6613d5c1399586049602 100644 GIT binary patch delta 918 zcmZWnO-vI(7~R=+x7(lXZvTM;MQD|#4Fzl^5EMZ}L;|*g5jj90yV4qH>uhTznphG| zN<=T;#RC@)qQ)LHB;HND3kiv`@t_G84kVbEXgoQywK0L&%$xb%n{Q{{H}k0ry2Y2G zXk$qGdi8N;?42laYzRThn}H{68!UNk@Q8CmK#*X7b-*_+Xf;>44HLt>F~L(IJZh;5 z8!U-PueE81Vx3?jHr;X|pDU`l;v^P`g6Ss1*iP_|wXit1tm2NWf@vBS35-WXIL}W; zbfJ*fvc+sZhg+%Ur{JX!s)LJ6v~?((Pr@#rZclE>>1{c^Bd5zFU!;Z#UR!?VUT=Dp z-jd?mQhY~>moL!|`YbK%Ufo*@EU{h0S2NsaTj9Df$|h9hpdv&~eANQ)g zWEs_TOCsAEO|h!Z1*F)j_F5h4{Isk%9x~G|MI`>6I-fFJ9<#rXHcQqhLap%Jh8ucF zwl33xEM~Rh>>U+r)O$Nb?LDDhA|5S6jB*qjNY{c0R?J}tdPErac;LX^vUy;yu#(P~ za@pG@6+6hP{kyQ@e1Vd1N(zbngm}$j4;gSjOi7(62CEVT5`>7DaFX(843WZRI4vho z95V7f)D3%b5?z5N*C21KNW+|K9`(UT*A0|_W_JReg9-P@OkX`+H{<-Gs&mDRx}@S! zN{HC`Tboy4GzN-VAzt2o1qoM4d=uY%6n8F}z`&Pq7I77391)MJxX32Z) zAmP6ojnLI$c;Fu-mHpsv;*EpG;k)05+ToW!iq@emP>yk^^N=|XmHD;I56b`iFFN6K G;P+o0+u8j9 delta 881 zcmZvaT}V@57{|{$_i^TScFxY`wlpon33Jn$%={oxlL=D9tX1fRrk#Th*3Nu4L+i%C zpdh+=y67eFP^L4j9&5M2a8&$B6n_#WQhd7uCDe?Ol09B5sS zOV1>!Qowfp;pkL&MUrin-vZRxKEXw&8gprmb3#Nk9sNeGsOO4lE~0gN7BRqG$Un|} zc6?6fEg217SHKWXyhT)sb?|Nh1 zkdvEoa!XFGy4_0_H$O^NIs^~HThCM00a>|0>Olby=Oeub9>~X76xL)2wxbZK;2orQ zmXd^1Fsme33%pRSvp7V&Z7c<2-acm=CC?&YT?xaQw~KXxQypgQa9K^TZn&=wj&|>^ zVp_S8)lFM*O24A>(tm=uKM8$NJ|F#0}#c1yqG@TQAp*C#kS3Y7^!P+p=hft>Aw-}*7-dhu2+k+0Y>0gq Y_UEmMJ5xVOxnKV~+)y71Ks@yMAC{EQ8UO$Q diff --git a/app.py b/app.py index 678a977..78d263c 100755 --- a/app.py +++ b/app.py @@ -7,9 +7,13 @@ from werkzeug.utils import secure_filename from functools import wraps from extensions import db, bcrypt, login_manager from sqlalchemy import text +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() # First import models -from models import User, Player, Content, Group, ServerLog +from models import User, Player, Group, Content, ServerLog, group_player # Then import utilities that use the models from flask_login import login_user, logout_user, login_required, current_user @@ -47,8 +51,10 @@ app = Flask(__name__, instance_relative_config=True) # Set the secret key from environment variable or use a default value app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'Ana_Are_Multe_Mere-Si_Nu_Are_Pere') -# Configure the database location to be in the instance folder -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.instance_path, 'dashboard.db') +instance_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'instance')) +os.makedirs(instance_dir, exist_ok=True) +db_path = os.path.join(instance_dir, 'dashboard.db') +app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Set maximum content length to 1GB @@ -56,6 +62,7 @@ app.config['MAX_CONTENT_LENGTH'] = 2048 * 2048 * 2048 # 2GB, adjust as needed # Ensure the instance folder exists os.makedirs(app.instance_path, exist_ok=True) +os.makedirs(instance_dir, exist_ok=True) db.init_app(app) bcrypt.init_app(app) @@ -68,8 +75,9 @@ app.config['UPLOAD_FOLDERLOGO'] = UPLOAD_FOLDERLOGO # Ensure the upload folder exists if not os.path.exists(UPLOAD_FOLDER): - os.makedirs(UPLOAD_FOLDER) - os.makedirs(UPLOAD_FOLDERLOGO) + os.makedirs(UPLOAD_FOLDER, exist_ok=True) +if not os.path.exists(UPLOAD_FOLDERLOGO): + os.makedirs(UPLOAD_FOLDERLOGO, exist_ok=True) login_manager.login_view = 'login' @@ -303,7 +311,8 @@ def add_player(): hostname = request.form['hostname'] password = bcrypt.generate_password_hash(request.form['password']).decode('utf-8') quickconnect_password = bcrypt.generate_password_hash(request.form['quickconnect_password']).decode('utf-8') - add_player_util(username, hostname, password, quickconnect_password) + orientation = request.form.get('orientation', 'Landscape') # <-- Get orientation + add_player_util(username, hostname, password, quickconnect_password, orientation) # <-- Pass orientation flash(f'Player "{username}" added successfully.', 'success') return redirect(url_for('dashboard')) return render_template('add_player.html') @@ -637,6 +646,16 @@ def create_admin(username, password): db.session.commit() print(f"Admin user '{username}' created successfully.") +from models.create_default_user import create_default_user + +with app.app_context(): + try: + db.session.execute(db.select(User).limit(1)) + except Exception as e: + print("Database not initialized or missing tables. Re-initializing...") + db.create_all() + # Always ensure default user exists + create_default_user(db, User, bcrypt) # Add this at the end of app.py if __name__ == '__main__': diff --git a/clear_db.py b/clear_db.py deleted file mode 100644 index e6b9e04..0000000 --- a/clear_db.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from flask import Flask -from flask_sqlalchemy import SQLAlchemy - -# Create a minimal Flask app just for clearing the database -app = Flask(__name__) -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///instance/dashboard.db' -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -db = SQLAlchemy(app) - -with app.app_context(): - db.reflect() # This loads all tables from the database - db.drop_all() - print("Dropped all tables successfully.") \ No newline at end of file diff --git a/create_default_user.py b/create_default_user.py deleted file mode 100644 index d6b822a..0000000 --- a/create_default_user.py +++ /dev/null @@ -1,20 +0,0 @@ -from app import app, db, User, bcrypt - -# Create the default user -username = 'admin' -password = '1234' -hashed_password = bcrypt.generate_password_hash(password).decode('utf-8') - -with app.app_context(): - # Delete the existing user if it exists - existing_user = User.query.filter_by(username=username).first() - if existing_user: - db.session.delete(existing_user) - db.session.commit() - - # Add the new user to the database - default_user = User(username=username, password=hashed_password, role='admin') - db.session.add(default_user) - db.session.commit() - -print(f"Default user '{username}' created with password '{password}'") \ No newline at end of file diff --git a/instance/dashboard.db b/instance/dashboard.db index 6abf2db328290b32ac30316a50bc43208d10cf71..9fbfd2a901bde5a1ff764fad595aef73b6a58707 100644 GIT binary patch delta 454 zcmZp8z}#?vX@ayM2Ll5G7ZAe$+e95>c@74>WJX^89}JwFj~V#h^PlIF<2%CJz@yCd zl=~Iu2AoY%Z0zEal8mkVU~>y{ z5-U@SAS64AsSq)cp_?Ok1Q}VmG#jN^C#Ui&FtSZ<C@9Z4#4{^3tiUMT%QU0Hs5sR(DZ|jnpv2WA-^{SoC?(t? p)F;u{vsB-!O3yRJbMrTSJ^>~Mfz6BwzxXFTV1Y9m2r;p$1pqn(fXV;> delta 1711 zcmb_bOK%%h6rS<3V|(Vti`Gpo$W2M>L`mG4@r)m#5lm9ob?rFz#4$}IWKHaeGtKys z@z`;*ah!?;LP$;g1K4!Q2BK^cf)%R@u|y(BAOr%5NLAUC1=o)TClu5Lx;po9?mgc* z=bLYKKR4`tZ+zr--o!8rAiIFffQ;Oq@=e^ty!H1<+n<=j`UC@OR0=TUE_I#w%=YDl zZ-}P#iNj$(wjbCJK50d)!9AR`y4-I3;fgM;sdBDd(&dugU#NL8ETqFQEzHHk@M_h=HDR~9CMWSdq6;9Ya&b+`W$Ut5Rm!DvN5>h!bC(_1ept3tE5eK* z&fgKlaUVMY6N_n>SdPc1ppBScOf02EA(}`-N%fscm#u8(U{Z`O2;vHig;(JC0N{je zV%A}Cr6$`a_RC}El4qH74sy>xw12jZ;kGtu{|?Q++5KiaSjB$WZyj4EfyMRKa0lpC zMU@`NT9)apU~h}Ex`v1G_h!3%^t`>9IMe;rp7WH=!=l59U+FZsoQS5D!*IM-m9>&o zlu_ZWH%#qO>Jn~l2dNLuW{Ydqg_6HM!W6V}tzv1tYp}X5Uc?{1)=k<8?3l4_1z+LD z_SfM0w5hKXtmVe7uF+9^5AVK3&k#phwOsgnXJ|hrcP^1A!b%=t;A2n*KI)kIfP&=F zp>yaXvZ+|jSZh>BCev_TQDx{^kV;ZPE+XyJ)NHg=m#ey>D{5I|yp@XH@W6^z&dJrP zoQLaW4R*_h9vwaFZapl`^3$|`nhimQonZnqOvuZII6uQFmVc&d{*Sq6euiVbAv(a* z0nu#2Y66l^B;4Dr5A=3H!gEYczsS4%1qRaVHMqd=fhP*+R4+Vp78cbN^Ut)8bEWIq4=Da+^ zhG?INqiz<3WLNblb$xoHtTG_zYqg>THiXHT$f?f{F&G4Kz#4rrad^+?6;MPTH zU1~50V)HH?@iGr$TCP#!t4om`nMvGUjxA**PDu#cHx~GN%VMHYFEkGS7@>wNI5@$; z$)OYe1g=o1z7hOXSVkLCF%F($;OU{0{soW@;|Otji(J2h{YRJ0ct`8KQ__5pG$BY8 z11b{x8fdnhxD9i=TjrJ-C;AQdmoav2jA6$lok?g(WlIw>S|}E8tVCid+8Ztv" - -class Content(db.Model): - id = db.Column(db.Integer, primary_key=True) - file_name = db.Column(db.String(255), nullable=False) - duration = db.Column(db.Integer, nullable=False) - player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False) - position = db.Column(db.Integer, default=0) # This field must exist - -class Player(db.Model): - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(255), nullable=False) - hostname = db.Column(db.String(255), nullable=False) - password = db.Column(db.String(255), nullable=False) - quickconnect_password = db.Column(db.String(255), nullable=True) - playlist_version = db.Column(db.Integer, default=1) # Make sure this exists - locked_to_group_id = db.Column(db.Integer, db.ForeignKey('group.id'), nullable=True) - locked_to_group = db.relationship('Group', foreign_keys=[locked_to_group_id], backref='locked_players') - - def verify_quickconnect_code(self, code): - return bcrypt.check_password_hash(self.quickconnect_password, code) - -class User(db.Model, UserMixin): - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(80), unique=True, nullable=False) - password = db.Column(db.String(120), nullable=False) - role = db.Column(db.String(80), nullable=False) - theme = db.Column(db.String(80), default='light') - - def set_password(self, password): - self.password = bcrypt.generate_password_hash(password).decode('utf-8') - - def check_password(self, password): - return bcrypt.check_password_hash(self.password, password) - - @property - def is_active(self): - return True - - @property - def is_authenticated(self): - return True - - @property - def is_anonymous(self): - return False - - def get_id(self): - return str(self.id) - -class Group(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(100), nullable=False, unique=True) - players = db.relationship('Player', secondary='group_player', backref='groups') - playlist_version = db.Column(db.Integer, default=0) # Playlist version counter - -# Association table for many-to-many relationship between Group and Player -group_player = db.Table('group_player', - db.Column('group_id', db.Integer, db.ForeignKey('group.id'), primary_key=True), - db.Column('player_id', db.Integer, db.ForeignKey('player.id'), primary_key=True) -) - -# other models... \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..8bea2f1 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,5 @@ +from .user import User +from .player import Player +from .group import Group, group_player +from .content import Content +from .server_log import ServerLog \ No newline at end of file diff --git a/models/__pycache__/__init__.cpython-311.pyc b/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..113550175539cb6aeac6d5164e7f153bbed3483a GIT binary patch literal 450 zcmZusu}Z{15Zz6(n_Tn|8w;_q5n^%s17f3SBO)9&+XOL7F5GT5Y;wYVgCB68ApT9t zu&tGyaNSkTCRzk;c+9-_n0d>7Os6q|y5#SR4a_em{>%F#+dCi|l%Whuq%g*yD&{e8I!8(PjUg<-Hh8Xufk~g_%Nm$x$-U95${yx! zq}5nbMh%HA~Z?ESfZ(RqI`J1?EbMYjX_e n?pO5^+K-Ph?$Aj)Mu$$@F*DkKvM9Kp`0UM2*$5> zUutg!gnn})9I=sd{uGp5gb@}jWD8|MKq7jLu=p9_2oW~K5O5Gd=m=IY$`Kr0MVZ(s zH!}hgab<1f@_+G>asB{^cM(Bl0i&`gA`;n%hJm1mX#oAPcmQNamxSf`c(iXNLM)CP zz{y9jf`OyQu`76K96!c(JzBm!+V}Xa3qCyu2nc}`*(TA5^tqU$t> z(|7~@oTyXNHfT%VB(2kbz&a0`lGC&-qhgUya@{gogl0`ldB4Dyz+~NMT8)*=6q9t_ zF>IpiEUD|Zi<=hLCw2XO)35>$$R|^BR&|VxW(;d3i+Dmos9g?1s?<@I%l5a1BxS8(5eS5$1 z%|2P`&&~~I=Y9gZ)K^ObwKR?{^%SkIXahxSFYrn4hUu1!mW8wyccvew#@4b zYP!QC{qSkfE~Mwdb-bYW!Q_wETLy6!5d>j~(!KHTEE*GXXXv`#4tx#_>d|C;ZA?y|p6 zPfr3)f7w5kUdRBv6h=tg4(Qn6;#YtGlFHyFK=NI1Q$kV}q?CUJV56V}xB-iBA?)Ml zl8ACR4{3L(BMHd|JpW->1MDmRgH(`u3aPqsdl(ny7SdG@c!k4ZmG)>~Lm<+|QTv)J z?W*B*TMM;O=_-%)@u;>wdLQZsTsaI4u&=v1{ZT01$MQ-vF9$Rn8|Lp4x@P-*WbC_^F4=={^K9 z72ROb%#|-zH^2HOx0$_ijcIvQv8(Kz(E0UhHhcZ*wG2}iK3!be(;CjsC!eJx1{)NU zYQBO~3K4@dSkF^>yG9UGNv(|cW-@pu-zYnl09uK?*@RUfIPYL&-L{>Q6~e8=iImto zld$q-evO(IzGG8|o=;6Nxki~@#15|BVrCInG2!94Ffdoj(-PAWF4Pca+V>4inrvdn zE<2dywwp}bu?clpgknnVT9qkzgqU8aRVuc_v^ue?P6{#^Z8LR~x5)JE0%_KrU>YLc z3yX;g7!513>7X~eTFi-|SQHIL3@j?PV7;M}3+Ym=g41<7ox$`wr&doRyJ+*ETbQIP zyx}rU57v(~8WbeMkPtNh=;v1*0IBS7tu`~CY56(S`d$9#Sw$NG&9G%&ZA8mQD zB|o;*TI-wd_sqqvx%gzwGcWn(rJkATnwh89J#*bR*IQTn#@S!K>>0_fk$jTu7)j4q z_KoG9vD!6OJ>#-(TyCxRr_LS$U0dnL=k7c08-K?BE_UKuUVO`sZ~YMM!^wM-KhOL) zbKmvgybtF)a6a($;A|JpdT`E%a~(MM3{H1u)*syAjGw2;f#lqS8$R6Vgy$cDyC`rM z2gifUK3wjE=a|n9QZk8h5p3iSKt2@u2b`V26~5O-8GlYb;->hC&;(~kiX=(T!9*)~ Pj?^h>{WsUpGAWc0){6*2BwXT>b z$77K_i9+;HscSZg{HiGpgh`^xfMqkVBEDr&MA1U)h7M^W60Iofn;1RBjP4wVlk|2s zmn1P+)|DEPWfGU=ngQz?(-X4%xUOh{2eZvfBDV~!Ueif*S+`M@CgoK~B1IdkdX)$) zl1K(tk!~xtYUm_^k>+dGhH8>%P@6^4Owm-!mVZasqJgi$5rqfuzsYYHHIz5i{35b8 zZNtn%wW?YO)3`h>3~5&0Z*0bFV77~V!Ai6KqM~_n48=?<(>~vs>t26#%@Jo^an=)O zn+ro}sx{aCu5-V$*1g}eUOebNc!mC5bfkhS6+EfXTpVV;>|E^Tddt0qt>u2@&y}r} z!H@sla5AfIX4T8AHt!5m7h09J(c2iz>|b$GMK@LSQpM)dFgxAB-9|sN{l(y(lbv_7 z^Imq|5vGC@Pmk)wXQ)PE!Ms%XN1)B9u!Px1PsD&HnYxZu>bsj0NgO?~kUU7X&xpTbu4yK|rs93<`(V^R;49Q*KNRa9Dh@*& z#|^=GXZ$=A!rZrq;A7ERX#ezuSH&uWZ)_ zKf9N1dY5kQmmFcu71lgqZ4|ZHo7=kHf7-{}PY1ubmkZwI!oKYYC08hULg{Tdl;+<7 LDw+v)_`Q4p>px?B literal 0 HcmV?d00001 diff --git a/models/__pycache__/player.cpython-311.pyc b/models/__pycache__/player.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bff7c0539016fbc188d4f3077c69ea4f75a4bf4b GIT binary patch literal 1976 zcmbtU%}*Og6rWwM*PpOpim?MqP!uY;tuRU}fzq^6DNR5GNJ^z5Usmg#vAuY`>&~v* zI!YuTa-b>)4oD#q2UkLr3LiM;m_K11S*kTsrAj?MK%5FGRjCbC=nfH4i z^WGc(JTMRjbc~xn7E~Vqf3Qtw_%E3A4az(R5I{}`B`(8pjOTT}@qhxDTN38Gr)fHam8}A@y)fr#CVp^sj@Ic*?dZj_@!`sW`9VYS~sVK<*-5Y|h{1 zSs(~xI0!O4bG#{ZfRyh#f?R_?q}XFRy^#@n(e5tLkwV`gN6~b80}2g)pMLO)`k^c8 zhyRy8_n-J9z4|@b5*vZO+eR-(yOM!!rhQRmv#E;}AW;5lYlb|*eH)M;b|eobsK9&o&+oO8VpT>1=F%Uu9GP| z%JhfW)pX6WWg54onFa~p^NPKUsw8&N3brY`T_RKCQH*rOrbw%xm5J1cAQ2O5$go`m zPEr@Y0DO%uIKc?}b8`t}tj}omYu#Tv6x)hzCwFe|-Z}cn3C%P^Gp*1}ZN4ofw(xd! zFS!@nPu4$taee>#$&@3_HKnjLkO3W?N&kj_^)9dDD&Q_Q9Q_r$@N)^kt@*UTmcoo%OGs zHXT^hSU4~9+I}=KP0vj aHStV{axC<>)2%PJH~w^Ge_eubqxuI{`s2m` literal 0 HcmV?d00001 diff --git a/models/__pycache__/server_log.cpython-311.pyc b/models/__pycache__/server_log.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..daa62a6df2d762e218b56f20bdda7f516055c4c0 GIT binary patch literal 1058 zcmZuvO=}ZD7@pbeCcA0Vgo4~ODS6V0osEtg6m<~W~S+;`w?e1+7u}{ z^iZ&e2!R%?;Hg&HgFnI_kU|b&ubz5~)Kf3|PBt59>+H@m&-=0O*X-9+Dv9{gKCC^) z2>t8{eUSR+`81F{L=eFivayCS=Yl0@f`H}_iESeyQe2Mp{m~(?5Q9m zI1tFnVli^%=OE7y5cUv78YYOxrm?aZIRcOTppFyaP=Fk;!==S9d-tS7WF@gf=@*;~qaF{3MI~m*^m>)5wo9nx zsiCA>ZfU$y3uIkqw8C_Kb99hmJks$Ac{c&-sFTj`FCV;arStt)&Y$x-7jq*#hGW=N z*Mp?4Lzb$=`LM3PsTx*jVZ6;C{?xUqwi6^49G{jb3j~t{@g<*`PKgb34|&G$>9T3l zKqkO^&f-0yV=Hr=A)yB zEv0a(6xvFm{-l$-wew>4RioBm&Dxh6AGexY-=$V+;xskUPEFKjJDF@lXe#^n4)RAE zZFRb>&b2c0r1A6|r=`i>^ah6)z|!1xdQ$Q^$jw@_Hj+StKLh1ZVY4GLD|;jw}aC4-qRK17^=Ar(1HAm>wo{p*vtDDSsVDt literal 0 HcmV?d00001 diff --git a/models/__pycache__/user.cpython-311.pyc b/models/__pycache__/user.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09c15348d2f66e59654ac38638a61138db4ab7e3 GIT binary patch literal 2471 zcmb7F&2JM|5P!S&ZtU2}=F91ZLI@SDWF!bmh#IP@NG46ofq)uN8%t>Acwg*I)@$>2 z7vd;FKBSUSeK1udK;n>78bl5pIrdMmktL#)Dk1g6Ehs&4>dbDuwH<`g=iS+P^Jd=6 z{AS+sA3Z(Y1jermzhxf;2>BZ)%^@B#2TQ;_CMr=mo#ePA$05(_d@hg-xO_kl=7glc z5uVV%HfXKC~O|@E=jT zH%Kz7Mu7IvUNyQwI;tpvwC9Ml52U?Er2VP{`wXakP|toRh=sca6^s;^mP@gsvOVo?6uDyH!|;b1VWMN6@uQ7OUT-(xsHGTWP;)u#EWQd$0`T zF|mL)$Ch8OrUbw1Tze(9T6gmm)efz7qWG0o3FO4jtqHcZbbdM>bi_rsL7m|YH7HA2 zR4JrP^M0PGN;YL?9Z{v}ybAd>#m{*sXi|NN4T3`x0~j0oaBe)C&(ZOMHa2f(UD-l1f|JJ-XKG`uFD; z?*U#DCGu7pEL+F&!$!e?oklVSq}5X&?rd9GYFJtt8cAJ?`3_sfm=5~_$#D{cW}W@G zzbN`M@jg(YU|P)K zHT4J*Vg@=~Q`aJH{@!E^Y)tGREc8J2(&pC>*DLF_(C2PgbMZkZq$oxzM-|2CRunih zwvK#MQSRC)-CJQ8ts`E|>vqm?x~>@(%}~bhusGtp#WW-1glsrasP1;96J&XvIs%jh zB%gsILtNlEvwVRvtH{ov>@1S6kRTKu-fo?+kr{&M5@gH?0H);s_QC_@fyzjAV)L7w zOSSODdiY`^e6f_+ljJh16rYSd8F)6bHSzS+vr{|PD_WCg>e5U@nkoI*t}*#^^x5dk zFKg0tU7Bu4)36>HDo<1{R~M>3Z7w`vkM3;V*|}YdOx7cljmTtaW-l5mCo0#fT6J+# z+u|SPHgh{a*P>JP=u{&*Rr-EE$O$*OebPcjPPpnyd@2&Lcc15kA=DdsAAsq`FP`8S zOEI*HwOGaW!Np0Gb`rPDE{JpSu+vRfEozur-Y~tIMwj%IxuSTNF$)6DPQ(-G`HW^T z{QLJVAmKV*r(R9mbLiLUL+D$tZN6idK^^" \ No newline at end of file diff --git a/models/user.py b/models/user.py new file mode 100644 index 0000000..fbedab2 --- /dev/null +++ b/models/user.py @@ -0,0 +1,33 @@ +from extensions import db +from flask_bcrypt import Bcrypt +from flask_login import UserMixin + +bcrypt = Bcrypt() + +class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password = db.Column(db.String(120), nullable=False) + role = db.Column(db.String(80), nullable=False) + theme = db.Column(db.String(80), default='light') + + def set_password(self, password): + self.password = bcrypt.generate_password_hash(password).decode('utf-8') + + def check_password(self, password): + return bcrypt.check_password_hash(self.password, password) + + @property + def is_active(self): + return True + + @property + def is_authenticated(self): + return True + + @property + def is_anonymous(self): + return False + + def get_id(self): + return str(self.id) \ No newline at end of file diff --git a/templates/add_player.html b/templates/add_player.html index d688465..c81f1c5 100644 --- a/templates/add_player.html +++ b/templates/add_player.html @@ -60,6 +60,17 @@ +
+
+
+ + +
+
+
Back to Dashboard diff --git a/test_api.py b/test_api.py deleted file mode 100644 index 5bf5bb7..0000000 --- a/test_api.py +++ /dev/null @@ -1,60 +0,0 @@ -import requests -import os - -# Replace with the actual server IP address or domain name, hostname, and quick connect code -server_ip = 'http://localhost:5000' -hostname = 'rpi-tv11' -quickconnect_code = '8887779' - -# Construct the URL for the playlist API -url = f'{server_ip}/api/playlists' -params = { - 'hostname': hostname, - 'quickconnect_code': quickconnect_code -} - -# Make the GET request to the API -response = requests.get(url, params=params) - -# Print the raw response content and status code for debugging -print(f'Status Code: {response.status_code}') -print(f'Response Content: {response.text}') - -# Check if the request was successful -if response.status_code == 200: - try: - # Parse the JSON response - response_data = response.json() - playlist = response_data.get('playlist', []) - playlist_version = response_data.get('playlist_version', None) - - print(f'Playlist Version: {playlist_version}') - print(f'Playlist: {playlist}') - - # Define the local folder for saving files - local_folder = './static/resurse' - if not os.path.exists(local_folder): - os.makedirs(local_folder) - - # Download each file in the playlist - for media in playlist: - file_name = media.get('file_name', '') - file_url = media.get('url', '') - duration = media.get('duration', 10) # Default duration if not provided - local_file_path = os.path.join(local_folder, file_name) - - print(f'Downloading {file_name} from {file_url}...') - try: - file_response = requests.get(file_url, timeout=10) - if file_response.status_code == 200: - with open(local_file_path, 'wb') as file: - file.write(file_response.content) - print(f'Successfully downloaded {file_name} to {local_file_path}') - else: - print(f'Failed to download {file_name}. Status Code: {file_response.status_code}') - except requests.exceptions.RequestException as e: - print(f'Error downloading {file_name}: {e}') - except requests.exceptions.JSONDecodeError as e: - print(f'Failed to parse JSON response: {e}') -else: - print(f'Failed to retrieve playlist. Status Code: {response.status_code}') \ No newline at end of file diff --git a/utils/__pycache__/group_player_management.cpython-311.pyc b/utils/__pycache__/group_player_management.cpython-311.pyc index 71a5c799a4602e020597d53f245e957935f631cb..830f25c05c875d8178451a9a3a3930ff61607ae2 100644 GIT binary patch literal 16047 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 VH;cT^4&Z1l@_fy;RYL+p{|hWOFIWHo delta 3216 zcmZ`*Yit}>6`t45&g^sc>2>VbV<&bpjqSvhQ=2@SM-|A!DJ2QieQejBv9s=aH{6*` zNwr%$C~DGRP{XBae-v;f2t)~@fDi~3kPry*3p-jV&Zv_6y8>wuR78l^Id|4;J8iFK zzP)?TxsP-1cg~p~SATOb@`G?Vz(Je&?(^r%i5rm&|MHD(!=L3jlQX4)JQElT%ml}R zGoi5%jQOr`V_{PUjF^7FsHp&IX4ni|;m2ZT5dOu@5MaVozrY!h8_S)F!MlPvZiqMj zHzF7!9p?9TObEDVqe<{9!uxj)M&INdp5AY3*Q$cEPH9@8uOvI@#iXY3+(UxSjt=^y z6!Y^(x##ApBJEA}(_3O%6s8q=A7mehiI(K*W*j#yul5&$^w5q}LYfY(j@%J$@*H=Y zr=M>f*j^FV1QjV8Syh_9{LldkkM5;onP`CLDgqcSzWf$_U)nLN=ZdDDGfmyL3f6>U znfir7?vh1p-6`pLXCRwuG`Vs6H|#k;Dv&F*a8G-3q3OVDK3Z(FvVFBM%84(0kL3uXH}LC3qk zFPANH$rYz8hxWl^^xskKXry88nfQPR}Z`uRucsB$>gq{9#4 z{;vvG75a{Hh}M+>QMsnjF158sx+YBWrg%kpO*zWlgq?1~>$R${A~71sG^_U0wvnh@ z^;i7lIr@&eh0d!{Iu(!7f*PQIQPaGiih)xpf3|u5^Oq|=n0~a{oE~7)yDH+e1f=Mo zUsJUX41~)1Sx-3as|Nr_obB5ufxbv%@r+y%fe|wOPw=OiR?xX6xWd3L(g|G~>8q z7P1MU4ZxL&HB-81F`kha4x>Ye0$`}F!YBg_f|Y?&?0~M14bo%Lt>=(S%q64`dbSFn z>o4fo9%lx)0n5xg4dxA@81+mD)dvq4%fj*b6Zb;hcSGF^BTJ#aTBvXC3Hqn#^FX#i zB-5G!S z9MtAcl5Tpkt(6ba*JCeqsa4VBD`FPC!rFYmX)+1&jGx}lbkIA4EM;vJ-0$e4w7rY| zK7MpIxONL<0M=ROe)R1+pE7?l$D)f2iR?o8AOKt+rh+VH0FPW4Md(1!e<2f6kYhDG zI1IAm@Y)psdLv<^c%M<{0Itc)Ten}6Yf9Im(nVJi2M;4%CUCLUhT+Y!^{e3iH#w6x zg#tIjS0x_OcSZVGIQr=2wifzQ@-b1ac>P;ql)jqW-s_+hjaK0*d_}JK??^Xcv)j!L zs|ri!=XONtcq+>)^mnP#LCIChwnflb+LGQ8Hv(>GvXHZ1%#KfxOBWonhd!M)cHv1q zGRCpT9EY{eP{}?V*pF}k;UK~u`it~|tseQ%bI=;(gLIkoG95VvnorZpCW9VnIg_ZX zzPS814&WaMbH_i5&>yuN)nQQ}SmJl2;=uZ8S z*|%{RFKCWi>^k-)?z()Azqv>v`XX7zsrOV3P#djj zqf6@PntFOgja|>Yv8@*0vZQXUsasdn+0gaGYpMBEUFCeCdV~ul=0-hN3ZtV&(v@dQ zKycfO9i%BMmr%OC0ym4_UAFfb7AiaWf;~_uO-+H{kesYe63jdV*-S9X2?iADLcoGY zhB-(iEd3CST7uET9K=2p*g06X`j@f~&QUH{2gtLa0y&zqKjf&ke|UD8JH7bn&ocMa z;-^1#DZ{7gToZ=2^RYVDgfo1Y@2+!A=t%PEI@g501Rt+c{Xl1XgiqADj{`gZ{{hdq B%zyv@ diff --git a/utils/__pycache__/logger.cpython-311.pyc b/utils/__pycache__/logger.cpython-311.pyc index 9f64de52eb8baab1257e20ceca70935dd3949995..675508cb57c55d8fc758219091991307fe75fa02 100644 GIT binary patch delta 1428 zcmZuw%}*0S6yGUZMEbE*QlJIQN;FLo5@WncOw@v*g;FSL1j(l9ET8QzncadmHTB@Z zYqOU|V`Ab#NHo#tfq2o2_ifTs6aEK`o`?tE%Z-U#)Y7&X zBj>?M#sKy`E#Gz__gE&9k}JxsZWh7Dng{%%NCZ%=NI_kb>_E1gq^lB(B@e)#fUeOK zpK^zM&=ivO3zq>w4wftp*x*x5K4m&$sDZl{%c#YY$VGJrc&-w)_%Bm(kDlS?H09`Am5@)_tJ5LpKJ1SrX$Aq*=)5uY;h_n1vS6rklN5Six|sY z9FhBUe*|EssnxB3R60HYZHmc&Ke2gW@)VJ#ir|9rZ5e%w`^&1 zT$pa~$tIsP9ihy)ycfV}0T^^Z|Cunpc1NNW^pe9J6%Qa^=>YUrc)-qAKBMLfSI?x& z!9zOSEF@G)rMl-`J`CmUP5WN{AvM9*xsn+SLEIH%#{er zQ>4wR;Gsg*l?-RKWS>s*5S@XpJpy2lbi{EzH$AWYmbUQVp?hX#PEYg7DKB~Ao*r={ zbiW@Q=e_0m#Sq*ucBA1DmZ~HL6w1EX$+u;1f073>Ly6M7w{I76zcl-xLXewO@mMn|;A2M!#0B~V|!2kdN delta 156 zcmdmGGE1FrIWI340}#xd6PMx5F_BM#v16jT4x{MAkZ4xMU2KczG$)vhQU$YliLVxP@b5-yAa42+CUj32-xSUmuc#U)Mv diff --git a/utils/__pycache__/uploads.cpython-311.pyc b/utils/__pycache__/uploads.cpython-311.pyc index 4b815691b9bd6ac924fbeb868140ec67b8ba441b..22b1f3449db33fccefd8bdea880b27df7f63abd8 100644 GIT binary patch delta 3453 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 BXcYhe delta 2718 zcmai0Yit|G5xymP$2(Fa#g|BlqF&bPY*Dggxr$syq7u_m99O7eAgI$sG^0DqlqHc_ z9wo&%Spi}L4q^m#rf32uZj_*KoGK_3umudXNKh2EgQ7smKt){w{^=iS^FvM0qOAd; zJ9m1-Nze}NKF!U}?s8|onbqd2WABWGkng6i5GaGLvZ8o z3PPWtI(nGuWN-c&ydzu{t&+UKF@VSI-7wM~irp1mC9z>$Y*=Y7i+fD#4Kw}@{wX$gtoVjsu~;@O#m!(ZiRGVRJXjKz z?7HnuP7!p0@sD{iUA$&n5;fd>OQvn`ps_JTbn!Zyvuf}Mhn6JWz9PZ%?qEn#q=&}{ zr}HZg93vw1c%yBOuk7H|r8l{2w(E==tH=*S2zOb7kHQh+gmSFIW#6`Q+;kOu+#H5S zy&h5p)ipJpI)8%O!)X|hEotRw!;(YWS&WsFOLxHWmO#rCtErQm;$Cu`XS6i`E|3Lo z(Rz|wAhIX@J@|QTEBvvg79Q086~3&?xhg^`$M~9}ezrUmA|3FT$N+JJx4!O)VP>KU z1{F!2E6l1it*JDO#t0gPbS|gPDMqsBalYigI(0fTqta1$u|6Cf zLl2Kp9fo->J*(y`Btx;l;N$w}cb{ZR9!sHVSExa};5PAaO)Z-;R>{-T%r}dZ-iL6s z$rj{&_aGj9_Tc?+^GbU;EU$-IE%8!eB8# zv*Lx{cDIo>s0!D?gMEP-Rvg|X>AZxSn%V-<+F@T$)auhERzsoaH^z6$$DjTHmV3Sr z@yr)Gj@AUJdgT?rFKDRq} zi^T;8hV~K}CWdNA0187*z6G{ag*R^~vqs^)p`&CDz8GpH^AH-o5Nu^GW~H{FhW|HE z8PKw8EvrP-v0%Y`)!N> diff --git a/utils/group_player_management.py b/utils/group_player_management.py index cf50be9..cdd1d32 100644 --- a/utils/group_player_management.py +++ b/utils/group_player_management.py @@ -8,28 +8,29 @@ from utils.logger import ( log_content_duration_changed, log_content_added ) -def create_group(name, player_ids): +def create_group(name, player_ids, orientation='Landscape'): """ - Create a new group with the given name and add selected players to it. - Clears individual playlists of players and locks them to the group. + Create a new group with the given name, orientation, and add selected players. + Only players with the same orientation can be added. """ - new_group = Group(name=name) + # Check all players have the same orientation + for player_id in player_ids: + player = Player.query.get(player_id) + if player and player.orientation != orientation: + raise ValueError(f"Player '{player.username}' has orientation '{player.orientation}', which does not match group orientation '{orientation}'.") + + new_group = Group(name=name, orientation=orientation) db.session.add(new_group) db.session.flush() # Get the group ID - + # Add players to the group and lock them for player_id in player_ids: player = Player.query.get(player_id) if player: - # Add player to group new_group.players.append(player) - - # Delete player's individual playlist Content.query.filter_by(player_id=player.id).delete() - - # Lock player to this group player.locked_to_group_id = new_group.id - + db.session.commit() log_group_created(name) return new_group @@ -106,7 +107,7 @@ def delete_group(group_id): db.session.commit() log_group_deleted(group_name) -def add_player(username, hostname, password, quickconnect_password): +def add_player(username, hostname, password, quickconnect_password, orientation='Landscape'): """ Add a new player with the given details. """ @@ -120,7 +121,8 @@ def add_player(username, hostname, password, quickconnect_password): username=username, hostname=hostname, password=hashed_password, - quickconnect_password=hashed_quickconnect + quickconnect_password=hashed_quickconnect, + orientation=orientation ) db.session.add(new_player)