commit-10-2-25

This commit is contained in:
admin 2025-10-02 15:45:10 -07:00
parent 343a7f7dd6
commit 4b75d0886e
28 changed files with 1212 additions and 290 deletions

View File

@ -1,11 +1,12 @@
{ {
"url": "yahoo.com", "url": "www.ataphotobooths.com",
"mediaFolder": "playlist", "mediaFolder": "playlist",
"imageDuration": "5", "imageDuration": "5",
"player_type": "mpv", "player_type": "mpv",
"mediaLocation": "/home/orangepi/Desktop/playlists/ATA", "mediaLocation": "/home/orangepi/Desktop/playlists/ATA",
"autoStart": true, "autoStart": false,
"autoStartDelay": 5, "autoStartDelay": 5,
"autoPlayAtBoot": true, "autoPlayAtBoot": false,
"browserPath": "chromium" "browserPath": "firefox",
"browserOptions": "--private-window"
} }

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
# Pyarmor 9.0.7 (trial), 000000, non-profits, 2025-03-05T12:22:13.847182 # Pyarmor 9.0.7 (trial), 000000, non-profits, 2025-09-26T00:34:58.972446
from pyarmor_runtime_000000 import __pyarmor__ from pyarmor_runtime_000000 import __pyarmor__
__pyarmor__(__name__, __file__, b'PY000000\x00\x03\n\x00o\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00e\x03\x00\x00\x12\t\x04\x00:\x81\xd6u\x83\xf4\xacR\xbbSmm\x9d\xa9\xf2\x8c\x00\x00\x00\x00\x00\x00\x00\x00\x85\xb7 \xad\x80\xbb\x7f\x90\x1f\n\x9f7\xe4\xc2R\xae&s\x0c\x11\x97\x12\xa4\x1f\xd53\r\xbbU]\x82t\xfa\t\xd4\xbf\xd2oTl\xfe\xf7\x92\xea\x9c4\x94\x13b\xc9\xc9\xe6+,\x98\x81\xa3\xc8w\xb5"\xd3\x0e6j\x00\x01\x01\xcb\xce\xa3\x1bC\xcf\xb5\xec\x06|(a\xe5\xaf}.\xb1\xef\xfbC\xe7\xce\xcaqFA\xf5\xf7\x1d:\x13\x0b<\x86\r\x0cx\xb8))"\xbb!\xef\x18\xd9l\xe0\xf27\x97\xec\x8a}P>\xdd\xd1(\xd1>c\xaf\x96,C\x04\xacQ\xecZ\xf3x\xaf\xc3\xa1\xcf`\xb7y\xba\xad\xe0\x10\xb3Ni\x1e\xa9\xe2.\xfa5\xe5Nnl|c@\x9c\x8a\x95\x189\xc9\xcfbP\xca\xb0\xbf\xedr-rO$\xab\x05qH\xb2\x01\r\xeaC*f><\xfb\xa0E<Vd\x98\x8bt\xae\x1bU\xfepF\xf3H\xbe)\x94\xe6sM\xde8\xc9\xd7\xa6 \x06\x12D[9\xd8\xed\x0f\xa48\xc0\xa4\x80]$\xd8\x1c\x1a\xee\xde\\\xab\x1b\xd6\xb5\x8c,?\xae\x8f\xf4%\x7fh\xef.\x1c\xd1\xdc\xf2\x03"\xa0\xf4A\x00\xe0\xb5\xff\xfb8\xa1\xfe\xabN\t\xb0v\\/X\xff\xd3\n\xf6\t\'F\xa6M\xb5\xdb\xba\xc3\x00\xecO\xe0O\x8a\x04P8U\xb1\x8f\x14\xf4\xe3\x05\x0eP\xff\xae\xfc\xda\x1d\x1a\xa7?Y-\x8c1Z\xb6\xd9CX[\xef\x03\x0f\x86\xe1\xb69oz\x01\x05{{\xdcz,qt;\x88\xceL\xe3\xaa\xe8}\x83\x0f\xb6#\xf7\xfc8\xcc\xee\xc3\xd3\xed\x0f\x14\xe3\xf8\x8a\x95F*:\xa0)LG\x07\x13\x12\xef7qk\':\xd4Y\xe1\xd8\x8d`*\xf4\tvDeo\x10<\x8a\x94\xc6/\xd6\xd2P\xd6\xb4~\xd4N\xca\x1d\xb5\xf3\xae\xaa\x9a\xc5c\x15\xba\x97%G\x96\xf8g\xbaYv\xb0m\xe16\\\x80\xb5\x1a\xa1(B+\xdfq\xc6\xa9\xdc>gh\xce\xe3c\xe6t\xec\xed\x89\x82c\x12;(\tF\x15\xc1\x9e\xeer\x10\xde\x99\xe2\xe8\xaeS\x81\xb0\xa0\xb8\xd2\xbc\xf0v\xe5T\xe8\xc8\x00\x88|\x82\xb8\x03\xa2\x92\xd3\xd0\xdd#\xf5\xd5T\xcc\xd9\xe3%\xec\x83ZN\x98\xeer\xcc\x19<|\xbdh\xf1*:)j\xe9;/\'[\xe5\xcaA\x94p\xf2\xfd\xbb\xbe^RC\xc8\x9a\xb9\xf21\x06\xbd&\xe8u\x94\x19!\xdat6\xe9\x0f\x8c\xfc\x15\x06."\xe0\x1e\xeb\x1e\xf5\xbb\r\x01W\x83\x92sm\xb3m\xc8\xe9e\x01O\xba\xfa1oy-Lo\xd3R$I\xc4\x83\r\'t\xf1\xfc\x12\xda\\\n\xb1\xc67\xa3wq=\xa9\xda\x80\x06\xb2\x87\xbd\x90T\xc2\xf5m5\xd2qP\x00\x88u\xba\x8d\x7f\x8d\x95\x8f\x9e\x90\x879^\xd7\xe2)\xe3\x94X\xa1\xa4\x1a\x01\xe3\xc8\rB\xccI:\x93\x9fZ\xc2\xb4\xb4\x0b\xaa\xd5\xc7\x05\tf\xf5c\xb8&`\xd0\xa8\xa3\x82\x0coe]\x056\x8cu\x87\xd3\x82\t\x80\x846\xc7\xbb\xaf#\xcaam5w\x03`\x91n\'\xc8:\x94\x13\x89\xd9\x98s\x15]9\x90\xf1\xdd\xa9,\x98\xa3\x1f\xcf0_cK\x86\x84#\xb0\r\x19\xf9Z\xd0\xe3\x1c\x83.\xa1\xd9%e<^M\xa7/\x14\xf7\x89O\x89D\xa2*\xdd2\xd0\xcc\xad\x0f\xfa\xd2\x8c\x83\xb3\x94\xcb|p\xb2\xdb\xa3)\x9a\x8d\x03H\x07\xa3\x15\xfc\n<\\\x1d\xeb\x07\xfb\xbe\xd3\xbbc?qm\xee\x08\x89\xb5\x19|J\xab\xe7g}R\x11\xbb\xb4l\x93\xa3\x0f\xd2]\xa2>\xe3U\xcdh\xb0\x8e\xd3\xf5YS/Y\xc1/\xfa\xd8\xb8\xc5BP\xa4') __pyarmor__(__name__, __file__, b'PY000000\x00\x03\n\x00o\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00e\x03\x00\x00\x12\t\x04\x00\x982\'p\xa04\x0f\x17B\x91\x9d\x8d\x82\xac\xf3.\x00\x00\x00\x00\x00\x00\x00\x00X#\xed\x0c\x92\xd5\xd0\xb3\xf8F\xce\x01\x0e\xa6b\x12y\xe4v\xd7l\xce\xd0\xaf\xc8-\\s\x9d0HAn\x8c2\x99\x16\xa2u\xca\xe1)\xe6\x9c\xb7+gZ\x1aE\x1c/\xfbySG\xf1\xee\xd6\x8c\xd4KK\xbf:\xc9\xd5\x14 \xe8\x8c\xf2\x7f\xb2\xba\xf1\xda\xc5\xc5/\xcfK\xd0M\xf8\x89\xf7\x9b\xc2\xe5\xfe\x9d.\x02(a\xa9\x0f\xee\xfb\x9e\x93\x897\xc8\x97\xa5\xf7#\xc7?\xa7\xe3\xaeZy\xe8\x1f\xd3\xfe\x1e\x0fV\x13e\x90hqi\xfd\x10\x1a\xc4c\x95\xbd\xde7\x16\x00\x1c\xac;%\xdd/\xe9\xc0\x91\x8e\x88\xca8\xd4\xc9l\x8bL\x0b!k{\xa4\xd1\xfb\x97`\xd8\xe2\xb5\x11\xabO\x13(,-@\x0f\xb2\\J\x94Gk\xa0\x9a\xed~S\xb4\x14\xf9`\xf7Z\xc7\xd06\xd3\x91\x05\x91\x8djn\xe8\xca\x80\xc8\x8c\xf3\xe6\x95\xd7\x05\xc6\xb9w\x87\xde\xd5S8\x13\xb8\x87\x1fP&\xee\xa5\x0f\xb2U\x16\xdeF\xb2\xe63\x12\x99Z\xef\xc5\xb0\xb48M|\x12\xf6m\xd6o>\x91B\xcei\x1f0u\xf2}#\xf0\x01~\x1c\xcc\xba\xcc\xba\x16\xe1\x90\xcd\x17c\x0fW\x19V\r\xb5\xd2{\xbf5LA\xcd\x8c%\xedi\xc6\x9e+d\xc8c\x921rP\xd7(\xc2F\xd7\xd1\x06rs\x9d\x17\'\xbb\xc2\x9fz\xfd\xe0a\xcb\xdb&)\x18`\x05\x8e\x05\xe4W\x07\xde\x88n\x04\xc7dV\x04j|\x92\xc3p\x8f\xd62\x04D\xcc\x84\'\xdd\xd6\xa2g\x1bs\xe3\xbc\xb3/{.\x1b\xf3.[\x15\xb9\x90\xcf{\n\x96\xee\xdb\xc1\x0e\x01\xf1z\xac\x88P\x0b\x13\x03\xca\xee\x10\x03W\xb2\x02JRD\xeb3\xf7y\xa8\xee3\xf6I\xc3^$@\xea\xbb\xe1T\xbaU\xa3\xe4F\xdaZJA\x13=H\xfcf\xc4\x03\xd3\nt\x81\xd0\xbfD\xcd\x8c\x00\x1bjlk\xa8*\x080\xf0\x85e\xdd\x9b\x02XS!^\x0f\x90\x0c\x04!\xe3\xa0\xaaC\x11\xee\xa7\xd4\x1a\xae4\x9d\xec\xec>C\xcc\xf3\x83\xd2|\x85A\xc8\xf3\xa6\x044\xa9\xcd\xdc\x96\xfd\xdd!\xff\x08\x9fx\x03\x8fA\x8f\xf89yN\xa4\xe0|\x07\x86\xef\x99\x99!\xa4\xc7\xda\xe0\xc3\xcbkm\xef\xfd\xf5\xa7\xb3]\xc7\xf5\xc8&\xfa]\x05\x12$\x97Q\xa7 o\xfbQ\xf9\xd2\x1c|a\xfc)O\x81\x8a\x18\x1d#\x90xud\x81\xb8{\xa7$9M\xc2M\'\xfd\x934\xd1df\rH\x1b\x8dg\xd8\xb2\xb1u\xfc;>[\xe3T\xda\x0b\xcf<\x7f\xbbe\xa7yRv_d)\xdcK\xbds\xe4\x98aK\xcd;\x0bbOg\xc7\x90\xc7\xd5\xd2\x9e\xf6Z\x9e(\'\xb3\xfe9\xa8\x80\xd0J\x0ca\x02P\x8c\xc5J\xf4G\xd7\xc6\xc9\xf5\xc0_\xedJ\x8cr"%\x04\xcd\x88\xe70\x87I\xfa\xbb\xa8*\xab\x13R\x15\x06\x16U\xa7\xd5\xf8L\xbeKU\x16\xfe\xc6K\xe6\xf2\xb2\x99;J\xef`!\xdblg\t\x99\xdc\x8e\xec\xbf\x8e\x02\xde\x8e\xafH\xcd\x02\xb3\xe9\xcf\xecH[\x9aF+\xd5\xf9.\xe8!\x16\xff._\xc1\xf4L}\x8f\xf6O~\xb7\xb4j\x05OX\x1e?\x97\x18\x14\x00\xfb\xaaP\xdd\xcc\xba\xe0;\xee\xc2K\x8bMa,;\'<w\xa3\x9bp1\xfe\xd2\x9cQF\xc5\xba\xd2\xc8\x02\xb3cY,\xca\xcdK2\x0f\x9d\xc2\x9c\x7f\x15W\xfe\xff\xees\xc0\xd0\x91\xe0\x1e\xfa\x83\x9a\x19\xd6\xe4K\xbb\xef\x9d\t-\xbb\x1f\x1f\xbb\x07\x03\xdc\x9bG\xe1\xadyPa\xe49\xa9\xba\x0c+][\x8e\xda\xb6\xf3/\x88\xc4\x95\x00\x85\xff+')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
# Pyarmor 9.0.7 (trial), 000000, 2025-03-05T12:22:13.488747 # Pyarmor 9.0.7 (trial), 000000, 2025-09-26T00:34:58.450603
from .pyarmor_runtime import __pyarmor__ from .pyarmor_runtime import __pyarmor__

