commit-10-2-25
This commit is contained in:
parent
343a7f7dd6
commit
4b75d0886e
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
@ -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
@ -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__
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -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
@ -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
440
templates/index _new.html
Normal 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>
|
||||||
@ -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
313
templates/index_orig.html
Normal 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user