View File

@ -1,3 +1,3 @@
# Pyarmor 9.0.7 (trial), 000000, non-profits, 2025-03-05T12:22:14.160307 # Pyarmor 9.0.7 (trial), 000000, non-profits, 2025-09-26T00:34:59.302633
from pyarmor_runtime_000000 import __pyarmor__ from pyarmor_runtime_000000 import __pyarmor__
__pyarmor__(__name__, __file__, b'PY000000\x00\x03\n\x00o\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x0b\x03\x00\x00\x12\t\x04\x00\xa5\xad=\xdfl\x98\xc7s{G\x13\xa1\xc0i\r\xc9\x00\x00\x00\x00\x00\x00\x00\x00\xff\x8en<\x1c\x85K@\r\xfd\x04\xdbX\xb1f\x03\x17\x89\xb6\xf7\xf5}2\xfd[\x1b\x08\xd6s\x8a\xb4\xbd\xdaU\x94\x8e\xbb-<@b\x9a+\xdf\xac\xc7\xc1\x92\x1e\x8f\xf1At\xf6\xc7bh\'ly*\x9f\x94?MXOb\xf4\xa8\xc7\x07\xcdx$+U\xdf<a<\nN\xdb\xd6M\x03\xd8\xe2\x007h1\xb4\x8e\xe9\x8b^\x80XA\xd9\xd5\x97*\x860\x19\x97]\x1bI\xc2$95\x90\xe3\x91\xb6\xb6_\xc6\x80>8\xad\r\xe1\x83\x15)\x87\x9eq\xcfN\xc2GL\x83\x01d\xa1v6\x1a\xfe\xc6\xe7\x86\xc2\tZ,\xfa\xc6iv\x90\xcd1&\x1d\xb8\x18\x8b\x7fW!\x9a%\xf4;#\x12\xdd\xa9\x1c(w\xca\xb6\xc7\x85\xee60\xefI\x93\xd0x.$\\\x80n1\x9en\xde\x1e\xd6\xfc\x07\xed\x1b?\xa9\xc5F\xa9\nR\x16\xb1.\xa4\x87\xb67#\xd5;\x8a\xdbH\x02W\xf2\x83\x8eK\x1b\x8bP_\xdby\x82\xdc\x83\x13\x86w\xcf\x9di\x93\xbe`S\x96\x8bP\x93}X\xe7C\x02\xed1\x07b=<\xe7\xf9.\x96\x9c7\xa8\xb3\xc2\x0f\xe5mA\x87q\xbb\xb5\x16;\xe0\xf3\x13\xc3\x05\xd8\xbd\xd9\x07}\x17\x7fk\x82\x91\xe3\xfb- \\m\x05\x8d\xb5)\x0f\xa6\x00G\xb2\xa0\xc1\xca\xc1\xd0\x8bu\x87\xbf\x18K\xd9T\x8cs\te\xd4s`\xb2VG\xdc!\xb4\xed\x7f\xcc]N\x9f|{\xe8W\xfb@V%p=\x93,\xa4\xa9\xd3\\\xfc\xe7\x9e\xfbAG\x08\xb3\x0f\xd8\xf4\x8c\xb6\x80~\xaa\xd2\xf8)\n\x19\xaa?\x0eD\xd1\xb3\xf1\tJ\xf6\xa1 ^\x05\xcbbk\xea\x08\xa3\xb1T\x0f\xcb\xc7\x82\xcbD\xc7\x94<\x93\x17\x9a\x8f#\x16\xe6\xc6\r\x05i\x19\x8a\x08\xb5r\xf2 \xa1\x1f)\xd0\x81\xc3\xd339o\xeaY\x036?\xd5\x1c\x98W\xf1A\xd4\xfd\xba\xd5\x9a\x10\xfe\xd4 \x03\xe4\xd0F\xf3\x984\xb2F\xad1]\xb3}a\xd6\xa8{"\x16\xdan`\x0f\x8e\x81\'\xf6\x88\x9f_\x02\xce\xff\xd3\x12\x05\x17[9I\x11:W\x1f\x1a\xd2\xa8\x02\x97\x9cQ6\x9dP)\x1f\x8f\xa6P_pg\x98d\x84\x85&5\xc6b\xd6\xf9s\xcb\xd1x\x16\xb2[\xa7\xa8E\x8ed\xbasc;|h\x88\xa6\xae:\x0e\xb8\xf5\xbb \x01<{:\x04D/|t\xc9^\xfe\xb0\x1a\xc6\x85\x91\n<\x13[\xf0I\xe2\xc0\xfa\xa7\x7f\xc0\xc9U\xd1\xc1\xeb\xba\xc6d{\x07\xa9o\xc6\xac\x84\xdf,V\xd8\r\xa3\xbf\x1e\x9f\x93\xb7}b\xcd\x16\x85\xd5\x9b%\x86\x87\xaf\xd0\x83\xc4\xc7\xb6_aR\xa7-\xa5\xfd2e\xd3\xcb\xa7\xcd\xb2\xf8i\x8b\x03\x87\xa3\xa3\xbf\xea\x18\x95;\xd1W\xe0\xa4F\xf4\xa5(\xc5B]\x0b\xacP\x8d\x11\x9c\xb7\'\xa3X\x99\xa6\x9c\x88\xc7\x89V1O\xc7;wie\xc3\xc4\x82}l\xe5\x109\x8d\xcc\xb8\xda \xfc`\xec\x17*B\xbaR\xb8T8Y!\x86\xd8W":-&\xcc\xbd\x8b\xc3\xc9\x10M\xda,\x05\xc6"\xcaBK\xf4k{\xa64D\xc1\xda\x11\xe7\'\xbaF=e\xafB\xf0\xa4A\x8b0B\xf0F\x81$A\xd7N]\xbe\xc0\xa2') __pyarmor__(__name__, __file__, b'PY000000\x00\x03\n\x00o\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x0b\x03\x00\x00\x12\t\x04\x00\xdc\xe7\xc9U\xb18\xad\xd9\x16\x89\xabv\xc3\x89<\xd9\x00\x00\x00\x00\x00\x00\x00\x00\xba\xf8n\xc6i\xeb\x0c\xbe\x1d\xea\x012\xef]\x04\x9d7\xa2\xe1\xfe\x82Z\xaf_\xd2\x835\xd1\xba\xa0j\x97\xef\xe39\x18\xe4\x01\xfb\xa9\xf7a\xb2\xd1\xed\n\x9b%q\xf6\x7fm\x8f\xd7&\xa7d\x04A\x90D\x7f?\xc2*\xb2\x05\x87\xf4*4\xbf\xcca\x80\xc1\xd8\xe5\x0fR\x04,~%8u\xf5\xe9\x97\xe2\x89!\xd2\xeb\xf06\xe2\xbatLu\xbdd\xede:\x9bz\xf7b*(\xf6\x13g\x8f\xfd\xfd\xaa\xa0\xd9\xc1\x03v\x9f\x7f\xb1\xe7\x87\xcb\xad\x99\xb8w\xfe}\x17\xed\xa3\x90\x07\xa9<H\x04\xbd\x8c\xf21E]\xba\xc8!\xa5\xa1\x16\xb34\xbf2g<\xea\n\xfe^\x18\xfbm\xc2\xa4\xe5\x0c\x05qn\xcc\xce}P\xedG\xe8\\\x16\xa1\xe8\x8d\x99\xaf\xf5\xc6\xc7:\xa9\xdc\xd9\x9d\xd6\x1c\x02\xd2\x96\xe3\xfa\xb6\xf5J\xe0\x0bV\xb2\xdfL\xff\xd3\xca\xecr9\xe8!@9\x8e\xe9r \xdd\xedY\x0b\x0f\x9b\x9f\xa6\xb9\x96\xba\x17w\x1cF\xbfT\x135\x193N\xb3\xcd\x04j\x82\xe4\xab\x8f\xb8\x13.\x82X\xdf\xea)\xf9M\xa1\xd0\x1b\xc8\xd4m1\x0e\x83\x86\xc9\x98OD\xa3/?\x11Z&\xfc\xd4\xf5\x02ky.\x8a\xdc\xb2\xa52`\xb2\xefo\xb2\xf4\x05a0\xb6\xad]FU`\xe3\xf4\x91\xd7\xdd\\\x14\xa5\xfc\x82\xe9\xfa\x1aY\xd2Q|\':\x83Hm0\xf1\x99\xe7\xdf]\xf2\x17\xe4\x82\x8c\xcd\x94Yl\x12)\xde\x1bE\x9c\x1f>\xa883\x12"\x96\xa3\x04\x1b\xb3\xb3\xf4\x9e\xb8\x96ot\x9aMJ\xcb\x15\x7f\x00=\x95\xc6\xeeX\x95\xc3~\xa4>\x07\xae,s\xcf\xac<\x84;\x15/{\xdf \xab0u1\x01\xb4\x87Z{\xa8\ro\x87\xca\x82X\xdcl\xbb\x89\xb1\xf9Y\xce\xe2\xa1p\xbf\x80O;o>&\x06\x7f\x88\xa5uHO\xa4\x9dxd\x0e\xa7/\xc4\xb6\xfb6\xdd\xad\xfc\xf3\x1e\xb1T)\xd7\xfb\xf58\xfd\x83\x88\x8c\n\x93\x8e\xabiV\xb7\xf6\xe9#\xba4b\xc8\xae-\x8cn\xf1\xbf\xc5\xce\xad\xf0C\x97vD3\xe7R-\xc07R\x8e\xa5\x1a\xa8\xf8\xcel\xd0\x9a)\xfd\xca/!B\xf9\x94\x08\x81\x86C\x14\xeb\x9e\xcd\xccg\xb4\xc7\xe0\x88\xa1ye58\xcc\x84p\xf6X5\x90Uq\x1d\xcf\xf1{\xad\x83\xe5\xcdjaUl\x93\x10\xc8\x13d:\xc7(,"\xdeU\xb5QI!qF\x08\x97`\xaa\xfb\xa7:{\xd3q\x01#\xf2\xbc@)\tb\xe7\xb8\x81\n\xac\xe3\x05\x8e:\x08o}8A{*\xf0\xf1M\x15\x98\x06M?dm\xf2S\xd5M]O?\xd7m\x9fM\x1b3$\x88:\x95,\x9d\x1a\xb8\xa4W\xe7/!p\x9bF4\xbbf\xb5\x9a,\xd8\xce\xf0\xe3\xad\xc5\x80\x9b\xbe\xb6\x86\x85\x1e\n@w\xf2\xab\xb7B\xdd-\xabhL\xff\xeeCY\xa1\xee\xde,\x8a\x99\x06,0\xc6\xca\xb5\xe6\xb1\x07R\xef\xda\xdf\xd4\xec|\x02\x0f\x01u*f\xf5\xff\x8e\x7f\xd0c\x9b.El\xbb\x05\xaf\xa7\xa1\x19\x82\xdd\xef\xee\x14\x18z\xd1\xfa\xc4\x11=\xec\x95\xdf+\x08\xf6\xdc\xa3p\x02\x93"e\xeb\xc8\x14\x9b.9\xb5;]\xe5\xb9{A\x9b\x99')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
# Pyarmor 9.0.7 (trial), 000000, non-profits, 2025-03-05T12:22:14.586715 # Pyarmor 9.0.7 (trial), 000000, non-profits, 2025-09-26T00:34:59.810850
from pyarmor_runtime_000000 import __pyarmor__ from pyarmor_runtime_000000 import __pyarmor__
__pyarmor__(__name__, __file__, b'PY000000\x00\x03\n\x00o\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x9a\x01\x00\x00\x12\t\x04\x00\xc2\x16\xeb\xdb\x01#\xb1\xd9\xc2\xe2\xf6A^\xf4\xda\x89\x00\x00\x00\x00\x00\x00\x00\x00s\r\xc6\x00\xc4\xb3\x81\'\xb2|yB\xebs\x12\xecdEy\x82S(\xce\xce\x18\xb7\x9e\x08\x99\xf4\xf0]\x863\xe9h\xd8\x98n9O\xf5\xe2\xbf\x16\xd6\xcb\x18p\xc7\xf5\x9b- \x1aY\xdb\xa2Iu\xd5\xe14\xa4As\xb8\xa3t\x1a\xbfB\xce%\x8au\x84\x8b=\x85\x054\x14|\x8c\x00]\xe2)R\xed\xae\xda>\x04U\xceJ:\xd8\x1dW\xfdf\xb6\x1b\x81F\x0b\xd0Oany\xf3\x1e\xf3k\x9f\xbb\xf9\x10i[y\xdbP;\x89\x0c\xa7\xb2\xa2\x91\xf1e\xd5\xd9\xce9fz\xbe\x03\x8dfq\x064\xf4\x1a\x99s\xbf\x08X\x82W\x1ey`^^X\x1ai\xef\xa3\x0bx\xf3\x16K\x106T\x0eOA\x10\xa7{\x9fV\x95\x9d\xc7\xbc\x1d(\xe57\x95\xea\xc4\x02w\x8bu\xe3\x15\x8ea\x7f\xe77\x96\x03\x8c\x08"\xf6\x92\xc3;1\xa8\x919w\xf6g\x1a\x15\xa6Ci\xec=\xee_\x03\x03\x97\xcfl\x9b\xfb\x00\xee\xd0\x1b\xb7\xdb\x9bh\xbe\xa9\x8dJjK\xe4\xab\xe8\xa3<3\x82\x9f\xb2\xb2\x1d\xda\xfd\x13\xb3\xcc\xd8z\xfce\x13#~e\x9a\x11bD\xe8\x91\x82V\xff\x15\xb6\xf3\x8c\xe6zw\xf9\xf5U\x88$\xbc\xef-T\x01\x91R\xec-\xfb;\x8f\xc2\xbdtI\x7f\x98\x88\xbe+\xcd\xca\x9f\x01v\x99T\x9a\x10iJ\xfdo\xdf\x03\xe8\xec\xb8,\x99\xd7\xea\x1c0j\xbc\x18\x9fO;\xdf\x89X\x94>"\xe6\xa6\xd7U\t\xaa\xca\xef\xf0\xda\xd8\xdf\xd6\xc2*\xb9C\xf8\xfcm#z\xc9:\x85+{5\xa3<\xcd\xb2\x94M/\x80\x823u\x06\x7fI\xae\xf5\xc2\xc1\x15\xaf\x9c\xc4Yq\x86|\xd0\x8a') __pyarmor__(__name__, __file__, b"PY000000\x00\x03\n\x00o\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x9a\x01\x00\x00\x12\t\x04\x00\xa9.\x02\x91|\x96\xea\x89\x92a\xc1\xc2p\xa2q&\x00\x00\x00\x00\x00\x00\x00\x00\xf3\x7f\x1bf\xe4\x1b\x00\xe7\x11\xe2\xd8g\xd5\x10\x1bHT\xd8+ok\xe9)6\xe9\xed\xf8X\xf6p\x08\xc9\x97\x00\xe7<o\x9a@\x1d\xb6&#\xb1\x01\xf5\xb6%\xb3\x84\x0c\xd5\xb5\xd8\x9b\x13\xb5\x8e\x89\xff\xe6[\x83]\xb3PrD\xc9\x86\x03\x83\xa3\xec\x96\xe2\x1a\x04\xeaX\xc4\x9fc\xb1Mx\x0183\xa0\xe1\x16G\x06EO\xe9:a/\xdb\x02\xb6\r\xe5\xde\x98@\x9a\xd0(\xd4\x0e0\xb4\xe3\xb4\xc6\x95[\xa9L{\xb2\xa7\x16\x17(\x86G\x13\xa8\xc6\xd6\xe5\xac\xe5\x13\x0f\x11 )\xe2K\x9c\x8c\xec\xda\x89w\xa3\x19$\xa7\xc1\xa4\xc8\x1e\x1d \x92\xeb\xe6\xc87E!\x87\x1c\xdd)<'\xa1A\x8b\xa1>\xeboW\xbe\x19&a\xa4\xb3\x0f\x1c\x05\xed\x80m\x05\xba.\xc8\xd8\xab\xb4\xf6s\x8e\x18C{\x97\x9dh*w\xe1\x1d%\xf7\x1f\x93\xe7\t\xefI\x1c\xef9#B\x82\xdb\x018\xfc\n\xd0\xe6\t\x9f+\x03\xe1\xf5:\x8b\x13\x8f\x19\x9e\x8f\x10l\xf8\xecsG\xbc\xa0\x1b\xe9\xbaWW\x8e\xfe\xe0\x1e\xa1\xb6)\n\xf9\xf1\xb9IR\xeb\x850\xc0D\x12\raJn\xe4\xe8n\xbd\xf5\x8c\xc5\x87\xcc\x97\x85\xf9\xbb,nxQ\xf7b6\xed\xa4\xb9\xaa\xe6\xc2\xb7R\x8f_P\xf7\xa4\x8c\xcd\xdb\xde\x89\x14\x94\xac\x1c\xcf\xd1\xa1\xf5`X7\xae\xf4\xc1f\xe3\xc2\x0e\xacO\xf0\xae\xef^9U\x8c\x82`\xac\xc8\x98\xbamg\xdb\xe1\xc0RY\xceU94\x08\xf0b+\xdc\x1b:N\xd5c\xae\xed\x06\xfd\xa1u\xcf|@\x8c\x89Q\xaag\xef\x7f\xcc(w\xf1\x97\x089\x15\\\xbf\xd4kNh\xcb\xb7\xb2f\x04")

440
templates/index _new.html Normal file
View File

@ -0,0 +1,440 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
<title>Media Dashboard</title>
<link rel="icon" type="image/x-icon" href="../static/images/favicon.ico" />
<link rel="stylesheet" href="../static/css/styles.css" />
<link rel="stylesheet" href="../static/fontawesome/css/all.min.css" />
<style>
/* High-contrast text everywhere */
body,
.content,
h1, h2, h3, h4, h5, h6,
p, label, .status,
.checkbox-inline,
.container,
.form-group,
.form-group * {
color: #1f2937; /* gray-800 */
}
*{ box-sizing: border-box; }
html, body{ height: 100%; }
body{
margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
line-height: 1.55;
/* Page/background image/color is controlled by your existing CSS; unchanged */
}
#navbar{ position: sticky; top: 0; z-index: 50; }
.content-wrapper{
position: relative; /* anchor for the underlay image */
max-width: 1100px;
margin: 0 auto;
padding: 16px;
z-index: 1; /* keep main content above the underlay */
}
@media (min-width: 980px){
.content-wrapper{ padding: 24px; }
}
.content{
position: relative;
z-index: 2; /* ensure UI is above the image */
display: flex;
flex-direction: column; /* stack containers vertically */
align-items: center; /* center containers horizontally */
gap: 20px;
}
/* Ensure the main title spans full width and is centered above the panels */
.content > h1{
flex-basis: 100%;
text-align: center;
margin-bottom: 8px;
}
/* Underlay image: left-aligned, same size, no layout impact */
.side-img{
position: absolute;
left: 0;
top: 110px; /* adjust as you like */
width: 230px; /* keep current size */
height: auto;
z-index: 0; /* behind everything */
pointer-events: none; /* clicks go through */
opacity: 1; /* fully visible; change if you want subtler */
}
h1{
margin: 0 0 8px;
font-size: clamp(1.6rem, 2.2vw, 2.2rem);
color: #333; /* explicit, high-contrast */
}
h2{
margin: 0 0 10px;
font-size: clamp(1.1rem, 1.8vw, 1.4rem);
color: #333;
}
/* Card containers background color kept EXACTLY as before */
.container{
width: 100%;
max-width: 420px;
margin: 0 0 16px;
padding: 16px;
border: 2px solid #ccc;
border-radius: 10px;
background-color: #ffffffb6; /* unchanged */
position: relative; /* creates its own stacking context above image */
z-index: 1;
}
.form-group{ margin-bottom: 14px; }
.form-group label{ display: block; margin-bottom: 6px; font-weight: 600; }
.form-group input,
.form-group select,
.form-group textarea{
width: 100%;
padding: 10px 12px;
font-size: 1rem;
border: 1px solid #d1d5db;
border-radius: 8px;
background: #fff;
color: #1f2937;
}
.checkbox-row{
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
flex-wrap: wrap;
}
.checkbox-inline{
display: inline-flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
.button-group{
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-start;
margin: 12px 0 6px;
}
.button-row{
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 12px;
}
.button-row .controls{ display:flex; gap:10px; }
.button-row .actions{ margin-left: auto; }
.btn{
appearance: none;
border: 1px solid transparent;
border-radius: 8px;
padding: 10px 16px;
font-size: 1rem;
cursor: pointer;
color: #fff; /* keep buttons readable */
background: #1e40af; /* blue-800 */
display: inline-flex;
align-items: center;
gap: 8px;
text-decoration: none;
}
.btn:hover{ background: #15327f; }
.btn:focus-visible{
outline: 3px solid #2563eb;
outline-offset: 2px;
}
.btn[disabled]{ opacity: 0.6; cursor: not-allowed; }
.status{
margin-top: 8px;
font-size: 0.98rem;
min-height: 1.2em;
}
.input-label{ text-align: left; }
.form-group textarea{
resize: none;
overflow-wrap: break-word;
white-space: pre-wrap;
}
/* Optional: make sure the underlay doesn't collide on very small screens */
@media (max-width: 480px){
.side-img{ top: 140px; width: 190px; }
}
</style>
</head>
<body>
<div id="navbar"></div>
<script>
fetch('/static/html/nav.html')
.then(r => r.ok ? r.text() : Promise.reject(r.status))
.then(html => { document.getElementById('navbar').innerHTML = html; })
.catch(() => { document.getElementById('navbar').innerHTML = '<div class="container" role="alert">Navigation failed to load.</div>'; });
</script>
<div class="background-image"></div>
<main class="content-wrapper">
<!-- Underlay image (beneath everything, left-aligned, fixed size) -->
<img src="/static/images/helio-posh.png" alt="Helio Posh" class="side-img" />
<div class="content">
<h1>Media Dashboard</h1>
<section class="container" aria-labelledby="playlist-heading">
<h2 id="playlist-heading">Playlist Loop</h2>
<div class="checkbox-row">
<label class="checkbox-inline" for="autoPlayAtBoot">
<input type="checkbox" id="autoPlayAtBoot" name="autoPlayAtBoot">
Autostart @ Boot
</label>
<label class="checkbox-inline" for="saveSettings">
<input type="checkbox" id="saveSettings" name="saveSettings">
Save
</label>
</div>
<div class="form-group">
<label class="input-label" for="mediaLocation">Media Sources</label>
<select id="mediaLocation" name="mediaLocation" aria-describedby="mediaHelp">
<option value="USB">No Media Available</option>
</select>
<div id="mediaHelp" class="sr-only">Choose a folder to loop through images.</div>
</div>
<div class="form-group">
<label class="input-label" for="imageDuration">Image Duration (secs)</label>
<input type="number" id="imageDuration" name="imageDuration" min="1" max="60" inputmode="numeric">
</div>
<div class="button-group" role="group" aria-label="Playlist loop controls">
<button class="btn" id="btnStartLoop" onclick="startMediaLoop()">
<i class="fa fa-play" aria-hidden="true"></i> Start
</button>
<button class="btn" id="btnStopLoop" onclick="stopMediaLoop()">
<i class="fa fa-stop" aria-hidden="true"></i> Stop
</button>
</div>
<div class="status" id="mediaLoopStatus" role="status" aria-live="polite">status</div>
</section>
<section class="container" aria-labelledby="gallery-heading">
<h2 id="gallery-heading">Web Gallery</h2>
<div class="checkbox-row" style="justify-content:flex-start;">
<label class="checkbox-inline" for="autoStart">
<input type="checkbox" id="autoStart" name="autoStart">
Autostart
</label>
</div>
<div class="form-group">
<label class="input-label" for="galleryURL">URL</label>
<textarea id="galleryURL" name="galleryURL" rows="3" placeholder="https://yahoo.com"></textarea>
</div>
<div class="button-row" role="group" aria-label="Web gallery controls">
<div class="controls">
<button class="btn" id="btnStartWeb" onclick="startWebGallery()">
<i class="fa fa-play" aria-hidden="true"></i> Start
</button>
<button class="btn" id="btnStopWeb" onclick="stopWebGallery()">
<i class="fa fa-stop" aria-hidden="true"></i> Stop
</button>
</div>
<div class="actions">
<button class="btn" type="button" onclick="clearURL()">
<i class="fa fa-eraser" aria-hidden="true"></i> Clear
</button>
</div>
</div>
<div class="status" id="webGalleryStatus" role="status" aria-live="polite">status</div>
</section>
</div>
</main>
<script>
function setBusy(el, busy){
if (!el) return;
el.disabled = !!busy;
if (busy) el.setAttribute('aria-busy','true'); else el.removeAttribute('aria-busy');
}
function setText(id, text){
const el = document.getElementById(id);
if (el) el.textContent = text || '';
}
async function loadMediaSources(){
try{
const res = await fetch('../get_media_sources');
const data = await res.json();
const select = document.getElementById('mediaLocation');
if (!select) return;
// Clear existing
select.innerHTML = '';
const arr = Array.isArray(data?.folders) ? data.folders : [];
if (arr.length === 0){
const opt = document.createElement('option');
opt.value = 'USB';
opt.text = 'No Media Available';
select.add(opt);
return;
}
arr.forEach(m => {
const opt = document.createElement('option');
opt.text = m.folder_name_display;
opt.value = m.folder_path;
select.add(opt);
});
}catch(err){
console.error('Error loading media sources:', err);
}
}
async function loadLastUsedData(){
try{
const res = await fetch('../get_screen_settings');
const data = await res.json();
// Only default if undefined (avoid forcing true when false)
const apb = (data.autoPlayAtBoot === undefined) ? true : !!data.autoPlayAtBoot;
const dur = (data.imageDuration === undefined) ? 5 : Number(data.imageDuration);
const as = (data.autoStart === undefined) ? true : !!data.autoStart;
const url = (data.url === undefined) ? 'google.com' : String(data.url);
document.getElementById('autoPlayAtBoot').checked = apb;
document.getElementById('imageDuration').value = dur;
document.getElementById('autoStart').checked = as;
document.getElementById('galleryURL').value = url;
document.getElementById('saveSettings').checked = false;
}catch(err){
console.error('Error loading screen settings:', err);
}
}
async function startMediaLoop(){
const mediaLocation = document.getElementById('mediaLocation').value;
const imageDuration = document.getElementById('imageDuration').value;
const autoPlayAtBoot = document.getElementById('autoPlayAtBoot').checked;
const saveSettings = document.getElementById('saveSettings').checked;
// Reset the save toggle after reading
document.getElementById('saveSettings').checked = false;
const btnStart = document.getElementById('btnStartLoop');
const btnStop = document.getElementById('btnStopLoop');
setBusy(btnStart, true); setBusy(btnStop, true);
setText('mediaLoopStatus', 'Starting media loop…');
try{
const res = await fetch('../start_media_loop', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mediaLocation, imageDuration, autoPlayAtBoot, saveSettings })
});
const data = await res.json();
setText('mediaLoopStatus', data.message || 'Started.');
}catch(err){
console.error('Error during start media loop:', err);
setText('mediaLoopStatus', 'Error during start media loop.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
async function stopMediaLoop(){
const btnStart = document.getElementById('btnStartLoop');
const btnStop = document.getElementById('btnStopLoop');
setBusy(btnStart, true); setBusy(btnStop, true);
setText('mediaLoopStatus', 'Stopping media loop…');
try{
const res = await fetch('../stop_media_loop', { method: 'POST' });
const data = await res.json();
setText('mediaLoopStatus', data.message || 'Stopped.');
}catch(err){
console.error('Error during stop media loop:', err);
setText('mediaLoopStatus', 'Error during stop media loop.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
async function startWebGallery(){
const autoStart = document.getElementById('autoStart').checked;
const url = document.getElementById('galleryURL').value;
const btnStart = document.getElementById('btnStartWeb');
const btnStop = document.getElementById('btnStopWeb');
setBusy(btnStart, true); setBusy(btnStop, true);
setText('webGalleryStatus', 'Starting web gallery…');
try{
const res = await fetch('../start_web_gallery', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url, autoStart })
});
const data = await res.json();
setText('webGalleryStatus', data.message || 'Started.');
}catch(err){
console.error('Error during start web gallery:', err);
setText('webGalleryStatus', 'Error during start web gallery.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
async function stopWebGallery(){
const btnStart = document.getElementById('btnStartWeb');
const btnStop = document.getElementById('btnStopWeb');
setBusy(btnStart, true); setBusy(btnStop, true);
setText('webGalleryStatus', 'Stopping web gallery…');
try{
const res = await fetch('../stop_web_gallery', { method: 'POST' });
const data = await res.json();
setText('webGalleryStatus', data.message || 'Stopped.');
}catch(err){
console.error('Error during stop web gallery:', err);
setText('webGalleryStatus', 'Error during stop web gallery.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
function clearURL(){ document.getElementById('galleryURL').value = ''; }
document.addEventListener('DOMContentLoaded', () => {
loadLastUsedData();
loadMediaSources();
});
</script>
</body>
</html>

View File

@ -1,283 +1,451 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta
<title>Media Dashboard</title> name="viewport"
<link rel="icon" type="image/x-icon" href="../static/images/favicon.ico"> content="width=device-width, initial-scale=1"
<link rel="stylesheet" href="../static/css/styles.css"> />
<link rel="stylesheet" href="../static/fontawesome/css/all.min.css"> <title>Media Dashboard</title>
<style> <link rel="icon" type="image/x-icon" href="../static/images/favicon.ico" />
<link rel="stylesheet" href="../static/css/styles.css" />
<link rel="stylesheet" href="../static/fontawesome/css/all.min.css" />
<style>
/* High-contrast text everywhere */
body,
.content,
h1, h2, h3, h4, h5, h6,
p, label, .status,
.checkbox-inline,
.container,
.form-group,
.form-group * {
color: #1f2937; /* gray-800 */
}
h1 { *{ box-sizing: border-box; }
margin-top: 20px; html, body{ height: 100%; }
font-size: 36px; body{
color: #333; margin: 0;
} font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
h2 { line-height: 1.55;
margin-top: 2px; /* Page/background image/color is controlled by your existing CSS; unchanged */
font-size: 24px; }
color: #333;
} #navbar{ position: sticky; top: 0; z-index: 50; }
.container {
width: 80%; .content-wrapper{
max-width: 300px; position: relative; /* anchor for the underlay image */
margin: 20px auto; max-width: 1100px;
padding: 20px; margin: 0 auto;
border: 2px solid #ccc; padding: 16px;
border-radius: 10px; z-index: 1; /* keep main content above the underlay */
background-color: #ffffffb6; }
position: relative; @media (min-width: 980px){
} .content-wrapper{ padding: 24px; }
.form-group { }
margin-bottom: 20px;
} .content{
.form-group label { position: relative;
display: block; z-index: 2; /* ensure UI is above the image */
margin-bottom: 5px; display: flex;
font-weight: bold; flex-direction: column; /* stack containers vertically */
} align-items: center; /* center containers horizontally */
.form-group input, .form-group select { gap: 20px;
width: 100%; }
padding: 8px;
font-size: 16px; /* Ensure the main title spans full width and is centered above the panels */
box-sizing: border-box; .content > h1{
} flex-basis: 100%;
.checkbox-group { text-align: center;
display: flex; margin-bottom: 8px;
align-items: center; }
margin-bottom: 20px;
} /* Underlay image: left-aligned, same size, no layout impact */
.checkbox-group input { .side-img{
margin-right: 10px; position: absolute;
} left: 0;
.button-group { top: 110px; /* adjust as you like */
display: flex; width: 230px; /* keep current size */
justify-content: space-around; height: auto;
margin-top: 20px; z-index: 0; /* behind everything */
} pointer-events: none; /* clicks go through */
.button-group .btn { opacity: 1; /* fully visible; change if you want subtler */
padding: 10px 20px; }
font-size: 18px;
cursor: pointer; h1{
background-color: #007bff; margin: 0 0 8px;
color: white; font-size: clamp(1.6rem, 2.2vw, 2.2rem);
border: none; color: #333; /* explicit, high-contrast */
border-radius: 5px; }
text-decoration: none; h2{
} margin: 0 0 10px;
.button-group .btn:hover { font-size: clamp(1.1rem, 1.8vw, 1.4rem);
background-color: #0056b3; color: #333;
} }
.status {
margin-top: 10px; /* Card containers background color kept EXACTLY as before */
font-size: 16px; .container{
color: #555; width: 100%;
} max-width: 420px;
.side-img { margin: 0 0 16px;
position: absolute; padding: 16px;
top: 80px; border: 2px solid #ccc;
left: 0; border-radius: 10px;
width: 230px; /* Set desired width */ background-color: #ffffffb6; /* unchanged */
height: auto; /* Adjust height automatically to maintain aspect ratio */ position: relative; /* creates its own stacking context above image */
z-index: -1; /* Ensure image is behind other content */ z-index: 1;
} }
.input-label{
text-align: left; .form-group{ margin-bottom: 14px; }
} .form-group label{ display: block; margin-bottom: 6px; font-weight: 600; }
.form-group textarea { .form-group input,
width: 100%; .form-group select,
padding: 8px; .form-group textarea{
font-size: 16px; width: 100%;
box-sizing: border-box; padding: 10px 12px;
resize: none; /* Prevents the user from resizing the textarea */ font-size: 1rem;
overflow-wrap: break-word; /* Ensures that long URLs wrap to the next line */ border: 1px solid #d1d5db;
white-space: pre-wrap; /* Preserves whitespace and wraps text as necessary */ border-radius: 8px;
} background: #fff;
</style> color: #1f2937;
}
.checkbox-row{
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
flex-wrap: wrap;
}
.checkbox-inline{
display: inline-flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
/* utility: push an inline checkbox/label to the far right inside a flex row */
.checkbox-inline.right { margin-left: auto; }
.button-group{
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-start;
margin: 12px 0 6px;
}
.button-row{
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 12px;
}
.button-row .controls{ display:flex; gap:10px; }
.button-row .actions{ margin-left: auto; }
.btn{
appearance: none;
border: 1px solid transparent;
border-radius: 8px;
padding: 10px 16px;
font-size: 1rem;
cursor: pointer;
color: #fff; /* keep buttons readable */
background: #1e40af; /* blue-800 */
display: inline-flex;
align-items: center;
gap: 8px;
text-decoration: none;
}
.btn:hover{ background: #15327f; }
.btn:focus-visible{
outline: 3px solid #2563eb;
outline-offset: 2px;
}
.btn[disabled]{ opacity: 0.6; cursor: not-allowed; }
.status{
margin-top: 8px;
font-size: 0.98rem;
min-height: 1.2em;
}
.input-label{ text-align: left; }
.form-group textarea{
resize: none;
overflow-wrap: break-word;
white-space: pre-wrap;
}
/* Optional: make sure the underlay doesn't collide on very small screens */
@media (max-width: 480px){
.side-img{ top: 140px; width: 190px; }
}
</style>
</head> </head>
<body> <body>
<div id="navbar"></div> <div id="navbar"></div>
<script> <script>
fetch('/static/html/nav.html') fetch('/static/html/nav.html')
.then(response => response.text()) .then(r => r.ok ? r.text() : Promise.reject(r.status))
.then(data => { .then(html => { document.getElementById('navbar').innerHTML = html; })
document.getElementById('navbar').innerHTML = data; .catch(() => { document.getElementById('navbar').innerHTML = '<div class="container" role="alert">Navigation failed to load.</div>'; });
}); </script>
</script>
<div class="background-image"></div> <div class="background-image"></div>
<!--<div><img src="/static/images/helio-posh.png" alt="Helio Posh" class="side-img"></div>--> <main class="content-wrapper">
<div class="content-wrapper"> <!-- Underlay image (beneath everything, left-aligned, fixed size) -->
<div class="content"> <img src="/static/images/helio-posh.png" alt="Helio Posh" class="side-img" />
<div> <div class="content">
<img src="/static/images/helio-posh.png" alt="Helio Posh" class="side-img"> <h1>Media Dashboard</h1>
</div>
<h1>Media Dashboard</h1> <section class="container" aria-labelledby="playlist-heading">
<div class="container"> <h2 id="playlist-heading">Playlist Loop</h2>
<h2>Playlist Loop</h2>
<div class="checkbox-group"> <div class="checkbox-row">
<input type="checkbox" id="autoPlayAtBoot" name="autoPlayAtBoot" checked> <label class="checkbox-inline" for="autoPlayAtBoot">
<label for="autoPlayAtBoot">Autostart @Boot</label> <input type="checkbox" id="autoPlayAtBoot" name="autoPlayAtBoot">
</div> Autostart @ Boot
<div class="form-group"> </label>
<label class="input-label" for="mediaLocation">Media Sources:</label>
<select id="mediaLocation" name="mediaLocation"> <label class="checkbox-inline" for="saveSettings">
<option value="USB">No Media Available</option> <input type="checkbox" id="saveSettings" name="saveSettings">
</select> Save
</div> </label>
<div class="form-group">
<label class="input-label" for="imageDuration">Image Duration in Secs:</label>
<input type="number" id="imageDuration" name="imageDuration" min="1" max="60">
</div>
<div class="button-group">
<button class="btn" onclick="startMediaLoop()">Start</button>
<button class="btn" onclick="stopMediaLoop()">Stop</button>
</div>
<div class="status" id="mediaLoopStatus">status</div>
</div>
<div class="container">
<h2>Web Gallery</h2>
<div class="checkbox-group">
<input type="checkbox" id="autoStart" name="autoStart" checked>
<label for="autoStart">Autostart</label>
</div>
<div class="form-group">
<button class="btn" onclick="clearURL()">Clear</button>
<label class="input-label" for="galleryURL">URL:</label>
<textarea id="galleryURL" name="galleryURL" rows="3"></textarea>
</div>
<div class="button-group">
<button class="btn" onclick="startWebGallery()">Start</button>
<button class="btn" onclick="stopWebGallery()">Stop</button>
</div>
<div class="status" id="webGalleryStatus">status</div>
</div>
</div> </div>
<div class="form-group">
<label class="input-label" for="mediaLocation">Media Sources</label>
<select id="mediaLocation" name="mediaLocation" aria-describedby="mediaHelp">
<option value="USB">No Media Available</option>
</select>
<div id="mediaHelp" class="sr-only">Choose a folder to loop through images.</div>
</div>
<div class="form-group">
<label class="input-label" for="imageDuration">Image Duration (secs)</label>
<input type="number" id="imageDuration" name="imageDuration" min="1" max="60" inputmode="numeric">
</div>
<div class="button-group" role="group" aria-label="Playlist loop controls">
<button class="btn" id="btnStartLoop" onclick="startMediaLoop()">
<i class="fa fa-play" aria-hidden="true"></i> Start
</button>
<button class="btn" id="btnStopLoop" onclick="stopMediaLoop()">
<i class="fa fa-stop" aria-hidden="true"></i> Stop
</button>
</div>
<div class="status" id="mediaLoopStatus" role="status" aria-live="polite">status</div>
</section>
<section class="container" aria-labelledby="gallery-heading">
<h2 id="gallery-heading">Web Gallery</h2>
<div class="checkbox-row" style="justify-content:flex-start;">
<label class="checkbox-inline" for="autoStart">
<input type="checkbox" id="autoStart" name="autoStart">
Autostart
</label>
<label class="checkbox-inline right" for="saveWebSettings">
<input type="checkbox" id="saveWebSettings" name="saveWebSettings">
Save
</label>
</div>
<div class="form-group">
<label class="input-label" for="galleryURL">URL</label>
<textarea id="galleryURL" name="galleryURL" rows="3" placeholder="https://yahoo.com"></textarea>
</div>
<div class="button-row" role="group" aria-label="Web gallery controls">
<div class="controls">
<button class="btn" id="btnStartWeb" onclick="startWebGallery()">
<i class="fa fa-play" aria-hidden="true"></i> Start
</button>
<button class="btn" id="btnStopWeb" onclick="stopWebGallery()">
<i class="fa fa-stop" aria-hidden="true"></i> Stop
</button>
</div>
<div class="actions">
<button class="btn" type="button" onclick="clearURL()">
<i class="fa fa-eraser" aria-hidden="true"></i> Clear
</button>
</div>
</div>
<div class="status" id="webGalleryStatus" role="status" aria-live="polite">status</div>
</section>
</div> </div>
</main>
<script>
function setBusy(el, busy){
if (!el) return;
el.disabled = !!busy;
if (busy) el.setAttribute('aria-busy','true'); else el.removeAttribute('aria-busy');
}
function setText(id, text){
const el = document.getElementById(id);
if (el) el.textContent = text || '';
}
<script> async function loadMediaSources(){
function loadMediaSources() { try{
fetch('../get_media_sources') const res = await fetch('../get_media_sources');
.then(response => response.json()) const data = await res.json();
.then(data => { const select = document.getElementById('mediaLocation');
media_sources = data.folders; // Access the "folders" array in the JSON if (!select) return;
const mediaLocationSelect = document.getElementById('mediaLocation'); // Clear existing
select.innerHTML = '';
// Clear any existing options const arr = Array.isArray(data?.folders) ? data.folders : [];
while (mediaLocationSelect.options.length > 0) { if (arr.length === 0){
mediaLocationSelect.remove(0); const opt = document.createElement('option');
} opt.value = 'USB';
opt.text = 'No Media Available';
// Add new options select.add(opt);
media_sources.forEach(media => { return;
var option = document.createElement('option');
option.text = media.folder_name_display;
option.value = media.folder_path;
mediaLocationSelect.add(option);
});
})
.catch(error => {
console.error("Error loading media sources:", error);
});
} }
arr.forEach(m => {
const opt = document.createElement('option');
opt.text = m.folder_name_display;
opt.value = m.folder_path;
select.add(opt);
});
}catch(err){
console.error('Error loading media sources:', err);
}
}
function loadLastUsedData() { async function loadLastUsedData(){
fetch('../get_screen_settings') try{
.then(response => response.json()) const res = await fetch('../get_screen_settings');
.then(data => { const data = await res.json();
console.info(data)
document.getElementById('autoPlayAtBoot').checked = data.autoPlayAtBoot || true;
document.getElementById('imageDuration').value = data.imageDuration || 5;
document.getElementById('autoStart').checked = data.autoStart || true;
document.getElementById('galleryURL').value = data.url || 'google.com';
})
.catch(error => {
console.error("Error loading screen settings:", error);
});
}
function startMediaLoop() { // Only default if undefined (avoid forcing true when false)
const mediaLocation = document.getElementById('mediaLocation').value; const apb = (data.autoPlayAtBoot === undefined) ? true : !!data.autoPlayAtBoot;
const imageDuration = document.getElementById('imageDuration').value; const dur = (data.imageDuration === undefined) ? 5 : Number(data.imageDuration);
const autoPlayAtBoot = document.getElementById('autoPlayAtBoot').checked; const as = (data.autoStart === undefined) ? true : !!data.autoStart;
const url = (data.url === undefined) ? 'google.com' : String(data.url);
fetch('../start_media_loop', { document.getElementById('autoPlayAtBoot').checked = apb;
method: 'POST', document.getElementById('imageDuration').value = dur;
headers: { document.getElementById('autoStart').checked = as;
'Content-Type': 'application/json' document.getElementById('galleryURL').value = url;
}, document.getElementById('saveSettings').checked = false;
body: JSON.stringify({ mediaLocation: mediaLocation, imageDuration: imageDuration , autoPlayAtBoot: autoPlayAtBoot}) }catch(err){
}) console.error('Error loading screen settings:', err);
.then(response => response.json()) }
.then(data => { }
document.getElementById('mediaLoopStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during start media loop:", error);
document.getElementById('mediaLoopStatus').innerText = "Error during start media loop.";
});
}
function stopMediaLoop() { async function startMediaLoop(){
fetch('../stop_media_loop', { const mediaLocation = document.getElementById('mediaLocation').value;
method: 'POST' const imageDuration = document.getElementById('imageDuration').value;
}) const autoPlayAtBoot = document.getElementById('autoPlayAtBoot').checked;
.then(response => response.json()) const saveSettings = document.getElementById('saveSettings').checked;
.then(data => {
document.getElementById('mediaLoopStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during stop media loop:", error);
document.getElementById('mediaLoopStatus').innerText = "Error during stop media loop.";
});
}
function startWebGallery() { // Reset the save toggle after reading
const autoStart = document.getElementById('autoStart').checked; document.getElementById('saveSettings').checked = false;
const url = document.getElementById('galleryURL').value
fetch('../start_web_gallery', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: url, autoStart: autoStart })
})
.then(response => response.json())
.then(data => {
document.getElementById('webGalleryStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during start web gallery:", error);
document.getElementById('webGalleryStatus').innerText = "Error during start web gallery.";
});
}
function stopWebGallery() { const btnStart = document.getElementById('btnStartLoop');
fetch('../stop_web_gallery', { const btnStop = document.getElementById('btnStopLoop');
method: 'POST' setBusy(btnStart, true); setBusy(btnStop, true);
}) setText('mediaLoopStatus', 'Starting media loop…');
.then(response => response.json())
.then(data => {
document.getElementById('webGalleryStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during stop web gallery:", error);
document.getElementById('webGalleryStatus').innerText = "Error during stop web gallery.";
});
}
function clearURL() { try{
document.getElementById('galleryURL').value = ''; const res = await fetch('../start_media_loop', {
} method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mediaLocation, imageDuration, autoPlayAtBoot, saveSettings })
});
const data = await res.json();
setText('mediaLoopStatus', data.message || 'Started.');
}catch(err){
console.error('Error during start media loop:', err);
setText('mediaLoopStatus', 'Error during start media loop.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
document.addEventListener('DOMContentLoaded', loadLastUsedData); async function stopMediaLoop(){
const btnStart = document.getElementById('btnStartLoop');
const btnStop = document.getElementById('btnStopLoop');
setBusy(btnStart, true); setBusy(btnStop, true);
setText('mediaLoopStatus', 'Stopping media loop…');
try{
const res = await fetch('../stop_media_loop', { method: 'POST' });
const data = await res.json();
setText('mediaLoopStatus', data.message || 'Stopped.');
}catch(err){
console.error('Error during stop media loop:', err);
setText('mediaLoopStatus', 'Error during stop media loop.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
// Call the function to load media sources when the page loads async function startWebGallery(){
window.onload = loadMediaSources; const autoStart = document.getElementById('autoStart').checked;
</script> const url = document.getElementById('galleryURL').value;
const btnStart = document.getElementById('btnStartWeb');
const btnStop = document.getElementById('btnStopWeb');
const saveWebSettings = document.getElementById('saveWebSettings').checked;
setBusy(btnStart, true); setBusy(btnStop, true);
setText('webGalleryStatus', 'Starting web gallery…');
try{
const res = await fetch('../start_web_gallery', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url, autoStart, saveWebSettings })
});
const data = await res.json();
setText('webGalleryStatus', data.message || 'Started.');
}catch(err){
console.error('Error during start web gallery:', err);
setText('webGalleryStatus', 'Error during start web gallery.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
document.getElementById('saveWebSettings').checked = false;
}
}
async function stopWebGallery(){
const btnStart = document.getElementById('btnStartWeb');
const btnStop = document.getElementById('btnStopWeb');
setBusy(btnStart, true); setBusy(btnStop, true);
setText('webGalleryStatus', 'Stopping web gallery…');
try{
const res = await fetch('../stop_web_gallery', { method: 'POST' });
const data = await res.json();
setText('webGalleryStatus', data.message || 'Stopped.');
}catch(err){
console.error('Error during stop web gallery:', err);
setText('webGalleryStatus', 'Error during stop web gallery.');
}finally{
setBusy(btnStart, false); setBusy(btnStop, false);
}
}
function clearURL(){ document.getElementById('galleryURL').value = ''; }
document.addEventListener('DOMContentLoaded', () => {
loadLastUsedData();
loadMediaSources();
});
</script>
</body> </body>
</html> </html>

313
templates/index_orig.html Normal file
View File

@ -0,0 +1,313 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Media Dashboard</title>
<link rel="icon" type="image/x-icon" href="../static/images/favicon.ico">
<link rel="stylesheet" href="../static/css/styles.css">
<link rel="stylesheet" href="../static/fontawesome/css/all.min.css">
<style>
h1 {
margin-top: 20px;
font-size: 36px;
color: #333;
}
h2 {
margin-top: 2px;
font-size: 24px;
color: #333;
}
.container {
width: 80%;
max-width: 300px;
margin: 20px auto;
padding: 20px;
border: 2px solid #ccc;
border-radius: 10px;
background-color: #ffffffb6;
position: relative;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input, .form-group select {
width: 100%;
padding: 8px;
font-size: 16px;
box-sizing: border-box;
}
.checkbox-group {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.checkbox-group input {
margin-right: 10px;
}
.checkbox-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.checkbox-inline {
display: flex;
align-items: center;
}
.right-align {
margin-left: auto;
}
.button-group {
display: flex;
justify-content: space-around;
margin-top: 20px;
}
.button-group .btn {
padding: 10px 20px;
font-size: 18px;
cursor: pointer;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
}
.button-group .btn:hover {
background-color: #0056b3;
}
.status {
margin-top: 10px;
font-size: 16px;
color: #555;
}
.side-img {
position: absolute;
top: 80px;
left: 0;
width: 230px; /* Set desired width */
height: auto; /* Adjust height automatically to maintain aspect ratio */
z-index: -1; /* Ensure image is behind other content */
}
.input-label{
text-align: left;
}
.form-group textarea {
/* Ensure the main title spans full width and is centered above the panels */
.content > h1{
flex-basis: 100%;
text-align: center;
margin-bottom: 8px;
}
width: 100%;
padding: 8px;
font-size: 16px;
box-sizing: border-box;
resize: none; /* Prevents the user from resizing the textarea */
overflow-wrap: break-word; /* Ensures that long URLs wrap to the next line */
white-space: pre-wrap; /* Preserves whitespace and wraps text as necessary */
}
</style>
</head>
<body>
<div id="navbar"></div>
<script>
fetch('/static/html/nav.html')
.then(response => response.text())
.then(data => {
document.getElementById('navbar').innerHTML = data;
});
</script>
<div class="background-image"></div>
<!--<div><img src="/static/images/helio-posh.png" alt="Helio Posh" class="side-img"></div>-->
<div class="content-wrapper">
<div class="content">
<div>
<img src="/static/images/helio-posh.png" alt="Helio Posh" class="side-img">
</div>
<h1>Media Dashboard</h1>
<div class="container">
<h2>Playlist Loop</h2>
<div class="checkbox-row">
<div class="checkbox-inline">
<input type="checkbox" id="autoPlayAtBoot" name="autoPlayAtBoot" unchecked>
<label for="autoPlayAtBoot">Autostart @Boot</label>
</div>
<div class="checkbox-inline right-align">
<input type="checkbox" id="saveSettings" name="saveSettings" unchecked>
<label for="saveSettings">Save</label>
</div>
</div>
<div class="form-group">
<label class="input-label" for="mediaLocation">Media Sources:</label>
<select id="mediaLocation" name="mediaLocation">
<option value="USB">No Media Available</option>
</select>
</div>
<div class="form-group">
<label class="input-label" for="imageDuration">Image Duration in Secs:</label>
<input type="number" id="imageDuration" name="imageDuration" min="1" max="60">
</div>
<div class="button-group">
<button class="btn" onclick="startMediaLoop()">Start</button>
<button class="btn" onclick="stopMediaLoop()">Stop</button>
</div>
<div class="status" id="mediaLoopStatus">status</div>
</div>
<div class="container">
<h2>Web Gallery</h2>
<div class="checkbox-group">
<input type="checkbox" id="autoStart" name="autoStart" unchecked>
<label for="autoStart">Autostart</label>
</div>
<div class="form-group">
<button class="btn" onclick="clearURL()">Clear</button>
<label class="input-label" for="galleryURL">URL:</label>
<textarea id="galleryURL" name="galleryURL" rows="3"></textarea>
</div>
<div class="button-group">
<button class="btn" onclick="startWebGallery()">Start</button>
<button class="btn" onclick="stopWebGallery()">Stop</button>
</div>
<div class="status" id="webGalleryStatus">status</div>
</div>
</div>
</div>
<script>
function loadMediaSources() {
fetch('../get_media_sources')
.then(response => response.json())
.then(data => {
media_sources = data.folders; // Access the "folders" array in the JSON
const mediaLocationSelect = document.getElementById('mediaLocation');
// Clear any existing options
while (mediaLocationSelect.options.length > 0) {
mediaLocationSelect.remove(0);
}
// Add new options
media_sources.forEach(media => {
var option = document.createElement('option');
option.text = media.folder_name_display;
option.value = media.folder_path;
mediaLocationSelect.add(option);
});
})
.catch(error => {
console.error("Error loading media sources:", error);
});
}
function loadLastUsedData() {
fetch('../get_screen_settings')
.then(response => response.json())
.then(data => {
console.info(data)
document.getElementById('autoPlayAtBoot').checked = data.autoPlayAtBoot || true;
document.getElementById('imageDuration').value = data.imageDuration || 5;
document.getElementById('autoStart').checked = data.autoStart || true;
document.getElementById('galleryURL').value = data.url || 'google.com';
document.getElementById('saveSettings').checked = false;
})
.catch(error => {
console.error("Error loading screen settings:", error);
});
}
function startMediaLoop() {
const mediaLocation = document.getElementById('mediaLocation').value;
const imageDuration = document.getElementById('imageDuration').value;
const autoPlayAtBoot = document.getElementById('autoPlayAtBoot').checked;
const saveSettings = document.getElementById('saveSettings').checked;
document.getElementById('saveSettings').checked = false;
fetch('../start_media_loop', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ mediaLocation: mediaLocation, imageDuration: imageDuration , autoPlayAtBoot: autoPlayAtBoot, saveSettings: saveSettings})
})
.then(response => response.json())
.then(data => {
document.getElementById('mediaLoopStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during start media loop:", error);
document.getElementById('mediaLoopStatus').innerText = "Error during start media loop.";
});
}
function stopMediaLoop() {
fetch('../stop_media_loop', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
document.getElementById('mediaLoopStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during stop media loop:", error);
document.getElementById('mediaLoopStatus').innerText = "Error during stop media loop.";
});
}
function startWebGallery() {
const autoStart = document.getElementById('autoStart').checked;
const url = document.getElementById('galleryURL').value
fetch('../start_web_gallery', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: url, autoStart: autoStart })
})
.then(response => response.json())
.then(data => {
document.getElementById('webGalleryStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during start web gallery:", error);
document.getElementById('webGalleryStatus').innerText = "Error during start web gallery.";
});
}
function stopWebGallery() {
fetch('../stop_web_gallery', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
document.getElementById('webGalleryStatus').innerText = data.message;
})
.catch(error => {
console.error("Error during stop web gallery:", error);
document.getElementById('webGalleryStatus').innerText = "Error during stop web gallery.";
});
}
function clearURL() {
document.getElementById('galleryURL').value = '';
}
document.addEventListener('DOMContentLoaded', loadLastUsedData);
// Call the function to load media sources when the page loads
window.onload = loadMediaSources;
</script>
</body>
</html>