linkkf 로직수정중
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="linkkf" uuid="8b6bf041-ffab-472b-b603-18b3316bc628">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/data/db/linkkf.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
20
.idea/flaskfarm.iml
generated
Normal file
20
.idea/flaskfarm.iml
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (FF)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/lib/framework/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
||||
76
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
76
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,76 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredUrls">
|
||||
<list>
|
||||
<option value="http://localhost" />
|
||||
<option value="http://127.0.0.1" />
|
||||
<option value="http://0.0.0.0" />
|
||||
<option value="http://www.w3.org/" />
|
||||
<option value="http://json-schema.org/draft" />
|
||||
<option value="http://java.sun.com/" />
|
||||
<option value="http://xmlns.jcp.org/" />
|
||||
<option value="http://javafx.com/javafx/" />
|
||||
<option value="http://javafx.com/fxml" />
|
||||
<option value="http://maven.apache.org/xsd/" />
|
||||
<option value="http://maven.apache.org/POM/" />
|
||||
<option value="http://www.springframework.org/schema/" />
|
||||
<option value="http://www.springframework.org/tags" />
|
||||
<option value="http://www.springframework.org/security/tags" />
|
||||
<option value="http://www.thymeleaf.org" />
|
||||
<option value="http://www.jboss.org/j2ee/schema/" />
|
||||
<option value="http://www.jboss.com/xml/ns/" />
|
||||
<option value="http://www.ibm.com/webservices/xsd" />
|
||||
<option value="http://activemq.apache.org/schema/" />
|
||||
<option value="http://schema.cloudfoundry.org/spring/" />
|
||||
<option value="http://schemas.xmlsoap.org/" />
|
||||
<option value="http://cxf.apache.org/schemas/" />
|
||||
<option value="http://primefaces.org/ui" />
|
||||
<option value="http://tiles.apache.org/" />
|
||||
<option value="http://yommi.duckdns.org" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="17">
|
||||
<item index="0" class="java.lang.String" itemvalue="trio-websocket" />
|
||||
<item index="1" class="java.lang.String" itemvalue="h11" />
|
||||
<item index="2" class="java.lang.String" itemvalue="loguru" />
|
||||
<item index="3" class="java.lang.String" itemvalue="sniffio" />
|
||||
<item index="4" class="java.lang.String" itemvalue="sqlalchemy" />
|
||||
<item index="5" class="java.lang.String" itemvalue="wsproto" />
|
||||
<item index="6" class="java.lang.String" itemvalue="attrs" />
|
||||
<item index="7" class="java.lang.String" itemvalue="sortedcontainers" />
|
||||
<item index="8" class="java.lang.String" itemvalue="exceptiongroup" />
|
||||
<item index="9" class="java.lang.String" itemvalue="trio" />
|
||||
<item index="10" class="java.lang.String" itemvalue="selenium" />
|
||||
<item index="11" class="java.lang.String" itemvalue="certifi" />
|
||||
<item index="12" class="java.lang.String" itemvalue="pysocks" />
|
||||
<item index="13" class="java.lang.String" itemvalue="urllib3" />
|
||||
<item index="14" class="java.lang.String" itemvalue="async-generator" />
|
||||
<item index="15" class="java.lang.String" itemvalue="outcome" />
|
||||
<item index="16" class="java.lang.String" itemvalue="idna" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N802" />
|
||||
<option value="N803" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="sqlalchemy.engine.result.Result.__await__" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (FF)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/flaskfarm.iml" filepath="$PROJECT_DIR$/.idea/flaskfarm.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
0
.idea/sonarlint/issuestore/0/3/03875fef6dc33ed50c8bc5f25df52c02e352a134
generated
Normal file
0
.idea/sonarlint/issuestore/0/3/03875fef6dc33ed50c8bc5f25df52c02e352a134
generated
Normal file
0
.idea/sonarlint/issuestore/0/c/0c528d2f014ab7c32dd27d4e5d79396e74f3e62c
generated
Normal file
0
.idea/sonarlint/issuestore/0/c/0c528d2f014ab7c32dd27d4e5d79396e74f3e62c
generated
Normal file
9
.idea/sonarlint/issuestore/0/e/0e68783ed60c8d2f67617374a92c1652fb6bdaee
generated
Normal file
9
.idea/sonarlint/issuestore/0/e/0e68783ed60c8d2f67617374a92c1652fb6bdaee
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
cpython:S6019"FFix this reluctant quantifier that will only ever match 0 repetitions.(¨†…™üÿÿÿÿ
|
||||
cpython:S6019"FFix this reluctant quantifier that will only ever match 0 repetitions.(Ìé©èùÿÿÿÿ
|
||||
cpython:S6019"FFix this reluctant quantifier that will only ever match 0 repetitions.(–…‡éþÿÿÿÿ
|
||||
^python:S6019"FFix this reluctant quantifier that will only ever match 0 repetitions.(ëÅÑš
|
||||
^python:S6019"FFix this reluctant quantifier that will only ever match 0 repetitions.(‹Ò•–
|
||||
6python:S125!"Remove this commented out code.(¡‚ÙÈ
|
||||
Tpython:S5754)"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
6python:S1252"Remove this commented out code.(ý<>™Ë
|
||||
6
.idea/sonarlint/issuestore/5/8/58836750c643ef469da17133f44914292a82f3b3
generated
Normal file
6
.idea/sonarlint/issuestore/5/8/58836750c643ef469da17133f44914292a82f3b3
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
qpython:S3776
|
||||
"TRefactor this function to reduce its Cognitive Complexity from 22 to the 15 allowed.(úÈœ‚ÿÿÿÿÿ
|
||||
6python:S125"Remove this commented out code.(ÑêÏà
|
||||
Tpython:S5754/"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
6python:S125("Remove this commented out code.(£ÌÎæ
|
||||
2
.idea/sonarlint/issuestore/6/2/627433fe5c5c7210e3062642e7963227a319d5c6
generated
Normal file
2
.idea/sonarlint/issuestore/6/2/627433fe5c5c7210e3062642e7963227a319d5c6
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
lpython:S3776"TRefactor this function to reduce its Cognitive Complexity from 25 to the 15 allowed.(”ÒÑ
|
||||
10
.idea/sonarlint/issuestore/8/2/82544e7bcd3de23afaf278a62d1180d65a1ef456
generated
Normal file
10
.idea/sonarlint/issuestore/8/2/82544e7bcd3de23afaf278a62d1180d65a1ef456
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
epython:S1192'"MDefine a constant instead of duplicating this literal 'Exception:%s' 9 times.(ï°Ð½
|
||||
Lpython:S10669"/Merge this if statement with the enclosing one.(Íìáöüÿÿÿÿ
|
||||
lpython:S3776"TRefactor this function to reduce its Cognitive Complexity from 50 to the 15 allowed.(<28>Øê
|
||||
6python:S125/"Remove this commented out code.(“Ä¡–
|
||||
lpython:S3776k"TRefactor this function to reduce its Cognitive Complexity from 39 to the 15 allowed.(ûãô”
|
||||
rpython:S3776á"TRefactor this function to reduce its Cognitive Complexity from 58 to the 15 allowed.(ó‚‚åýÿÿÿÿ
|
||||
Upython:S5754ü"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
<python:S125¼"Remove this commented out code.(ÁêÕúûÿÿÿÿ
|
||||
Upython:S5754é"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
7
.idea/sonarlint/issuestore/9/5/95519e06d92ec11e26cf873c3c30e2fcedf78892
generated
Normal file
7
.idea/sonarlint/issuestore/9/5/95519e06d92ec11e26cf873c3c30e2fcedf78892
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
Bpython:S1481"%Remove the unused local variable "e".(¼–¯¸ÿÿÿÿÿ
|
||||
6python:S125"Remove this commented out code.(™èÇÅ
|
||||
;python:S125"Remove this commented out code.(à髈üÿÿÿÿ
|
||||
6python:S125T"Remove this commented out code.(»—Ö
|
||||
Cpython:S5806Y"+Rename this variable; it shadows a builtin.(Ç¡¡
|
||||
6python:S125Z"Remove this commented out code.(¾§Ç¡
|
||||
33
.idea/sonarlint/issuestore/e/e/eeb13886aba87bd6947610e1cba3283d308baf0c
generated
Normal file
33
.idea/sonarlint/issuestore/e/e/eeb13886aba87bd6947610e1cba3283d308baf0c
generated
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
epython:S5797I"HReplace this expression; used as a condition it will always be constant.(™¼ï€üÿÿÿÿ
|
||||
Mpython:S1066ã"/Merge this if statement with the enclosing one.(„Õý“þÿÿÿÿ
|
||||
vpython:S1163"ZRename this field "SystemModelSetting" to match the regular expression ^[_a-z][_a-z0-9]*$.(òªÆÓýÿÿÿÿ
|
||||
bpython:S1164"KRename this field "Job" to match the regular expression ^[_a-z][_a-z0-9]*$.(„è´’
|
||||
rpython:S116Ù"URename this field "PluginManager" to match the regular expression ^[_a-z][_a-z0-9]*$.(æ¥Ë÷ùÿÿÿÿ
|
||||
6python:S125/"Remove this commented out code.(´ÔÔí
|
||||
6python:S125_"Remove this commented out code.(„ÿ’È
|
||||
6python:S125"Remove this commented out code.(æ‰Èì
|
||||
[python:S112‰">Replace this generic exception class with a more specific one.(¢…›°þÿÿÿÿ
|
||||
ypython:S1186¯"[Add a nested comment explaining why this function is empty, or complete the implementation.(³‡Êºþÿÿÿÿ
|
||||
|python:S117Á"_Rename this local variable "SystemInstance" to match the regular expression ^[_a-z][a-z0-9_]*$.(çÉÍ¥úÿÿÿÿ
|
||||
rpython:S3776÷"TRefactor this function to reduce its Cognitive Complexity from 26 to the 15 allowed.(<28><>›†øÿÿÿÿ
|
||||
7python:S125ü"Remove this commented out code.(‚ðû<C3B0>
|
||||
Apython:S108–")Either remove or fill this block of code.(ã‘Û¾
|
||||
Upython:S5754 "<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
<python:S125¼"Remove this commented out code.(ÛÑ“¹üÿÿÿÿ
|
||||
rpython:S3776Ç"TRefactor this function to reduce its Cognitive Complexity from 21 to the 15 allowed.(û´´¨ýÿÿÿÿ
|
||||
<python:S125Ê"Remove this commented out code.(ˆà¡×ÿÿÿÿÿ
|
||||
Upython:S5754þ"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
tpython:S117²"\Rename this local variable "fileHandler" to match the regular expression ^[_a-z][a-z0-9_]*$.(ºŽ™…
|
||||
vpython:S117¶"^Rename this local variable "streamHandler" to match the regular expression ^[_a-z][a-z0-9_]*$.(<28>½ÝÀ
|
||||
mpython:S3776<18>"TRefactor this function to reduce its Cognitive Complexity from 24 to the 15 allowed.(¸ì¶Ê
|
||||
Upython:S5754¢"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
Upython:S5754–"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
npython:S1542¥"PRename function "customTime" to match the regular expression ^[a-z_][a-z0-9_]*$.(ŠˆíÖúÿÿÿÿ
|
||||
Upython:S5754À"<Specify an exception class to catch or reraise the exception(–’Ê<E28099>
|
||||
Upython:S5754Â"<Specify an exception class to catch or reraise the exception(–’Ê<E28099>
|
||||
Upython:S5754Ä"<Specify an exception class to catch or reraise the exception(–’Ê<E28099>
|
||||
Upython:S5754Æ"<Specify an exception class to catch or reraise the exception(–’Ê<E28099>
|
||||
Upython:S5754È"<Specify an exception class to catch or reraise the exception(–’Ê<E28099>
|
||||
Upython:S5754Ï"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
Upython:S5754<18>"<Specify an exception class to catch or reraise the exception(ˆÊÉ·
|
||||
17
.idea/sonarlint/issuestore/index.pb
generated
Normal file
17
.idea/sonarlint/issuestore/index.pb
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
I
|
||||
lib/framework/__init__.py,0/3/03875fef6dc33ed50c8bc5f25df52c02e352a134
|
||||
M
|
||||
lib/framework/init_declare.py,9/5/95519e06d92ec11e26cf873c3c30e2fcedf78892
|
||||
J
|
||||
lib/framework/init_menu.py,6/2/627433fe5c5c7210e3062642e7963227a319d5c6
|
||||
L
|
||||
lib/framework/init_plugin.py,8/2/82544e7bcd3de23afaf278a62d1180d65a1ef456
|
||||
K
|
||||
lib/framework/init_route.py,5/8/58836750c643ef469da17133f44914292a82f3b3
|
||||
I
|
||||
lib/framework/init_web.py,0/e/0e68783ed60c8d2f67617374a92c1652fb6bdaee
|
||||
K
|
||||
lib/framework/log_viewer.py,0/c/0c528d2f014ab7c32dd27d4e5d79396e74f3e62c
|
||||
J
|
||||
lib/framework/init_main.py,e/e/eeb13886aba87bd6947610e1cba3283d308baf0c
|
||||
13
.idea/vcs.xml
generated
Normal file
13
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/ffmpeg" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/flaskcode" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/klive_plus" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/number_baseball" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/sjva" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/terminal" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/data/plugins/trans" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
43
cli/encode.py
Normal file
43
cli/encode.py
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'lib'))
|
||||
|
||||
from support import SupportFile, SupportSC, logger
|
||||
|
||||
|
||||
class Encode:
|
||||
def start_folder(self, folderpath):
|
||||
for name in os.listdir(folderpath):
|
||||
filepath = os.path.join(folderpath, name)
|
||||
if os.path.isfile(filepath) and name not in ['setup.py', '__init__.py']:
|
||||
self.encode_file(filepath)
|
||||
|
||||
def encode_file(self, filepath):
|
||||
text = SupportFile.read_file(filepath)
|
||||
data = SupportSC.encode(text, 0)
|
||||
SupportFile.write_file(filepath + 'f', data)
|
||||
logger.info(f"Create {os.path.basename(filepath + 'f')}")
|
||||
|
||||
|
||||
def process_args(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--mode', default='encode')
|
||||
parser.add_argument('--source', required=True, help=u'absolute path. folder or file')
|
||||
args = parser.parse_args()
|
||||
if SupportSC.LIBRARY_LOADING == False:
|
||||
logger.error("sc import fail")
|
||||
return
|
||||
if os.path.exists(args.source):
|
||||
if os.path.isdir(args.source):
|
||||
self.start_folder(args.source)
|
||||
elif os.path.isfile(args.source):
|
||||
self.encode_file(args.source)
|
||||
else:
|
||||
logger.error("wrong source path!!")
|
||||
|
||||
|
||||
if __name__== "__main__":
|
||||
Encode().process_args()
|
||||
90
ff_3_10_requirements.txt
Normal file
90
ff_3_10_requirements.txt
Normal file
@@ -0,0 +1,90 @@
|
||||
aiohttp==3.8.3
|
||||
aiosignal==1.2.0
|
||||
amqp==5.1.1
|
||||
appdirs==1.4.4
|
||||
APScheduler==3.9.1
|
||||
async-generator==1.10
|
||||
async-timeout==4.0.2
|
||||
attrs==22.1.0
|
||||
beautifulsoup4==4.11.1
|
||||
bidict==0.22.0
|
||||
billiard==3.6.4.0
|
||||
cattrs==22.2.0
|
||||
celery==5.2.7
|
||||
certifi==2022.9.24
|
||||
charset-normalizer==2.1.1
|
||||
click==8.1.3
|
||||
click-didyoumean==0.3.0
|
||||
click-plugins==1.1.1
|
||||
click-repl==0.2.0
|
||||
cloudscraper==1.2.64
|
||||
Deprecated==1.2.13
|
||||
discord-webhook==0.17.0
|
||||
EditorConfig==0.12.3
|
||||
exceptiongroup==1.0.0rc9
|
||||
Flask==2.2.2
|
||||
Flask-Cors==3.0.10
|
||||
Flask-Dropzone==1.6.0
|
||||
Flask-Login==0.6.2
|
||||
Flask-Markdown==0.3
|
||||
Flask-SocketIO==5.3.1
|
||||
Flask-SQLAlchemy==3.0.2
|
||||
FlaskFarm==4.0.47
|
||||
frozenlist==1.3.1
|
||||
gevent==22.10.1
|
||||
gevent-websocket==0.10.1
|
||||
greenlet==1.1.3.post0
|
||||
h11==0.14.0
|
||||
idna==3.4
|
||||
importlib-metadata==5.0.0
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
jsbeautifier==1.14.7
|
||||
kombu==5.2.4
|
||||
lxml==4.9.1
|
||||
Markdown==3.4.1
|
||||
MarkupSafe==2.1.1
|
||||
multidict==6.0.2
|
||||
outcome==1.2.0
|
||||
packaging==21.3
|
||||
Pillow==9.2.0
|
||||
prompt-toolkit==3.0.31
|
||||
psutil==5.9.3
|
||||
pycryptodome==3.15.0
|
||||
pyparsing==3.0.9
|
||||
PySocks==1.7.1
|
||||
python-dotenv==0.21.0
|
||||
python-engineio==4.3.4
|
||||
python-socketio==5.7.2
|
||||
pytz==2022.5
|
||||
pytz-deprecation-shim==0.1.0.post0
|
||||
PyYAML==6.0
|
||||
redis==4.3.4
|
||||
requests==2.28.1
|
||||
requests-cache==0.9.6
|
||||
requests-toolbelt==0.10.1
|
||||
selenium==4.5.0
|
||||
selenium-stealth==1.0.6
|
||||
six==1.16.0
|
||||
sniffio==1.3.0
|
||||
sortedcontainers==2.4.0
|
||||
soupsieve==2.3.2.post1
|
||||
SQLAlchemy==1.4.42
|
||||
telepot-mod==0.0.1
|
||||
tqdm==4.64.1
|
||||
trio==0.22.0
|
||||
trio-websocket==0.9.2
|
||||
tzdata==2022.5
|
||||
tzlocal==4.2
|
||||
url-normalize==1.4.3
|
||||
urllib3==1.26.12
|
||||
vine==5.0.0
|
||||
wcwidth==0.2.5
|
||||
webdriver-manager==3.8.4
|
||||
Werkzeug==2.2.2
|
||||
wrapt==1.14.1
|
||||
wsproto==1.2.0
|
||||
yarl==1.8.1
|
||||
zipp==3.10.0
|
||||
zope.event==4.5.0
|
||||
zope.interface==5.5.0
|
||||
@@ -1,18 +1,12 @@
|
||||
# 카테고리
|
||||
# uri 가 plugin인 경우 name 값은 대체
|
||||
|
||||
#- name: "토렌트"
|
||||
# list:
|
||||
# - uri: "rss"
|
||||
|
||||
|
||||
#- name: "기본 기능"
|
||||
# list:
|
||||
# - uri: "terminal"
|
||||
# - uri: "command"
|
||||
# - uri: "flaskfilemanager"
|
||||
# - uri: "flaskcode"
|
||||
# - uri: "number_baseball"
|
||||
|
||||
|
||||
#- name: "링크"
|
||||
@@ -32,6 +26,8 @@
|
||||
name: "확장 설정"
|
||||
- uri: "system/plugin"
|
||||
name: "플러그인 관리"
|
||||
- uri: "system/tool/command"
|
||||
name: "Command 관리"
|
||||
- uri: "-"
|
||||
- uri: "system/logout"
|
||||
name: "로그아웃"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# message id 가 없을 경우 DEFAULT 값 사용
|
||||
# 공통: type, enable_time (시작시간-종료시간. 항상 받을 경우 생략)
|
||||
|
||||
- DEFAULT:
|
||||
DEFAULT:
|
||||
- type: 'telegram'
|
||||
token: ''
|
||||
chat_id: ''
|
||||
@@ -14,7 +14,7 @@
|
||||
enable_time: '09-23'
|
||||
|
||||
|
||||
- system_start:
|
||||
system_start:
|
||||
- type: 'telegram'
|
||||
token: ''
|
||||
chat_id: ''
|
||||
|
||||
@@ -3,11 +3,11 @@ Flask
|
||||
Flask-SQLAlchemy
|
||||
Flask-Login
|
||||
Flask-Cors
|
||||
Flask-Markdown
|
||||
#Flask-Markdown
|
||||
Flask-SocketIO
|
||||
python-engineio
|
||||
python-socketio
|
||||
Werkzeug
|
||||
python-socketio<5.8.0
|
||||
Werkzeug<3.0
|
||||
Jinja2
|
||||
|
||||
# common util
|
||||
@@ -26,3 +26,4 @@ pillow
|
||||
gevent
|
||||
gevent-websocket
|
||||
pycryptodome
|
||||
json_fix
|
||||
|
||||
BIN
lib/.DS_Store
vendored
Normal file
BIN
lib/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
lib/framework/.DS_Store
vendored
Normal file
BIN
lib/framework/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1,25 +1,70 @@
|
||||
try:
|
||||
import yaml
|
||||
except:
|
||||
import os
|
||||
try:
|
||||
os.system("pip install pyyaml")
|
||||
except:
|
||||
pass
|
||||
|
||||
from .init_main import Framework
|
||||
from .version import VERSION
|
||||
|
||||
frame = Framework.get_instance()
|
||||
F = frame
|
||||
logger = frame.logger
|
||||
app = frame.app
|
||||
celery = frame.celery
|
||||
db = frame.db
|
||||
scheduler = frame.scheduler
|
||||
socketio = frame.socketio
|
||||
path_app_root = frame.path_app_root
|
||||
path_data = frame.path_data
|
||||
get_logger = frame.get_logger
|
||||
# 2024.06.13
|
||||
# 잘못된 설계로 인해 import 만으로 초기화 되버려 lib을 사용할 수 없다.
|
||||
# 분리.
|
||||
|
||||
F = None
|
||||
frame = None
|
||||
logger = None
|
||||
app = None
|
||||
celery = None
|
||||
db = None
|
||||
scheduler = None
|
||||
socketio = None
|
||||
rd = None
|
||||
path_app_root = None
|
||||
path_data = None
|
||||
get_logger = None
|
||||
SystemModelSetting = None
|
||||
get_cache = None
|
||||
|
||||
def initiaize():
|
||||
global F
|
||||
global frame
|
||||
global logger
|
||||
global app
|
||||
global celery
|
||||
global db
|
||||
global scheduler
|
||||
global socketio
|
||||
global path_app_root
|
||||
global path_data
|
||||
global get_logger
|
||||
global SystemModelSetting
|
||||
global get_cache
|
||||
|
||||
F = Framework.get_instance()
|
||||
frame = F
|
||||
logger = frame.logger
|
||||
app = frame.app
|
||||
celery = frame.celery
|
||||
db = frame.db
|
||||
scheduler = frame.scheduler
|
||||
socketio = frame.socketio
|
||||
rd = frame.rd
|
||||
path_app_root = frame.path_app_root
|
||||
path_data = frame.path_data
|
||||
get_logger = frame.get_logger
|
||||
|
||||
frame.initialize_system()
|
||||
from system.setup import SystemModelSetting as SS
|
||||
SystemModelSetting = SS
|
||||
frame.initialize_plugin()
|
||||
return frame
|
||||
|
||||
from flask_login import login_required
|
||||
from support import d
|
||||
|
||||
from .init_declare import User, check_api
|
||||
from .scheduler import Job
|
||||
|
||||
frame.initialize_system()
|
||||
from system.setup import SystemModelSetting
|
||||
|
||||
frame.initialize_plugin()
|
||||
|
||||
73
lib/framework/init_cache_manager.py
Normal file
73
lib/framework/init_cache_manager.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import redis
|
||||
|
||||
class _RedisManager:
|
||||
_instance = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not cls._instance:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, host='localhost', port=6379):
|
||||
if hasattr(self, 'redis_client'):
|
||||
return
|
||||
|
||||
try:
|
||||
self.redis_client = redis.Redis(host=host, port=port, db=1, decode_responses=True)
|
||||
self.redis_client.ping()
|
||||
self.is_redis = True
|
||||
except redis.exceptions.ConnectionError:
|
||||
self.is_redis = False
|
||||
self.cache_backend = {} # Redis 실패 시 메모리 캐시 사용
|
||||
|
||||
def set(self, key, value, ex=None):
|
||||
if self.is_redis:
|
||||
self.redis_client.set(key, value, ex=ex)
|
||||
else:
|
||||
self.cache_backend[key] = value
|
||||
|
||||
def get(self, key):
|
||||
if self.is_redis:
|
||||
return self.redis_client.get(key)
|
||||
else:
|
||||
return self.cache_backend.get(key)
|
||||
|
||||
def delete(self, key):
|
||||
if self.is_redis:
|
||||
self.redis_client.delete(key)
|
||||
else:
|
||||
if key in self.cache_backend:
|
||||
del self.cache_backend[key]
|
||||
|
||||
|
||||
#_redis_manager_instance = _RedisManager()
|
||||
|
||||
class NamespacedCache:
|
||||
def __init__(self, namespace):
|
||||
self._manager = _RedisManager._instance
|
||||
self.namespace = namespace
|
||||
|
||||
def _make_key(self, key):
|
||||
# 'plugin_name:key' 형식으로 실제 키를 생성
|
||||
return f"{self.namespace}:{key}"
|
||||
|
||||
def set(self, key, value, ex=None):
|
||||
full_key = self._make_key(key)
|
||||
self._manager.set(full_key, value, ex=ex)
|
||||
|
||||
def get(self, key):
|
||||
full_key = self._make_key(key)
|
||||
return self._manager.get(full_key)
|
||||
|
||||
def delete(self, key):
|
||||
full_key = self._make_key(key)
|
||||
self._manager.delete(full_key)
|
||||
|
||||
|
||||
def get_cache(plugin_name: str) -> NamespacedCache:
|
||||
"""
|
||||
플러그인 이름을 기반으로 네임스페이스가 적용된 캐시 객체를 반환합니다.
|
||||
"""
|
||||
if not plugin_name:
|
||||
raise ValueError("플러그인 이름은 필수입니다.")
|
||||
return NamespacedCache(plugin_name)
|
||||
@@ -13,13 +13,13 @@ def check_api(original_function):
|
||||
#logger.warning(request.url)
|
||||
#logger.warning(request.form)
|
||||
try:
|
||||
if F.SystemModelSetting.get_bool('auth_use_apikey'):
|
||||
if request.method == 'POST':
|
||||
apikey = request.form['apikey']
|
||||
else:
|
||||
apikey = request.args.get('apikey')
|
||||
#apikey = request.args.get('apikey')
|
||||
if apikey is None or apikey != F.SystemModelSetting.get('auth_apikey'):
|
||||
if F.SystemModelSetting.get_bool('use_apikey'):
|
||||
try:
|
||||
d = request.get_json()
|
||||
except Exception:
|
||||
d = request.form.to_dict() if request.method == 'POST' else request.args.to_dict()
|
||||
apikey = d.get('apikey')
|
||||
if apikey is None or apikey != F.SystemModelSetting.get('apikey'):
|
||||
F.logger.warning('CHECK API : ABORT no match ({})'.format(apikey))
|
||||
F.logger.warning(request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
|
||||
abort(403)
|
||||
@@ -31,7 +31,7 @@ def check_api(original_function):
|
||||
return original_function(*args, **kwargs) #2
|
||||
return wrapper_function
|
||||
|
||||
# Suuport를 logger 생성전에 쓰지 않기 위해 중복 선언
|
||||
# Support를 logger 생성전에 쓰지 않기 위해 중복 선언
|
||||
import logging
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class CustomFormatter(logging.Formatter):
|
||||
# pathname filename
|
||||
#format = "[%(asctime)s|%(name)s|%(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
|
||||
|
||||
__format = '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset} %(pathname)s:%(lineno)s] {color}%(message)s{reset}' if os.environ.get('LOGGER_PATHNAME', "False") == "True" else '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset} %(filename)s:%(lineno)s] {color}%(message)s{reset}'
|
||||
__format = '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset}|%(pathname)s:%(lineno)s] {color}%(message)s{reset}' if os.environ.get('LOGGER_PATHNAME', "False") == "True" else '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset}|%(filename)s:%(lineno)s] {color}%(message)s{reset}'
|
||||
|
||||
FORMATS = {
|
||||
logging.DEBUG: __format.format(color=grey, reset=reset, yellow=yellow, green=green),
|
||||
|
||||
@@ -8,14 +8,15 @@ import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
import redis
|
||||
import yaml
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from flask_login import LoginManager, login_required
|
||||
from flask_socketio import SocketIO
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flaskext.markdown import Markdown
|
||||
from pytz import timezone, utc
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
from .init_declare import CustomFormatter, check_api
|
||||
|
||||
@@ -37,12 +38,15 @@ class Framework:
|
||||
self.db = None
|
||||
self.scheduler = None
|
||||
self.socketio = None
|
||||
self.rd = None
|
||||
self.path_app_root = None
|
||||
self.path_data = None
|
||||
self.users = {}
|
||||
self.get_cache = None
|
||||
|
||||
self.__level_unset_logger_list = []
|
||||
self.__logger_list = []
|
||||
self.all_log_filehandler = None
|
||||
self.__exit_code = -1
|
||||
self.login_manager = None
|
||||
#self.plugin_instance_list = {}
|
||||
@@ -59,14 +63,17 @@ class Framework:
|
||||
def __initialize(self):
|
||||
os.environ["PYTHONUNBUFFERED"] = "1"
|
||||
os.environ['FF'] = "true"
|
||||
os.environ['FF_PYTHON'] = sys.executable
|
||||
self.__config_initialize("first")
|
||||
self.__make_default_dir()
|
||||
|
||||
self.logger = self.get_logger(__package__)
|
||||
self.get_logger('support')
|
||||
import support
|
||||
|
||||
self.__prepare_starting()
|
||||
self.app = Flask(__name__)
|
||||
self.app.wsgi_app = ProxyFix(self.app.wsgi_app, x_proto=1)
|
||||
self.__config_initialize('flask')
|
||||
|
||||
self.__init_db()
|
||||
@@ -82,7 +89,6 @@ class Framework:
|
||||
self.socketio = SocketIO(self.app, cors_allowed_origins="*", async_mode='threading')
|
||||
|
||||
CORS(self.app)
|
||||
Markdown(self.app)
|
||||
|
||||
self.login_manager = LoginManager()
|
||||
self.login_manager.init_app(self.app)
|
||||
@@ -94,12 +100,13 @@ class Framework:
|
||||
self.app.config.update(
|
||||
DROPZONE_MAX_FILE_SIZE = 102400,
|
||||
DROPZONE_TIMEOUT = 5*60*1000,
|
||||
#DROPZONE_ALLOWED_FILE_CUSTOM = True,
|
||||
#DROPZONE_ALLOWED_FILE_TYPE = 'default, image, audio, video, text, app, *.*',
|
||||
DROPZONE_ALLOWED_FILE_CUSTOM = True,
|
||||
DROPZONE_ALLOWED_FILE_TYPE = "image/*, audio/*, video/*, text/*, application/*, *.*",
|
||||
)
|
||||
self.dropzone = Dropzone(self.app)
|
||||
|
||||
|
||||
|
||||
def __init_db(self):
|
||||
# https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/config/#flask_sqlalchemy.config.SQLALCHEMY_BINDS
|
||||
# 어떤 편법도 불가. db를 사용하지 않아도 파일이 생김.
|
||||
@@ -131,19 +138,20 @@ class Framework:
|
||||
|
||||
|
||||
def __init_celery(self):
|
||||
redis_port = 6379
|
||||
try:
|
||||
from celery import Celery
|
||||
|
||||
#if frame.config['use_celery'] == False or platform.system() == 'Windows':
|
||||
if self.config['use_celery'] == False:
|
||||
raise Exception('no celery')
|
||||
|
||||
raise Exception('use_celery=False')
|
||||
from celery import Celery
|
||||
redis_port = os.environ.get('REDIS_PORT', None)
|
||||
if redis_port == None:
|
||||
redis_port = self.config.get('redis_port', None)
|
||||
if redis_port == None:
|
||||
redis_port = '6379'
|
||||
|
||||
self.config['redis_port'] = redis_port
|
||||
self.rd = redis.StrictRedis(host='localhost', port=redis_port, db=0)
|
||||
if self.config['use_celery'] == False:
|
||||
raise Exception('no celery')
|
||||
|
||||
self.app.config['CELERY_BROKER_URL'] = 'redis://localhost:%s/0' % redis_port
|
||||
self.app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:%s/0' % redis_port
|
||||
@@ -166,6 +174,7 @@ class Framework:
|
||||
F.logger.info(f"celery running_type: {running_type}")
|
||||
#F.logger.info(f"celery running_type: {options}")
|
||||
celery.steps['worker'].add(CustomArgs)
|
||||
|
||||
except Exception as e:
|
||||
if self.config['use_celery']:
|
||||
self.logger.error('CELERY!!!')
|
||||
@@ -187,6 +196,14 @@ class Framework:
|
||||
if len(args) > 0 and type(args[0]) == type(dummy_func):
|
||||
return args[0]
|
||||
self.f(*args, **kwargs)
|
||||
try:
|
||||
from .init_cache_manager import _RedisManager, get_cache
|
||||
_RedisManager(host='localhost', port=redis_port)
|
||||
self.get_cache = get_cache
|
||||
except Exception as e:
|
||||
self.logger.error(f"get_cache import error: {str(e)}")
|
||||
self.get_cache = None
|
||||
|
||||
return celery
|
||||
|
||||
|
||||
@@ -201,11 +218,13 @@ class Framework:
|
||||
self.logger.error(f'Exception:{str(e)}')
|
||||
self.logger.error(traceback.format_exc())
|
||||
self.SystemModelSetting = SystemInstance.ModelSetting
|
||||
SystemInstance.plugin_load()
|
||||
if self.config['run_flask']:
|
||||
SystemInstance.plugin_load()
|
||||
self.app.register_blueprint(SystemInstance.blueprint)
|
||||
self.config['flag_system_loading'] = True
|
||||
self.__config_initialize('member')
|
||||
self.__config_initialize('system_loading_after')
|
||||
self.set_level(self.SystemModelSetting.get_int('log_level'))
|
||||
|
||||
|
||||
def initialize_plugin(self):
|
||||
@@ -232,6 +251,7 @@ class Framework:
|
||||
|
||||
self.__make_default_logger()
|
||||
self.__config_initialize("last")
|
||||
self.config['loading_completed'] = True
|
||||
self.logger.info('### LAST')
|
||||
self.logger.info(f"### PORT: {self.config.get('port')}")
|
||||
self.logger.info('### Now you can access App by webbrowser!!')
|
||||
@@ -248,6 +268,7 @@ class Framework:
|
||||
def __config_initialize(self, mode):
|
||||
if mode == "first":
|
||||
self.config = {}
|
||||
self.config['loading_completed'] = False
|
||||
self.config['os'] = platform.system()
|
||||
self.config['flag_system_loading'] = False
|
||||
#self.config['run_flask'] = True if sys.argv[0].endswith('main.py') else False
|
||||
@@ -263,6 +284,8 @@ class Framework:
|
||||
self.config['export_filepath'] = os.path.join(self.config['path_app'], 'export.sh')
|
||||
self.config['exist_export'] = os.path.exists(self.config['export_filepath'])
|
||||
self.config['recent_version'] = '--'
|
||||
from .version import VERSION
|
||||
self.config['version'] = VERSION
|
||||
self.__process_args()
|
||||
self.__load_config()
|
||||
self.__init_define()
|
||||
@@ -270,7 +293,7 @@ class Framework:
|
||||
self.config['notify_yaml_filepath'] = os.path.join(self.config['path_data'], 'db', 'notify.yaml')
|
||||
if 'running_type' not in self.config:
|
||||
self.config['running_type'] = 'native'
|
||||
|
||||
self.pip_install()
|
||||
elif mode == "flask":
|
||||
self.app.secret_key = os.urandom(24)
|
||||
self.app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||
@@ -295,8 +318,8 @@ class Framework:
|
||||
self.config['DEFINE'] = {}
|
||||
# 이건 필요 없음
|
||||
self.config['DEFINE']['GIT_VERSION_URL'] = 'https://raw.githubusercontent.com/flaskfarm/flaskfarm/main/lib/framework/version.py'
|
||||
self.config['DEFINE']['CHANGELOG'] = 'https://flaskfarm.github.io/posts/changelog'
|
||||
|
||||
self.config['DEFINE']['CHANGELOG'] = 'https://github.com/flaskfarm/flaskfarm'
|
||||
#self.config['DEFINE']['WEB_DIRECT_URL'] = "http://52.78.103.230:49734"
|
||||
|
||||
|
||||
def __process_args(self):
|
||||
@@ -363,6 +386,9 @@ class Framework:
|
||||
self.config['debug'] = False
|
||||
if self.config.get('plugin_update') == None:
|
||||
self.config['plugin_update'] = True
|
||||
# 2022-11-20
|
||||
if self.config['debug']:
|
||||
self.config['plugin_update'] = False
|
||||
if self.config.get('plugin_loading_only_devpath') == None:
|
||||
self.config['plugin_loading_only_devpath'] = False
|
||||
if self.config.get('plugin_loading_list') == None:
|
||||
@@ -402,8 +428,8 @@ class Framework:
|
||||
try:
|
||||
if self.config['flag_system_loading']:
|
||||
try:
|
||||
from system import SystemModelSetting
|
||||
level = SystemModelSetting.get_int('log_level')
|
||||
#from system import SystemModelSetting
|
||||
level = self.SystemModelSetting.get_int('log_level')
|
||||
except:
|
||||
level = logging.DEBUG
|
||||
if self.__level_unset_logger_list is not None:
|
||||
@@ -426,7 +452,7 @@ class Framework:
|
||||
return converted.timetuple()
|
||||
|
||||
if from_command == False:
|
||||
file_formatter = logging.Formatter(u'[%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s] %(message)s')
|
||||
file_formatter = logging.Formatter(u'[%(asctime)s|%(levelname)s|%(name)s|%(filename)s:%(lineno)s] %(message)s')
|
||||
else:
|
||||
file_formatter = logging.Formatter(u'[%(asctime)s] %(message)s')
|
||||
|
||||
@@ -435,10 +461,18 @@ class Framework:
|
||||
fileHandler = logging.handlers.RotatingFileHandler(filename=os.path.join(self.path_data, 'log', f'{name}.log'), maxBytes=file_max_bytes, backupCount=5, encoding='utf8', delay=True)
|
||||
fileHandler.setFormatter(file_formatter)
|
||||
logger.addHandler(fileHandler)
|
||||
if name == 'framework' and self.all_log_filehandler == None:
|
||||
self.all_log_filehandler = logging.handlers.RotatingFileHandler(filename=os.path.join(self.path_data, 'log', f'all.log'), maxBytes=5*1024*1024, backupCount=5, encoding='utf8', delay=True)
|
||||
self.all_log_filehandler.setFormatter(file_formatter)
|
||||
|
||||
if from_command == False:
|
||||
streamHandler = logging.StreamHandler()
|
||||
streamHandler.setFormatter(CustomFormatter())
|
||||
logger.addHandler(streamHandler)
|
||||
|
||||
if self.all_log_filehandler != None:
|
||||
logger.addHandler(self.all_log_filehandler)
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
@@ -459,7 +493,7 @@ class Framework:
|
||||
def set_level(self, level):
|
||||
try:
|
||||
for l in self.__logger_list:
|
||||
l.setLevel(level)
|
||||
l.setLevel(int(level))
|
||||
self.__make_default_logger()
|
||||
except:
|
||||
pass
|
||||
@@ -517,8 +551,8 @@ class Framework:
|
||||
PluginManager.plugin_unload()
|
||||
with self.app.test_request_context():
|
||||
self.socketio.stop()
|
||||
except Exception as exception:
|
||||
self.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Exception:{str(e)}")
|
||||
self.logger.error(traceback.format_exc())
|
||||
|
||||
def get_recent_version(self):
|
||||
@@ -532,3 +566,11 @@ class Framework:
|
||||
self.logger.error(traceback.format_exc())
|
||||
self.config['recent_version'] = '확인 실패'
|
||||
return False
|
||||
|
||||
|
||||
# dev 도커용. package는 setup에 포함.
|
||||
def pip_install(self):
|
||||
try:
|
||||
import json_fix
|
||||
except:
|
||||
os.system('pip install json_fix')
|
||||
|
||||
@@ -1,93 +1,144 @@
|
||||
import os
|
||||
import shutil
|
||||
import traceback
|
||||
|
||||
from framework import F, logger
|
||||
from support import SupportYaml, d
|
||||
|
||||
from framework import F
|
||||
|
||||
|
||||
class MenuManager:
|
||||
menu_map = None
|
||||
|
||||
@classmethod
|
||||
def __load_menu_yaml(cls):
|
||||
menu_yaml_filepath = os.path.join(F.config['path_data'], 'db', 'menu.yaml')
|
||||
if os.path.exists(menu_yaml_filepath) == False:
|
||||
shutil.copy(
|
||||
os.path.join(F.config['path_app'], 'files', 'menu.yaml.template'),
|
||||
menu_yaml_filepath
|
||||
)
|
||||
cls.menu_map = SupportYaml.read_yaml(menu_yaml_filepath)
|
||||
try:
|
||||
menu_yaml_filepath = os.path.join(F.config['path_data'], 'db', 'menu.yaml')
|
||||
if os.path.exists(menu_yaml_filepath) == False:
|
||||
shutil.copy(
|
||||
os.path.join(F.config['path_app'], 'files', 'menu.yaml.template'),
|
||||
menu_yaml_filepath
|
||||
)
|
||||
cls.menu_map = SupportYaml.read_yaml(menu_yaml_filepath)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
cls.menu_map = SupportYaml.read_yaml(os.path.join(F.config['path_app'], 'files', 'menu.yaml.template'))
|
||||
|
||||
|
||||
@classmethod
|
||||
def init_menu(cls):
|
||||
cls.__load_menu_yaml()
|
||||
from .init_plugin import PluginManager
|
||||
plugin_menus = PluginManager.plugin_menus
|
||||
copy_map = []
|
||||
if cls.__init_menu() == False:
|
||||
cls.menu_map = SupportYaml.read_yaml(os.path.join(F.config['path_app'], 'files', 'menu.yaml.template'))
|
||||
cls.__init_menu()
|
||||
|
||||
for category in cls.menu_map:
|
||||
if 'uri' in category:
|
||||
copy_map.append(category)
|
||||
continue
|
||||
cate_count = 0
|
||||
@classmethod
|
||||
def __init_menu(cls):
|
||||
try:
|
||||
from .init_plugin import PluginManager
|
||||
plugin_menus = PluginManager.plugin_menus
|
||||
copy_map = []
|
||||
for category in cls.menu_map:
|
||||
if 'uri' in category:
|
||||
if category['uri'] in plugin_menus:
|
||||
plugin_menus[category['uri']]['match'] = True
|
||||
copy_map.append(plugin_menus[category['uri']]['menu'])
|
||||
else:
|
||||
copy_map.append(category)
|
||||
continue
|
||||
cate_count = 0
|
||||
|
||||
tmp_cate_list = []
|
||||
for item in category['list']:
|
||||
if item['uri'] in plugin_menus:
|
||||
plugin_menus[item['uri']]['match'] = True
|
||||
tmp_cate_list.append(plugin_menus[item['uri']]['menu'])
|
||||
cate_count += 1
|
||||
elif item['uri'].startswith('http'):
|
||||
tmp_cate_list.append({
|
||||
'uri': item['uri'],
|
||||
'name': item['name'],
|
||||
'target': item.get('target', '_blank')
|
||||
})
|
||||
cate_count += 1
|
||||
elif (len(item['uri'].split('/')) > 1 and item['uri'].split('/')[0] in plugin_menus) or item['uri'].startswith('javascript') or item['uri'] in ['-']:
|
||||
tmp_cate_list.append({
|
||||
'uri': item['uri'],
|
||||
'name': item.get('name', ''),
|
||||
})
|
||||
cate_count += 1
|
||||
elif item['uri'] == 'setting':
|
||||
if len(PluginManager.setting_menus) > 0:
|
||||
tmp_cate_list = []
|
||||
for item in category['list']:
|
||||
if item['uri'] in plugin_menus:
|
||||
plugin_menus[item['uri']]['match'] = True
|
||||
tmp_cate_list.append(plugin_menus[item['uri']]['menu'])
|
||||
cate_count += 1
|
||||
elif item['uri'].startswith('http'):
|
||||
tmp_cate_list.append({
|
||||
'uri': item['uri'],
|
||||
'name': item['name'],
|
||||
'target': item.get('target', '_blank')
|
||||
})
|
||||
cate_count += 1
|
||||
elif (len(item['uri'].split('/')) > 1 and item['uri'].split('/')[0] in plugin_menus) or item['uri'].startswith('javascript') or item['uri'] in ['-']:
|
||||
tmp_cate_list.append({
|
||||
'uri': item['uri'],
|
||||
'name': item.get('name', ''),
|
||||
'list': PluginManager.setting_menus
|
||||
})
|
||||
cate_count += 1
|
||||
elif item['uri'] == 'setting':
|
||||
# 2024.06.04
|
||||
# 확장설정도 메뉴 구성
|
||||
if len(PluginManager.setting_menus) > 0:
|
||||
set_tmp = item.get('list')
|
||||
if set_tmp:
|
||||
cp = PluginManager.setting_menus.copy()
|
||||
include = []
|
||||
for set_ch in set_tmp:
|
||||
if set_ch.get('uri') and (set_ch.get('uri') == '-' or set_ch.get('uri').startswith('http')):
|
||||
include.append(set_ch)
|
||||
continue
|
||||
|
||||
for i, ps in enumerate(cp):
|
||||
if set_ch.get('plugin') != None and set_ch.get('plugin') == ps.get('plugin'):
|
||||
include.append(ps)
|
||||
del cp[i]
|
||||
break
|
||||
tmp_cate_list.append({
|
||||
'uri': item['uri'],
|
||||
'name': item.get('name', ''),
|
||||
'list': include + cp
|
||||
})
|
||||
|
||||
else:
|
||||
tmp_cate_list.append({
|
||||
'uri': item['uri'],
|
||||
'name': item.get('name', ''),
|
||||
'list': PluginManager.setting_menus
|
||||
})
|
||||
|
||||
if cate_count > 0:
|
||||
copy_map.append({
|
||||
'name': category['name'],
|
||||
'list': tmp_cate_list,
|
||||
'count': cate_count
|
||||
})
|
||||
cls.menu_map = copy_map
|
||||
|
||||
make_dummy_cate = False
|
||||
for name, plugin_menu in plugin_menus.items():
|
||||
#F.logger.info(d(plugin_menu))
|
||||
#if 'uri' not in plugin_menu['menu']:
|
||||
# continue
|
||||
if plugin_menu['match'] == False:
|
||||
if make_dummy_cate == False:
|
||||
make_dummy_cate = True
|
||||
cls.menu_map.insert(len(cls.menu_map)-1, {
|
||||
'name':'미분류', 'count':0, 'list':[]
|
||||
})
|
||||
|
||||
if cate_count > 0:
|
||||
copy_map.append({
|
||||
'name': category['name'],
|
||||
'list': tmp_cate_list,
|
||||
'count': cate_count
|
||||
})
|
||||
cls.menu_map = copy_map
|
||||
|
||||
make_dummy_cate = False
|
||||
for name, plugin_menu in plugin_menus.items():
|
||||
#F.logger.info(d(plugin_menu))
|
||||
#if 'uri' not in plugin_menu['menu']:
|
||||
# continue
|
||||
if plugin_menu['match'] == False:
|
||||
if make_dummy_cate == False:
|
||||
make_dummy_cate = True
|
||||
cls.menu_map.insert(len(cls.menu_map)-1, {
|
||||
'name':'미분류', 'count':0, 'list':[]
|
||||
})
|
||||
|
||||
c = cls.menu_map[-2]
|
||||
c['count'] += 1
|
||||
c['list'].append(plugin_menu['menu'])
|
||||
c = cls.menu_map[-2]
|
||||
c['count'] += 1
|
||||
c['list'].append(plugin_menu['menu'])
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
#F.logger.warning(d(cls.menu_map))
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_menu_map(cls):
|
||||
#F.logger.warning(d(cls.menu_map))
|
||||
return cls.menu_map
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_setting_menu(cls, plugin):
|
||||
from .init_plugin import PluginManager
|
||||
for tmp in PluginManager.setting_menus:
|
||||
if tmp['plugin'] == plugin:
|
||||
return tmp
|
||||
|
||||
@@ -7,9 +7,8 @@ import traceback
|
||||
import zipfile
|
||||
|
||||
import requests
|
||||
from support import SupportFile, SupportSubprocess, SupportYaml
|
||||
|
||||
from framework import F
|
||||
from support import SupportFile, SupportSubprocess, SupportYaml
|
||||
|
||||
|
||||
class PluginManager:
|
||||
@@ -30,13 +29,13 @@ class PluginManager:
|
||||
tmps = os.listdir(plugin_path)
|
||||
add_plugin_list = []
|
||||
for t in tmps:
|
||||
if not t.startswith('_') and os.path.isdir(os.path.join(plugin_path, t)):
|
||||
if t.startswith('_') == False and t.startswith('.') == False and os.path.isdir(os.path.join(plugin_path, t)) and t != 'false' and t != 'tmp':
|
||||
add_plugin_list.append(t)
|
||||
cls.all_package_list[t] = {'pos':'normal', 'path':os.path.join(plugin_path, t), 'loading':(F.config.get('plugin_loading_only_devpath', None) != True)}
|
||||
|
||||
plugins = plugins + add_plugin_list
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
if F.config.get('plugin_loading_only_devpath', None) == True:
|
||||
@@ -59,12 +58,12 @@ class PluginManager:
|
||||
tmps = os.listdir(__)
|
||||
add_plugin_list = []
|
||||
for t in tmps:
|
||||
if not t.startswith('_') and os.path.isdir(os.path.join(__, t)):
|
||||
if t.startswith('_') == False and t.startswith('.') == False and os.path.isdir(os.path.join(__, t)) and t != 'false' and t != 'tmp':
|
||||
add_plugin_list.append(t)
|
||||
cls.all_package_list[t] = {'pos':'dev', 'path':os.path.join(__, t), 'loading':True}
|
||||
plugins = plugins + add_plugin_list
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
# plugin_loading_list
|
||||
@@ -79,8 +78,8 @@ class PluginManager:
|
||||
cls.all_package_list[_]['loading'] = False
|
||||
cls.all_package_list[_]['status'] = 'not_include_loading_list'
|
||||
plugins = new_plugins
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
# plugin_except_list
|
||||
@@ -95,8 +94,8 @@ class PluginManager:
|
||||
cls.all_package_list[_]['loading'] = False
|
||||
cls.all_package_list[_]['status'] = 'include_except_list'
|
||||
plugins = new_plugins
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
return plugins
|
||||
|
||||
@@ -113,43 +112,26 @@ class PluginManager:
|
||||
for plugin_name in plugins:
|
||||
F.logger.debug(f'[+] PLUGIN LOADING Start.. [{plugin_name}]')
|
||||
entity = cls.all_package_list[plugin_name]
|
||||
entity['version'] = '3'
|
||||
try:
|
||||
mod = __import__('%s' % (plugin_name), fromlist=[])
|
||||
mod_plugin_info = None
|
||||
try:
|
||||
mod_plugin_info = getattr(mod, 'plugin_info')
|
||||
entity['module'] = mod
|
||||
except Exception as exception:
|
||||
F.logger.info(f'[!] PLUGIN_INFO not exist : [{plugin_name}] - is FF')
|
||||
|
||||
if mod_plugin_info == None:
|
||||
try:
|
||||
mod = __import__(f'{plugin_name}.setup', fromlist=['setup'])
|
||||
entity['version'] = '4'
|
||||
except Exception as e:
|
||||
F.logger.error(f'Exception:{str(e)}')
|
||||
F.logger.error(traceback.format_exc())
|
||||
F.logger.warning(f'[!] NOT normal plugin : [{plugin_name}]')
|
||||
mod = __import__(f'{plugin_name}.setup', fromlist=['setup'])
|
||||
except Exception as e:
|
||||
F.logger.error(f'Exception:{str(e)}')
|
||||
F.logger.error(traceback.format_exc())
|
||||
F.logger.warning(f'[!] NOT normal plugin : [{plugin_name}]')
|
||||
continue
|
||||
|
||||
try:
|
||||
if entity['version'] != '4':
|
||||
mod_blue_print = getattr(mod, 'blueprint')
|
||||
else:
|
||||
entity['setup_mod'] = mod
|
||||
entity['P'] = getattr(mod, 'P')
|
||||
mod_blue_print = getattr(entity['P'], 'blueprint')
|
||||
entity['setup_mod'] = mod
|
||||
entity['P'] = getattr(mod, 'P')
|
||||
mod_blue_print = getattr(entity['P'], 'blueprint')
|
||||
if mod_blue_print:
|
||||
F.app.register_blueprint(mod_blue_print)
|
||||
except Exception as exception:
|
||||
#logger.error('Exception:%s', exception)
|
||||
#logger.error(traceback.format_exc())
|
||||
F.logger.warning(f'[!] BLUEPRINT not exist : [{plugin_name}]')
|
||||
cls.plugin_list[plugin_name] = entity
|
||||
#system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'success'
|
||||
#system.LogicPlugin.current_loading_plugin_list[plugin_name]['info'] = mod_plugin_info
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
F.logger.debug('no blueprint')
|
||||
cls.all_package_list[plugin_name]['loading'] = False
|
||||
@@ -157,36 +139,50 @@ class PluginManager:
|
||||
cls.all_package_list[plugin_name]['log'] = traceback.format_exc()
|
||||
|
||||
|
||||
|
||||
if not F.config['run_celery']:
|
||||
try:
|
||||
with F.app.app_context():
|
||||
F.db.create_all()
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
F.logger.debug('db.create_all error')
|
||||
|
||||
if F.config['run_celery']:
|
||||
for key, entity in cls.plugin_list.items():
|
||||
try:
|
||||
mod_plugin_load = getattr(entity['P'], 'plugin_load_celery')
|
||||
if mod_plugin_load:
|
||||
def func(mod_plugin_load, key):
|
||||
try:
|
||||
#F.logger.debug(f'[!] plugin_load_celery threading start : [{key}]')
|
||||
mod_plugin_load()
|
||||
#F.logger.debug(f'[!] plugin_load_celery threading end : [{key}]')
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
t = threading.Thread(target=func, args=(mod_plugin_load, key))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
if not F.config['run_flask']:
|
||||
return
|
||||
|
||||
for key, entity in cls.plugin_list.items():
|
||||
try:
|
||||
mod_plugin_load = None
|
||||
if entity['version'] == '3':
|
||||
mod_plugin_load = getattr(entity['module'], 'plugin_load')
|
||||
elif entity['version'] == '4':
|
||||
mod_plugin_load = getattr(entity['P'], 'plugin_load')
|
||||
mod_plugin_load = getattr(entity['P'], 'plugin_load')
|
||||
if mod_plugin_load:
|
||||
def func(mod_plugin_load, key):
|
||||
try:
|
||||
F.logger.debug(f'[!] plugin_load threading start : [{key}]')
|
||||
#mod.plugin_load()
|
||||
F.logger.info(f'[!] plugin_load threading start : [{key}]')
|
||||
mod_plugin_load()
|
||||
F.logger.debug(f'[!] plugin_load threading end : [{key}]')
|
||||
except Exception as exception:
|
||||
except Exception as e:
|
||||
F.logger.error('### plugin_load exception : %s', key)
|
||||
F.logger.error('Exception:%s', exception)
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
cls.all_package_list[key]['loading'] = False
|
||||
cls.all_package_list[key]['status'] = 'plugin_load error'
|
||||
@@ -199,42 +195,29 @@ class PluginManager:
|
||||
MenuManager.init_menu()
|
||||
F.logger.info(f"플러그인 로딩 실패로 메뉴 삭제2 : {key}")
|
||||
|
||||
t = threading.Thread(target=func, args=(mod_plugin_load, key))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
# mod는 위에서 로딩
|
||||
if key != 'mod':
|
||||
t = threading.Thread(target=func, args=(mod_plugin_load, key))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
#if key == 'mod':
|
||||
# t.join()
|
||||
except Exception as exception:
|
||||
except Exception as e:
|
||||
F.logger.debug(f'[!] PLUGIN_LOAD function not exist : [{key}]')
|
||||
#logger.error('Exception:%s', exception)
|
||||
#logger.error(traceback.format_exc())
|
||||
#logger.debug('no init_scheduler')
|
||||
try:
|
||||
mod_menu = None
|
||||
if entity['version'] == '3':
|
||||
mod_menu = getattr(entity['module'], 'menu')
|
||||
elif entity['version'] == '4':
|
||||
mod_menu = getattr(entity['P'], 'menu')
|
||||
|
||||
try:
|
||||
mod_menu = getattr(entity['P'], 'menu')
|
||||
if mod_menu and cls.all_package_list[key]['loading'] != False:
|
||||
cls.plugin_menus[key]= {'menu':mod_menu, 'match':False}
|
||||
if entity['version'] == '4':
|
||||
setting_menu = getattr(entity['P'], 'setting_menu')
|
||||
if setting_menu != None and cls.all_package_list[key]['loading'] != False:
|
||||
F.logger.info(f"메뉴 포함 : {key}")
|
||||
cls.setting_menus.append(setting_menu)
|
||||
|
||||
|
||||
setting_menu = getattr(entity['P'], 'setting_menu')
|
||||
setting_menu['plugin'] = entity['P'].package_name
|
||||
if setting_menu != None and cls.all_package_list[key]['loading'] != False:
|
||||
F.logger.info(f"확장 설정 : {key}")
|
||||
cls.setting_menus.append(setting_menu)
|
||||
except Exception as exception:
|
||||
F.logger.debug('no menu')
|
||||
F.logger.debug('### plugin_load threading all start.. : %s ', len(cls.plugin_list))
|
||||
# 모든 모듈을 로드한 이후에 app 등록, table 생성, start
|
||||
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -243,17 +226,9 @@ class PluginManager:
|
||||
def plugin_unload(cls):
|
||||
for key, entity in cls.plugin_list.items():
|
||||
try:
|
||||
if entity['version'] == '3':
|
||||
mod_plugin_unload = getattr(entity['module'], 'plugin_unload')
|
||||
elif entity['version'] == '4':
|
||||
mod_plugin_unload = getattr(entity['P'], 'plugin_unload')
|
||||
|
||||
#if plugin_name == 'rss':
|
||||
# continue
|
||||
#mod_plugin_unload = getattr(mod, 'plugin_unload')
|
||||
mod_plugin_unload = getattr(entity['P'], 'plugin_unload')
|
||||
if mod_plugin_unload:
|
||||
mod_plugin_unload()
|
||||
#mod.plugin_unload()
|
||||
except Exception as e:
|
||||
F.logger.error('module:%s', key)
|
||||
F.logger.error(f'Exception:{str(e)}')
|
||||
@@ -267,6 +242,7 @@ class PluginManager:
|
||||
|
||||
@classmethod
|
||||
def plugin_install(cls, plugin_git, zip_url=None, zip_filename=None):
|
||||
plugin_git = plugin_git.strip()
|
||||
is_git = True if plugin_git != None and plugin_git != '' else False
|
||||
ret = {}
|
||||
try:
|
||||
@@ -381,7 +357,7 @@ class PluginManager:
|
||||
tmps = os.listdir(plugins_path)
|
||||
for t in tmps:
|
||||
plugin_path = os.path.join(plugins_path, t)
|
||||
if t.startswith('_'):
|
||||
if t.startswith('_') or t.startswith('.'):
|
||||
continue
|
||||
if os.path.exists(os.path.join(plugin_path, '.git')):
|
||||
command = ['git', '-C', plugin_path, 'reset', '--hard', 'HEAD']
|
||||
@@ -392,14 +368,15 @@ class PluginManager:
|
||||
F.logger.debug(ret)
|
||||
else:
|
||||
F.logger.debug(f"{plugin_path} not git repo")
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_plugin_instance(cls, package_name):
|
||||
try:
|
||||
return cls.all_package_list[package_name]['P']
|
||||
if cls.all_package_list[package_name]['loading']:
|
||||
return cls.all_package_list[package_name]['P']
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -4,7 +4,6 @@ import traceback
|
||||
from flask import (jsonify, redirect, render_template, request,
|
||||
send_from_directory)
|
||||
from flask_login import login_required
|
||||
|
||||
from framework import F
|
||||
|
||||
|
||||
@@ -86,27 +85,31 @@ def open_file(path):
|
||||
@F.app.route("/file/<path:path>")
|
||||
@F.check_api
|
||||
def file2(path):
|
||||
# 윈도우 drive 필요 없음
|
||||
import platform
|
||||
if platform.system() == 'Windows':
|
||||
path = os.path.splitdrive(path)[1][1:]
|
||||
return send_from_directory('/', path, as_attachment=True)
|
||||
|
||||
|
||||
@F.app.route("/upload", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def upload():
|
||||
try:
|
||||
if request.method == 'POST':
|
||||
f = request.files['file']
|
||||
from werkzeug import secure_filename
|
||||
from werkzeug.utils import secure_filename
|
||||
upload_path = F.SystemModelSetting.get('path_upload')
|
||||
os.makedirs(upload_path, exist_ok=True)
|
||||
f.save(os.path.join(upload_path, secure_filename(f.filename)))
|
||||
return jsonify('success')
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
return jsonify('fail')
|
||||
|
||||
|
||||
@F.app.route("/videojs", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def videojs():
|
||||
data = {}
|
||||
data['play_title'] = request.form['play_title']
|
||||
@@ -116,9 +119,33 @@ def videojs():
|
||||
data['play_subtitle_src'] = request.form['play_subtitle_src']
|
||||
return render_template('videojs.html', data=data)
|
||||
|
||||
@F.app.route("/videojs_drm", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def videojs_drm():
|
||||
data = {}
|
||||
data['play_title'] = request.form['play_title']
|
||||
data['play_source_src'] = request.form['play_source_src']
|
||||
data['play_source_type'] = request.form['play_source_type']
|
||||
if 'play_subtitle_src' in request.form:
|
||||
data['play_subtitle_src'] = request.form['play_subtitle_src']
|
||||
return render_template('videojs_drm.html', data=data)
|
||||
|
||||
@F.app.route("/videojs_discord", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def videojs_og():
|
||||
data = {}
|
||||
"""
|
||||
data['play_title'] = request.form['play_title']
|
||||
data['play_source_src'] = request.form['play_source_src']
|
||||
data['play_source_type'] = request.form['play_source_type']
|
||||
if 'play_subtitle_src' in request.form:
|
||||
data['play_subtitle_src'] = request.form['play_subtitle_src']
|
||||
"""
|
||||
return render_template('videojs_discord.html', data=data)
|
||||
|
||||
|
||||
@F.app.route("/headers", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def headers():
|
||||
from support import d
|
||||
F.logger.info(d(request.headers))
|
||||
@@ -127,6 +154,7 @@ def headers():
|
||||
|
||||
# 3.10에서 이거 필수
|
||||
@F.socketio.on('connect', namespace=f'/framework')
|
||||
@login_required
|
||||
def connect():
|
||||
pass
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@ from framework import F
|
||||
|
||||
|
||||
def get_menu(full_query):
|
||||
match = re.compile(r'\/(?P<package_name>.*?)\/(?P<module_name>.*?)\/manual\/(?P<sub2>.*?)($|\?)').match(full_query)
|
||||
if match:
|
||||
return match.group('package_name'), match.group('module_name'), f"manual/{match.group('sub2')}"
|
||||
|
||||
match = re.compile(r'\/(?P<menu>.*?)\/manual\/(?P<sub2>.*?)($|\?)').match(full_query)
|
||||
if match:
|
||||
return match.group('menu'), 'manual', match.group('sub2')
|
||||
@@ -48,12 +52,14 @@ def jinja_initialize(app):
|
||||
app.jinja_env.globals.update(get_menu=get_menu)
|
||||
app.jinja_env.globals.update(get_theme=get_theme)
|
||||
app.jinja_env.globals.update(get_menu_map=MenuManager.get_menu_map)
|
||||
app.jinja_env.globals.update(get_setting_menu=MenuManager.get_setting_menu)
|
||||
app.jinja_env.globals.update(get_web_title=get_web_title)
|
||||
app.jinja_env.globals.update(dropzone=F.dropzone)
|
||||
|
||||
app.jinja_env.filters['get_menu'] = get_menu
|
||||
app.jinja_env.filters['get_theme'] = get_theme
|
||||
app.jinja_env.filters['get_menu_map'] = MenuManager.get_menu_map
|
||||
app.jinja_env.filters['get_setting_menu'] = MenuManager.get_setting_menu
|
||||
app.jinja_env.filters['get_web_title'] = get_web_title
|
||||
|
||||
app.jinja_env.auto_reload = True
|
||||
|
||||
@@ -4,17 +4,18 @@ import time
|
||||
import traceback
|
||||
|
||||
from flask import request
|
||||
from support import SingletonClass
|
||||
|
||||
from framework import F
|
||||
from support import SingletonClass
|
||||
|
||||
namespace = 'log'
|
||||
|
||||
@F.socketio.on('connect', namespace='/%s' % namespace)
|
||||
@F.login_required
|
||||
def socket_connect():
|
||||
F.logger.debug('log connect')
|
||||
|
||||
@F.socketio.on('start', namespace='/%s' % namespace)
|
||||
@F.login_required
|
||||
def socket_file(data):
|
||||
try:
|
||||
package = filename = None
|
||||
@@ -24,8 +25,8 @@ def socket_file(data):
|
||||
filename = data['filename']
|
||||
LogViewer.instance().start(package, filename, request.sid)
|
||||
F.logger.debug('start package:%s filename:%s sid:%s', package, filename, request.sid)
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
@F.socketio.on('disconnect', namespace='/%s' % namespace)
|
||||
@@ -33,8 +34,8 @@ def disconnect():
|
||||
try:
|
||||
LogViewer.instance().disconnect(request.sid)
|
||||
F.logger.debug('disconnect sid:%s', request.sid)
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -62,18 +63,17 @@ class WatchThread(threading.Thread):
|
||||
key = 'filename'
|
||||
value = self.filename
|
||||
if os.path.exists(logfile):
|
||||
with open(logfile, 'r') as f:
|
||||
with open(logfile, 'r', encoding='utf8') as f:
|
||||
f.seek(0, os.SEEK_END)
|
||||
while not self.stop_flag:
|
||||
line = f.readline()
|
||||
if not line:
|
||||
time.sleep(0.1) # Sleep briefly
|
||||
continue
|
||||
F.socketio.emit("add", {key : value, 'data': line}, namespace='/log', broadcast=True)
|
||||
F.socketio.emit("add", {key : value, 'data': line}, namespace='/log')
|
||||
F.logger.debug('WatchThread.. End %s', value)
|
||||
else:
|
||||
F.socketio.emit("add", {key : value, 'data': 'not exist logfile'}, namespace='/log', broadcast=True)
|
||||
|
||||
F.socketio.emit("add", {key : value, 'data': 'not exist logfile'}, namespace='/log')
|
||||
|
||||
|
||||
class LogViewer(SingletonClass):
|
||||
|
||||
@@ -49,8 +49,8 @@ class Scheduler(object):
|
||||
if flag_exit:
|
||||
self.remove_job("scheduler_check")
|
||||
#time.sleep(30)
|
||||
except Exception as exception:
|
||||
self.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Exception:{str(e)}")
|
||||
self.logger.error(traceback.format_exc())
|
||||
|
||||
def shutdown(self):
|
||||
@@ -233,21 +233,21 @@ class Job(object):
|
||||
if self.args is None:
|
||||
self.thread = threading.Thread(target=self.target_function, args=())
|
||||
else:
|
||||
self.thread = threading.Thread(target=self.target_function, args=(self.args,))
|
||||
self.thread = threading.Thread(target=self.target_function, args=self.args)
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
F.socketio.emit('notify', {'type':'success', 'msg':f"{self.description}<br>작업을 시작합니다." }, namespace='/framework', broadcast=True)
|
||||
F.socketio.emit('notify', {'type':'success', 'msg':f"{self.description}<br>작업을 시작합니다." }, namespace='/framework')
|
||||
self.thread.join()
|
||||
F.socketio.emit('notify', {'type':'success', 'msg':f"{self.description}<br>작업이 종료되었습니다." }, namespace='/framework', broadcast=True)
|
||||
F.socketio.emit('notify', {'type':'success', 'msg':f"{self.description}<br>작업이 종료되었습니다." }, namespace='/framework')
|
||||
self.end_time = datetime.now(timezone('Asia/Seoul'))
|
||||
self.running_timedelta = self.end_time - self.start_time
|
||||
self.status = 'success'
|
||||
if not F.scheduler.is_include(self.job_id):
|
||||
F.scheduler.remove_job_instance(self.job_id)
|
||||
self.count += 1
|
||||
except Exception as exception:
|
||||
except Exception as e:
|
||||
self.status = 'exception'
|
||||
F.logger.error('Exception:%s', exception)
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
finally:
|
||||
self.is_running = False
|
||||
|
||||
BIN
lib/framework/static/.DS_Store
vendored
Normal file
BIN
lib/framework/static/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -106,3 +106,5 @@ background-color: #ffff0080 !important;
|
||||
.dropdown-menu {
|
||||
margin:-2px;
|
||||
}
|
||||
|
||||
.modal { overflow: scroll !important; }
|
||||
160
lib/framework/static/css/showdown.css
Normal file
160
lib/framework/static/css/showdown.css
Normal file
@@ -0,0 +1,160 @@
|
||||
h3 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
height: 45px;
|
||||
min-height: 45px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.bars-lnk {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bars-lnk i {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.bars-lnk img {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
margin-top: -15px;
|
||||
margin-right: 15px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.lateral-menu {
|
||||
background-color: #333;
|
||||
color: rgb(144, 144, 144);
|
||||
width: 300px;
|
||||
|
||||
}
|
||||
|
||||
.lateral-menu label {
|
||||
color: rgb(144, 144, 144);
|
||||
}
|
||||
|
||||
.lateral-menu-content {
|
||||
padding-left: 10px;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: bold;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.lateral-menu-content .title{
|
||||
padding-top: 15px;
|
||||
font-size: 2em;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.lateral-menu-content-inner {
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 50px;
|
||||
padding-right: 10px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#preview {
|
||||
height: 97%;
|
||||
max-height: 97%;
|
||||
border: 1px solid #eee;
|
||||
overflow-y: scroll;
|
||||
width: 55%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #dfdfdf;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
padding: 0.125rem 0.3125rem 0.0625rem;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
.modal-wrapper {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
background-color: rgba(51,51,51,0.5);
|
||||
}
|
||||
|
||||
.modal-inner {
|
||||
margin-top: 200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 600px;
|
||||
height: 225px;
|
||||
background-color: #fff;
|
||||
opacity: 1;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-close-btn {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
color: #ff4336;
|
||||
}
|
||||
|
||||
.modal-close-btn:hover {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
color: #8d0002;
|
||||
}
|
||||
|
||||
.modal-topbar {
|
||||
clear: both;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.modal-inner .link-area {
|
||||
margin: 10px;
|
||||
height: 170px;
|
||||
|
||||
}
|
||||
|
||||
.modal-inner textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.version {
|
||||
color: white;
|
||||
font-size: 0.8em !important;
|
||||
}
|
||||
9
lib/framework/static/img/loader.svg
Normal file
9
lib/framework/static/img/loader.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="100px" height="100px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<circle cx="50" cy="50" r="31" stroke-width="4" stroke="#e15b64" stroke-dasharray="48.69468613064179 48.69468613064179" fill="none" stroke-linecap="round">
|
||||
<animateTransform attributeName="transform" type="rotate" dur="2.6315789473684212s" repeatCount="indefinite" keyTimes="0;1" values="0 50 50;360 50 50"></animateTransform>
|
||||
</circle>
|
||||
<circle cx="50" cy="50" r="26" stroke-width="4" stroke="#f8b26a" stroke-dasharray="40.840704496667314 40.840704496667314" stroke-dashoffset="40.840704496667314" fill="none" stroke-linecap="round">
|
||||
<animateTransform attributeName="transform" type="rotate" dur="2.6315789473684212s" repeatCount="indefinite" keyTimes="0;1" values="0 50 50;-360 50 50"></animateTransform>
|
||||
</circle>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
3845
lib/framework/static/js/chartjs-utils.js
Normal file
3845
lib/framework/static/js/chartjs-utils.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ if (tmp.length == 2) {
|
||||
var PACKAGE_NAME = tmp[1];
|
||||
var MODULE_NAME = tmp[2];
|
||||
var PAGE_NAME = "";
|
||||
} else if (tmp.length == 4){
|
||||
} else if (tmp.length > 3){
|
||||
var PACKAGE_NAME = tmp[1];
|
||||
var MODULE_NAME = tmp[2];
|
||||
var PAGE_NAME = tmp[3];
|
||||
@@ -23,8 +23,6 @@ $(window).on("load resize", function (event) {
|
||||
});
|
||||
|
||||
$('#command_modal').on('show.bs.modal', function (event) {
|
||||
console.log('111111111')
|
||||
console.log(event);
|
||||
})
|
||||
|
||||
///////////////////////////////////////
|
||||
@@ -113,7 +111,6 @@ function showModal(data='EMPTY', title='JSON', json=true) {
|
||||
data = JSON.stringify(data, null, 2);
|
||||
}
|
||||
document.getElementById("modal_body").innerHTML = '<pre style="white-space: pre-wrap;">' +data + '</pre>';
|
||||
|
||||
$("#large_modal").modal();
|
||||
}
|
||||
|
||||
@@ -168,7 +165,22 @@ function use_collapse(div, reverse=false) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// jquery extend function
|
||||
// post로 요청하면서 리다이렉트
|
||||
$.extend(
|
||||
{
|
||||
redirectPost: function(location, args)
|
||||
{
|
||||
var form = '';
|
||||
$.each( args, function( key, value ) {
|
||||
console.log(key);
|
||||
console.log(value);
|
||||
value = value.split('"').join('\"')
|
||||
form += '<input type="hidden" name="'+key+'" value="'+value+'">';
|
||||
});
|
||||
$('<form action="' + location + '" method="POST">' + form + '</form>').appendTo($(document.body)).submit();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -282,20 +294,3 @@ function pad(n, width) {
|
||||
return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n;
|
||||
}
|
||||
|
||||
// jquery extend function
|
||||
// post로 요청하면서 리다이렉트
|
||||
// 푹 자동에서 푹 기본 검색할때 사용
|
||||
$.extend(
|
||||
{
|
||||
redirectPost: function(location, args)
|
||||
{
|
||||
var form = '';
|
||||
$.each( args, function( key, value ) {
|
||||
console.log(key);
|
||||
console.log(value);
|
||||
value = value.split('"').join('\"')
|
||||
form += '<input type="hidden" name="'+key+'" value="'+value+'">';
|
||||
});
|
||||
$('<form action="' + location + '" method="POST">' + form + '</form>').appendTo($(document.body)).submit();
|
||||
}
|
||||
});
|
||||
@@ -15,11 +15,10 @@ $(document).ready(function(){
|
||||
|
||||
var protocol = window.location.protocol;
|
||||
var frameSocket = io.connect(protocol + "//" + document.domain + ":" + location.port + "/framework");
|
||||
console.log(frameSocket);
|
||||
|
||||
frameSocket.on('notify', function(data){
|
||||
$.notify({
|
||||
message : data['msg'],
|
||||
message : '<strong>' + data['msg'] + '</strong>',
|
||||
url: data['url'],
|
||||
target: '_self'
|
||||
},{
|
||||
@@ -29,7 +28,7 @@ frameSocket.on('notify', function(data){
|
||||
});
|
||||
|
||||
frameSocket.on('modal', function(data){
|
||||
m_modal(data.data, data.title, false);
|
||||
showModal(data.data, data.title, false);
|
||||
});
|
||||
|
||||
frameSocket.on('loading_hide', function(data){
|
||||
@@ -37,14 +36,12 @@ frameSocket.on('loading_hide', function(data){
|
||||
});
|
||||
|
||||
frameSocket.on('refresh', function(data){
|
||||
console.log('data')
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
|
||||
$('#command_modal').on('hide.bs.modal', function (e) {
|
||||
//e.preventDefault(); 있으면 동작 안함.
|
||||
console.log("ff global command_modal hide.bs.modal CATCH")
|
||||
$.ajax({
|
||||
url: `/global/ajax/command_modal_hide`,
|
||||
type: 'POST',
|
||||
@@ -74,13 +71,27 @@ $("body").on('click', '#globalLinkBtn', function(e) {
|
||||
window.location.href = url;
|
||||
});
|
||||
|
||||
$("body").on('click', '#globalReloadBtn', function(e) {
|
||||
e.preventDefault();
|
||||
location.reload();
|
||||
});
|
||||
// global_link_btn 모두 찾아 변경
|
||||
|
||||
$("body").on('click', '#globalSettingSaveBtn', function(e){
|
||||
e.preventDefault();
|
||||
globalSettingSave();
|
||||
if (globalSettingSaveBefore()) {
|
||||
globalSettingSave();
|
||||
}
|
||||
});
|
||||
|
||||
function globalSettingSaveBefore() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function globalSettingSaveAfter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function globalSettingSave() {
|
||||
var formData = getFormdata('#setting');
|
||||
$.ajax({
|
||||
@@ -94,6 +105,7 @@ function globalSettingSave() {
|
||||
$.notify('<strong>설정을 저장하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
globalSettingSaveAfter();
|
||||
} else {
|
||||
$.notify('<strong>설정 저장에 실패하였습니다.</strong>', {
|
||||
type: 'warning'
|
||||
@@ -106,7 +118,10 @@ function globalSettingSave() {
|
||||
$("body").on('click', '#globalEditBtn', function(e) {
|
||||
e.preventDefault();
|
||||
file = $(this).data('file');
|
||||
console.log(file);
|
||||
if (file == null) {
|
||||
var tag = $(this).data('tag');
|
||||
file = $('#' + tag).val();
|
||||
}
|
||||
$.ajax({
|
||||
url: '/global/ajax/is_available_edit',
|
||||
type: "POST",
|
||||
@@ -237,106 +252,187 @@ $("body").on('click', '#globalImmediatelyExecutePageBtn', function(e){
|
||||
});
|
||||
|
||||
|
||||
$("body").on('click', '#globalDbDeleteBtn', function(e){
|
||||
$("body").on('click', '#globalDbDeleteDayBtn', function(e){
|
||||
e.preventDefault();
|
||||
document.getElementById("confirm_title").innerHTML = "DB 삭제";
|
||||
document.getElementById("confirm_body").innerHTML = "전체 목록을 삭제 하시겠습니까?";
|
||||
$('#confirm_button').attr('onclick', "globalDbDelete();");
|
||||
$("#confirm_modal").modal();
|
||||
return;
|
||||
var tag_id = $(this).data('tag_id');
|
||||
var day = $('#' + tag_id).val();
|
||||
globalConfirmModal('DB 삭제', "최근 " + day + "일 이내 데이터를 제외하고 삭제 하시겠습니까?", function() {
|
||||
globalDbDelete(day);
|
||||
});
|
||||
});
|
||||
|
||||
function globalDbDelete() {
|
||||
$("body").on('click', '#globalDbDeleteBtn', function(e){
|
||||
e.preventDefault();
|
||||
globalConfirmModal('DB 삭제', "전체 목록을 삭제 하시겠습니까?", function() {
|
||||
globalDbDelete(0);
|
||||
});
|
||||
});
|
||||
|
||||
function globalDbDelete(day) {
|
||||
$.ajax({
|
||||
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/reset_db',
|
||||
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/db_delete',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {},
|
||||
data: {day:day},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data) {
|
||||
$.notify('<strong>삭제하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
if (data == -1) {
|
||||
$.notify('<strong>삭제에 실패하였습니다.</strong>',{
|
||||
type: 'warning'
|
||||
});
|
||||
} else {
|
||||
$.notify('<strong>'+data+'개를 삭제하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
globalRequestSearch('1');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$("body").on('click', '#globalDbDeletePageBtn', function(e){
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
$("body").on('click', '#globalDbDeleteDayPageBtn', function(e){
|
||||
e.preventDefault();
|
||||
document.getElementById("confirm_title").innerHTML = "DB 삭제";
|
||||
document.getElementById("confirm_body").innerHTML = "전체 목록을 삭제 하시겠습니까?";
|
||||
$('#confirm_button').attr('onclick', "globalDbDeletePage();");
|
||||
$("#confirm_modal").modal();
|
||||
return;
|
||||
var tag_id = $(this).data('tag_id');
|
||||
var day = $('#' + tag_id).val();
|
||||
globalConfirmModal('DB 삭제', day + "일 제외 목록을 삭제 하시겠습니까?", function() {
|
||||
globalDbDeletePage(day);
|
||||
});
|
||||
});
|
||||
|
||||
function globalDbDeletePage() {
|
||||
|
||||
$("body").on('click', '#globalDbDeletePageBtn', function(e){
|
||||
e.preventDefault();
|
||||
globalConfirmModal('DB 삭제', "최근 " + day + "일 이내 데이터를 제외하고 삭제 하시겠습니까?", function() {
|
||||
globalDbDeletePage(0);
|
||||
});
|
||||
});
|
||||
|
||||
function globalDbDeletePage(day) {
|
||||
$.ajax({
|
||||
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/reset_db',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {sub:sub},
|
||||
data: {day:day},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data) {
|
||||
$.notify('<strong>삭제하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
if (data == -1) {
|
||||
$.notify('<strong>삭제에 실패하였습니다.</strong>',{
|
||||
type: 'warning'
|
||||
});
|
||||
} else {
|
||||
$.notify('<strong>'+data+'개를 삭제하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
globalRequestSearch('1');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$("body").on('click', '#globalDbDeleteItemBtn', function(e){
|
||||
e.preventDefault();
|
||||
var db_id = $(this).data('id');
|
||||
$.ajax({
|
||||
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/db_delete_item',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {db_id:db_id},
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret) {
|
||||
notify('삭제하였습니다.', 'success');
|
||||
globalRequestSearch(current_page);
|
||||
} else {
|
||||
notify('삭제에 실패하였습니다.', 'warning');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on('click', '#globalDbDeleteItemPageBtn', function(e){
|
||||
e.preventDefault();
|
||||
var db_id = $(this).data('id');
|
||||
$.ajax({
|
||||
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/db_delete_item',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {db_id:db_id},
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret) {
|
||||
notify('삭제하였습니다.', 'success');
|
||||
globalRequestSearch(current_page);
|
||||
} else {
|
||||
notify('삭제에 실패하였습니다.', 'warning');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("body").on('click', '#globalJsonBtn', function(e){
|
||||
e.preventDefault();
|
||||
showModal(current_data.list[$(this).data('idx')]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// Global - 함수
|
||||
///////////////////////////////////////
|
||||
|
||||
function globalSendCommand(command, arg1, arg2, arg3, modal_title, callback) {
|
||||
console.log("globalSendCommand [" + command + '] [' + arg1 + '] [' + arg2 + '] [' + arg3 + '] [' + modal_title + '] [' + callback + ']');
|
||||
console.log('/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/command');
|
||||
function globalSendCommand(command, arg1, arg2, arg3, callback) {
|
||||
var url = '/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/command';
|
||||
return globalSendCommandByUrl(url, command, arg1, arg2, arg3, callback);
|
||||
}
|
||||
|
||||
function globalSendCommandByUrl(url, command, arg1, arg2, arg3, callback) {
|
||||
$.ajax({
|
||||
url: '/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/command',
|
||||
url: url,
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{command:command, arg1:arg1, arg2:arg2, arg3},
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
console.log(ret)
|
||||
if (ret.msg != null) notify(ret.msg, ret.ret);
|
||||
if (ret.modal != null) showModal(ret.modal, modal_title, false);
|
||||
if (ret.json != null) showModal(ret.json, modal_title, true);
|
||||
if (ret.modal != null) showModal(ret.modal, ret.title, false);
|
||||
if (ret.json != null) showModal(ret.json, ret.title, true);
|
||||
if (callback != null) callback(ret);
|
||||
if (ret.reload) location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function globalSendCommandPage(command, arg1, arg2, arg3, modal_title, callback) {
|
||||
console.log("globalSendCommandPage [" + command + '] [' + arg1 + '] [' + arg2 + '] [' + arg3 + '] [' + modal_title + '] [' + callback + ']');
|
||||
console.log('/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/command');
|
||||
function globalSendCommandPage(command, arg1, arg2, arg3, callback) {
|
||||
var url = '/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/command';
|
||||
return globalSendCommandPageByUrl(url, command, arg1, arg2, arg3, callback);
|
||||
}
|
||||
|
||||
function globalSendCommandPageByUrl(url, command, arg1, arg2, arg3, callback) {
|
||||
$.ajax({
|
||||
url: '/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/command',
|
||||
url: url,
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{command:command, arg1:arg1, arg2:arg2, arg3},
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret.msg != null) notify(ret.msg, ret.ret);
|
||||
if (ret.modal != null) m_modal(ret.modal, modal_title, false);
|
||||
if (ret.json != null) m_modal(ret.json, modal_title, true);
|
||||
if (ret.modal != null) showModal(ret.modal, ret.title, false);
|
||||
if (ret.json != null) showModal(ret.json, ret.title, true);
|
||||
if (callback != null) callback(ret);
|
||||
if (ret.reload) location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -400,6 +496,10 @@ function make_page_html(data) {
|
||||
str += '<button id="gloablSearchPageBtn" data-page="' + (data.last_page+1) + '" type="button" class="btn btn-secondary">»</button>'
|
||||
}
|
||||
|
||||
if (data.last_page != data.total_page) {
|
||||
str += '<button id="gloablSearchPageBtn" data-page="' + (data.total_page) + '" type="button" class="btn btn-secondary">'+data.total_page+'</button>'
|
||||
}
|
||||
|
||||
str += '</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
@@ -431,6 +531,22 @@ $("body").on('click', '#globalSearchResetBtn', function(e){
|
||||
});
|
||||
|
||||
|
||||
$("body").on('change', '#option1', function(e){
|
||||
e.preventDefault();
|
||||
globalRequestSearch(1);
|
||||
});
|
||||
|
||||
$("body").on('change', '#option2', function(e){
|
||||
e.preventDefault();
|
||||
globalRequestSearch(1);
|
||||
});
|
||||
|
||||
$("body").on('change', '#order', function(e){
|
||||
e.preventDefault();
|
||||
globalRequestSearch(1);
|
||||
});
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// 파일 선택 모달
|
||||
@@ -483,7 +599,6 @@ let listdir = (path = '/', only_dir = true) => {
|
||||
},
|
||||
dataType: 'json'
|
||||
}).done((datas) => {
|
||||
console.log(datas)
|
||||
if (datas.length == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -510,8 +625,6 @@ let listdir = (path = '/', only_dir = true) => {
|
||||
} else {
|
||||
//new_path = (path !== path_spliter) ? path + path_spliter + $(evt.currentTarget).text() : path + $(evt.currentTarget).text();
|
||||
new_path = $(evt.currentTarget).data('value');
|
||||
console.log(new_path)
|
||||
console.log(evt)
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -587,3 +700,23 @@ function ResizeTextArea() {
|
||||
|
||||
///////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// Confirm MODAL
|
||||
///////////////////////////////////////
|
||||
|
||||
function globalConfirmModal(title, body, func) {
|
||||
$("#confirm_title").html(title);
|
||||
$("#confirm_body").html(body);
|
||||
//$('#confirm_button').attr('onclick', func);
|
||||
$("body").on('click', '#confirm_button', function(e){
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
func();
|
||||
});
|
||||
$("#confirm_modal").modal();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
14
lib/framework/static/js/ff_global_plugin.js
Normal file
14
lib/framework/static/js/ff_global_plugin.js
Normal file
@@ -0,0 +1,14 @@
|
||||
///////////////////////////////////////
|
||||
// 자주 사용하는 플러그인에 전용 명령
|
||||
|
||||
function pluginRcloneLs(remote_path) {
|
||||
var url = '/rclone/ajax/config/command';
|
||||
globalSendCommandByUrl(url, "ls", remote_path);
|
||||
}
|
||||
|
||||
function pluginRcloneSize(remote_path) {
|
||||
var url = '/rclone/ajax/config/command';
|
||||
globalSendCommandByUrl(url, "size", remote_path);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@ function j_button_group(h) {
|
||||
}
|
||||
|
||||
// primary, secondary, success, danger, warning, info, light, dark, white
|
||||
function j_button(id, text, data={}, color='primary', outline=true, small=false) {
|
||||
function j_button(id, text, data={}, color='primary', outline=true, small=false, _class='') {
|
||||
var str = '<button id="'+id+'" name="'+id+'" class="btn btn-sm btn';
|
||||
if (outline) {
|
||||
str += '-outline';
|
||||
}
|
||||
str += '-' + color+'';
|
||||
str += ' ' + _class;
|
||||
if (small) {
|
||||
str += ' py-0" style="font-size: 0.8em;"';
|
||||
} else {
|
||||
@@ -35,9 +36,14 @@ function j_button_small(id, text, data={}, color='primary', outline=true) {
|
||||
|
||||
|
||||
function j_row_start(padding='10', align='center') {
|
||||
var str = '<div class="row" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
||||
var str = '<div class="row chover" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
||||
return str;
|
||||
}
|
||||
function j_row_start_hover(padding='10', align='center') {
|
||||
var str = '<div class="row my_hover" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
||||
return str;
|
||||
}
|
||||
|
||||
function j_col(w, h, align='left') {
|
||||
var str = '<div class="col-sm-' + w + ' " style="text-align: '+align+'; word-break:break-all;">';
|
||||
str += h;
|
||||
@@ -45,6 +51,13 @@ function j_col(w, h, align='left') {
|
||||
return str;
|
||||
}
|
||||
|
||||
function j_col_with_class(w, h, align='left', _class='context_menu') {
|
||||
var str = '<div class="col-sm-' + w + ' '+_class+'" style="text-align: '+align+'; word-break:break-all;">';
|
||||
str += h;
|
||||
str += '</div>';
|
||||
return str;
|
||||
}
|
||||
|
||||
function j_col_wide(w, h, align='left') {
|
||||
var str = '<div class="col-sm-' + w + ' " style="padding:0px; margin:0px; text-align: '+align+'; word-break:break-all;">';
|
||||
str += h;
|
||||
@@ -87,57 +100,101 @@ function j_row_info(left, right, l=2, r=8) {
|
||||
function j_progress(id, width, label) {
|
||||
var str = '';
|
||||
str += '<div class="progress" style="height: 25px;">'
|
||||
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
|
||||
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 " style="margin-top:2px">'+label+'</div>';
|
||||
str += '<div id="'+id+'" class="progress-bar bg-success" style="width:'+width+'%"></div>';
|
||||
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin-top:2px">'+label+'</div>';
|
||||
str += '</div>'
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
function j_td(text, width='10', align='center', colspan='1') {
|
||||
str = '<td scope="col" colspan="'+colspan+'" style="width:'+width+'%; text-align:'+align+';">'+ text + '</td>';
|
||||
return str;
|
||||
}
|
||||
|
||||
function j_th(text, width='10', align='center', colspan='1') {
|
||||
str = '<th scope="col" colspan="'+colspan+'" style="width:'+width+'%; text-align:'+align+';">'+ text + '</td>';
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function make_log(key, value, left=2, right=10) {
|
||||
row = m_col(left, key, aligh='right');
|
||||
row += m_col(right, value, aligh='left');
|
||||
function j_info_text(key, value, left=2, right=10) {
|
||||
row = j_row_start(0);
|
||||
row += j_col(left, '<strong>' + key + '</strong>', aligh='right');
|
||||
row += j_col(right, value, aligh='left');
|
||||
row += j_row_end();
|
||||
return row;
|
||||
}
|
||||
|
||||
function j_info_text_left(key, value, left=3, right=9) {
|
||||
row = j_row_start(0);
|
||||
row += j_col(left, '<strong>' + key + '</strong>', aligh='left');
|
||||
row += j_col(right, value, aligh='left');
|
||||
row += j_row_end();
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
function j_tab_make(data) {
|
||||
str = '<nav><div class="nav nav-tabs" id="nav-tab" role="tablist">';
|
||||
for (i in data) {
|
||||
if (data[i][2]) {
|
||||
str += '<a class="nav-item nav-link active" id="tab_head_'+data[i][0]+'" data-toggle="tab" href="#tab_content_'+data[i][0]+'" role="tab">'+data[i][1]+'</a>';
|
||||
} else {
|
||||
str += '<a class="nav-item nav-link" id="tab_head_'+data[i][0]+'" data-toggle="tab" href="#tab_content_'+data[i][0]+'" role="tab">'+data[i][1]+'</a>';
|
||||
}
|
||||
}
|
||||
str += '</div></nav>';
|
||||
str += '<div class="tab-content" id="nav-tabContent">';
|
||||
for (i in data) {
|
||||
if (data[i][2]) {
|
||||
str += '<div class="tab-pane fade show active" id="tab_content_'+data[i][0]+'" role="tabpanel" ></div>';
|
||||
} else {
|
||||
str += '<div class="tab-pane fade show" id="tab_content_'+data[i][0]+'" role="tabpanel" ></div>';
|
||||
}
|
||||
}
|
||||
str += '</div>';
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
// javascript에서 화면 생성
|
||||
function text_color(text, color='red') {
|
||||
return '<span style="color:'+color+'; font-weight:bold">' + text + '</span>';
|
||||
}
|
||||
|
||||
|
||||
function j_pre(text) {
|
||||
return '<pre style="word-wrap: break-word;white-space: pre-wrap;white-space: -moz-pre-wrap;white-space: -pre-wrap;white-space: -o-pre-wrap;word-break:break-all;">'+text+'</pre>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -277,10 +334,7 @@ document.addEventListener("DOMContentLoaded", function(){
|
||||
|
||||
|
||||
|
||||
function m_row_start_hover(padding='10', align='center') {
|
||||
var str = '<div class="row my_hover" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
||||
return str;
|
||||
}
|
||||
|
||||
function m_row_start_top(padding='10') {
|
||||
return m_row_start(padding, 'top');
|
||||
}
|
||||
@@ -309,46 +363,5 @@ function m_row_start_color2(padding='10', align='center') {
|
||||
|
||||
|
||||
|
||||
function m_tab_head(name, active) {
|
||||
if (active) {
|
||||
var str = '<a class="nav-item nav-link active" id="id_'+name+'" data-toggle="tab" href="#'+name+'" role="tab">'+name+'</a>';
|
||||
} else {
|
||||
var str = '<a class="nav-item nav-link" id="id_'+name+'" data-toggle="tab" href="#'+name+'" role="tab">'+name+'</a>';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function m_tab_content(name, content, active) {
|
||||
if (active) {
|
||||
var str = '<div class="tab-pane fade show active" id="'+name+'" role="tabpanel" >';
|
||||
} else {
|
||||
var str = '<div class="tab-pane fade show" id="'+name+'" role="tabpanel" >';
|
||||
}
|
||||
str += content;
|
||||
str += '</div>'
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function m_progress2(id, width, label) {
|
||||
var str = '';
|
||||
str += '<div class="progress" style="height: 25px;">'
|
||||
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
|
||||
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin:0px; margin-top:2px">'+label+'</div>';
|
||||
str += '</div>'
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
35
lib/framework/static/js/showdown-prettify.js
Normal file
35
lib/framework/static/js/showdown-prettify.js
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Google Prettify
|
||||
// A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)
|
||||
// hints to showdown's HTML output.
|
||||
//
|
||||
|
||||
(function () {
|
||||
|
||||
var prettify = function () {
|
||||
return [
|
||||
{
|
||||
type: 'output',
|
||||
filter: function (source) {
|
||||
return source.replace(/(<pre[^>]*>)?[\n\s]?<code([^>]*)>/gi, function (match, pre, codeClass) {
|
||||
if (pre) {
|
||||
return '<pre class="prettyprint linenums"><code' + codeClass + '>';
|
||||
} else {
|
||||
return ' <code class="prettyprint">';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
// Client-side export
|
||||
if (typeof window !== 'undefined' && window.showdown && window.showdown.extensions) {
|
||||
window.showdown.extensions.prettify = prettify;
|
||||
}
|
||||
// Server-side export
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = prettify;
|
||||
}
|
||||
|
||||
}());
|
||||
3
lib/framework/static/js/showdown_2.1.0.js
Normal file
3
lib/framework/static/js/showdown_2.1.0.js
Normal file
File diff suppressed because one or more lines are too long
@@ -35,6 +35,12 @@
|
||||
<script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.4.0/js/bootstrap4-toggle.min.js"></script>
|
||||
<!-- end 토글 -->
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.ui.position.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body class="body ">
|
||||
@@ -50,7 +56,8 @@
|
||||
</div>
|
||||
</main>
|
||||
<div class="loading" id="loading">
|
||||
<img src="/static/img/loading.gif" />
|
||||
<!-- <img src="/static/img/loading.gif" /> -->
|
||||
<img src="/static/img/loader.svg" />
|
||||
</div>
|
||||
{{ modals() }}
|
||||
</body>
|
||||
@@ -59,3 +66,5 @@
|
||||
<!-- 글로벌 버튼이 모두 나오고 처리-->
|
||||
<script src="{{ url_for('static', filename='js/sjva_global1.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/ff_global1.js') }}"></script>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/ff_global_plugin.js') }}"></script>
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
{{ macros.m_tab_head_end() }}
|
||||
</nav>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
{{ macros.m_tab_content_start('이전', true) }}
|
||||
{{ macros.m_tab_content_start('old', true) }}
|
||||
<div>
|
||||
<textarea id="log" class="col-md-12" rows="30" charswidth="23" disabled style="background-color:#ffffff;visibility:hidden"></textarea>
|
||||
</div>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
{{ macros.m_tab_content_start('실시간', false) }}
|
||||
{{ macros.m_tab_content_start('new', false) }}
|
||||
<div>
|
||||
<textarea id="add" class="col-md-12" rows="30" charswidth="23" disabled style="background-color:#ffffff;visibility:visible"></textarea>
|
||||
</div>
|
||||
@@ -52,6 +52,7 @@ $(window).resize(function() {
|
||||
|
||||
var protocol = window.location.protocol;
|
||||
var socket = io.connect(protocol + "//" + document.domain + ":" + location.port + "/log");
|
||||
|
||||
socket.emit("start", {'package':'{{package}}'} );
|
||||
socket.on('on_start', function(data){
|
||||
document.getElementById("log").innerHTML += data.data;
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
|
||||
<!-- 탭 -->
|
||||
{% macro m_tab_head_start() %}
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_tab_head_end() %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_tab_head(name, title, active) %}
|
||||
@@ -39,12 +39,25 @@
|
||||
<a class="nav-item nav-link" id="tab_{{name}}" data-toggle="tab" href="#{{name}}" role="tab">{{title}}</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
<!----------------------------------------------------------------->
|
||||
|
||||
|
||||
<!-- SETTING -->
|
||||
<!-- SETTING -->
|
||||
<!-- SETTING -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!------------------------------------------------------------------>
|
||||
<!-- 설정 -->
|
||||
|
||||
<!-- SETTING 기본 틀-->
|
||||
{% macro setting_top(left='', padding='10') %}
|
||||
@@ -57,6 +70,16 @@
|
||||
<div class='col-sm-9'>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro setting_top_big(left='', padding='10') %}
|
||||
<div class='row' style="padding-top: {{padding}}px; padding-bottom:{{padding}}px; align-items: center;">
|
||||
<div class='col-sm-3 set-left'>
|
||||
{% if left != '' %}
|
||||
<strong><h4>{{ left }}</h4></strong>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class='col-sm-9'>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro setting_bottom(desc=None, padding_top='5') %}
|
||||
{% if desc is not none %}
|
||||
<div style="padding-left:20px; padding-top:{{padding_top}}px;">
|
||||
@@ -247,18 +270,39 @@
|
||||
|
||||
|
||||
<!-- 스케쥴링 작동 버튼-->
|
||||
{% macro setting_global_scheduler_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
|
||||
{% macro global_setting_scheduler_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if is_include == True %}
|
||||
{% if is_include == True or is_include == "True" %}
|
||||
<input id="globalSchedulerSwitchBtn" name="globalSchedulerSwitchBtn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="globalSchedulerSwitchBtn" name="globalSchedulerSwitchBtn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
{% if is_running == True %}
|
||||
{% if is_running == True or is_running == "True" %}
|
||||
<span style="padding-left:10px; padding-top: 8px; font-weight: bold;">실행중</span>
|
||||
{% else %}
|
||||
{% if is_include == True %}
|
||||
{% if is_include == True or is_include == "True" %}
|
||||
<span style="padding-left:10px; padding-top: 8px; ">대기중</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- 스케쥴링 작동 버튼 페이지 -->
|
||||
{% macro global_setting_scheduler_button_page(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if is_include == True or is_include == "True" %}
|
||||
<input id="globalSchedulerSwitchPageBtn" name="globalSchedulerSwitchPageBtn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="globalSchedulerSwitchPageBtn" name="globalSchedulerSwitchPageBtn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
{% if is_running == True or is_running == "True" %}
|
||||
<span style="padding-left:10px; padding-top: 8px; font-weight: bold;">실행중</span>
|
||||
{% else %}
|
||||
{% if is_include == True or is_include == "True" %}
|
||||
<span style="padding-left:10px; padding-top: 8px; ">대기중</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -268,13 +312,116 @@
|
||||
|
||||
|
||||
|
||||
setting_gole
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- NOT SETTING -->
|
||||
<!-- NOT SETTING -->
|
||||
<!-- NOT SETTING -->
|
||||
<!-- SELECT Dummy
|
||||
option을 script로 넣을 때 사용
|
||||
예: 시스템 - 전체로그
|
||||
-->
|
||||
{% macro setting_select_empty(id, title, col='9', desc=None, value=None) %}
|
||||
{{ setting_top(title) }}
|
||||
<div class="input-group col-sm-{{col}}">
|
||||
<div id="{{id}}_div" name="{{id}}_div"></div>
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
{% macro setting_input_int(id, left, value='', min='', max='', placeholder='', desc=None) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
<input id="{{ id }}" name="{{ id }}" type="number" class="form-control form-control-sm"
|
||||
{% if min != '' %}
|
||||
min="{{ min }}"
|
||||
{% endif %}
|
||||
{% if max != '' %}
|
||||
max="{{ max }}"
|
||||
{% endif %}
|
||||
{% if placeholder != '' %}
|
||||
placeholder="{{ placeholder }}"
|
||||
{% endif %}
|
||||
value="{{ value }}">
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
<!-- 토글버튼형식 -->
|
||||
{% macro setting_checkbox(id, left, value, desc='') %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if value == True or value == 'True' or value == 'true' or value == 'On' %}
|
||||
<input id="{{ id }}" name="{{ id }}" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="{{ id }}" name="{{ id }}" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!------------------------------------------------------------------>
|
||||
<!-- 설정 외 -->
|
||||
|
||||
<!-- 리스트 div로 꾸밀때 헤드 -->
|
||||
{% macro m_hr_head_top() %}
|
||||
<div class="d-inline-block"></div>
|
||||
<hr style="width: 100%; margin:0px; background-color:#808080;">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_hr_head_bottom() %}
|
||||
<hr style="width: 100%; margin:0px; margin-bottom:10px; margin-top:2px; background-color:#808080; height:2px" />
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- 버튼 그룹 -->
|
||||
{% macro m_button_group(buttons) %}
|
||||
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">
|
||||
@@ -304,6 +451,14 @@
|
||||
{{ setting_bottom(desc, padding_top='-5') }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_text_big(id, left, value='', desc=None) %}
|
||||
{{ setting_top_big(left) }}
|
||||
<div style="padding-left:20px; padding-top:-5px;">
|
||||
<span id={{id}}><h4>{{value}}</h4></span>
|
||||
</div>
|
||||
{{ setting_bottom(desc, padding_top='-5') }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_text_go(id, left, value='', desc=None, padding=10) %}
|
||||
{{ setting_top(left, padding) }}
|
||||
<div style="padding-left:20px; padding-top:-5px;">
|
||||
@@ -354,219 +509,208 @@
|
||||
|
||||
|
||||
|
||||
<!-- SELECT Dummy
|
||||
option을 script로 넣을 때 사용
|
||||
예: 시스템 - 전체로그
|
||||
-->
|
||||
{% macro setting_select_empty(id, title, col='9', desc=None, value=None) %}
|
||||
{{ setting_top(title) }}
|
||||
<div class="input-group col-sm-{{col}}">
|
||||
<div id="{{id}}_div" name="{{id}}_div"></div>
|
||||
|
||||
|
||||
{% macro m_modal_start(id, title, size) %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="{{id}}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog {{size}}">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="{{id}}_title">{{title}}</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body" id="{{id}}_modal_body" style="word-break:break-all;">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_modal_end() %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-warning" data-dismiss="modal">닫기 (취소)</button>
|
||||
</div>
|
||||
<div class="loading" id="modal_loading">
|
||||
<img src="/static/img/loading.gif" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
</div>
|
||||
<!-- Modal end -->
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 삭제해야함 --------------------------------------------------------->
|
||||
<!--
|
||||
{% macro setting_radio(id, title, radios, value=None, desc=None, disabled=False) %}
|
||||
{{ setting_top(title) }}
|
||||
<div class="input-group col-sm-9">
|
||||
{% for r in radios %}
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
{% if value|int == loop.index0 %}
|
||||
<input id="{{id}}{{loop.index0}}" type="radio" class="custom-control-input" name="{{id}}" value="{{loop.index0}}" checked {% if disabled %} disabled {% endif %}>
|
||||
{% else %}
|
||||
<input id="{{id}}{{loop.index0}}" type="radio" class="custom-control-input" name="{{id}}" value="{{loop.index0}}" {% if disabled %} disabled {% endif %}>
|
||||
{% endif %}
|
||||
<label class="custom-control-label" for="{{id}}{{loop.index0}}">{{r}}</label>
|
||||
{% macro m_modal_end_with_button(buttons) %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">
|
||||
{% for b in buttons %}
|
||||
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary"
|
||||
{% if b|length > 2 %}
|
||||
{% for d in b[2] %}
|
||||
data-{{d[0]}}="{{d[1]}}""
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>{{b[1]}}</button>
|
||||
{% endfor %}
|
||||
<button type="button" class="btn btn-sm btn-warning" data-dismiss="modal">닫기 (취소)</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loading" id="modal_loading">
|
||||
<img src="/static/img/loading.gif" />
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
-->
|
||||
|
||||
<!-- 그룹화 하지 않음.. 삭제-->
|
||||
<!--
|
||||
{% macro setting_button(buttons, left='', desc='') %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-9">
|
||||
{% for b in buttons %}
|
||||
{% if not loop.first %}
|
||||
<span class='text-left' style="padding-left:5px; padding-top:0px">
|
||||
{% endif %}
|
||||
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary">{{b[1]}}</button>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
-->
|
||||
<!----------------------------------------------------------------->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% macro setting_input_int(id, left, value='', min='', max='', placeholder='', desc=None) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
<input id="{{ id }}" name="{{ id }}" type="number" class="form-control form-control-sm"
|
||||
{% if min != '' %}
|
||||
min="{{ min }}"
|
||||
{% endif %}
|
||||
{% if max != '' %}
|
||||
max="{{ max }}"
|
||||
{% endif %}
|
||||
{% if placeholder != '' %}
|
||||
placeholder="{{ placeholder }}"
|
||||
{% endif %}
|
||||
value="{{ value }}">
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal end -->
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
<!-- 토글버튼형식 -->
|
||||
{% macro setting_checkbox(id, left, value, desc='') %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if value == 'True' or value == 'true' or value == 'On' %}
|
||||
<input id="{{ id }}" name="{{ id }}" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="{{ id }}" name="{{ id }}" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
|
||||
{% macro print_md(id, text) %}
|
||||
<div id="{{id}}_div" data-text="{{text}}"></div>
|
||||
<script type="text/javascript">
|
||||
ret = converter.makeHtml($('#{{id}}_div').data('text'));
|
||||
$('#{{id}}_div').html(ret);
|
||||
</script>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro print_md1(id, text) %}
|
||||
|
||||
<script type="text/javascript">
|
||||
ret = converter.makeHtml($('#{{id}}_div').data('text'));
|
||||
</script>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
<!----------------------------------------------------------->
|
||||
<!----------------------------------------------------------->
|
||||
<!----------------------------------------------------------->
|
||||
<!----------------------------------------------------------->
|
||||
<!----------------------------------------------------------->
|
||||
<!--이하 정리 필요------------------------>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 일반적인 체크박스 -->
|
||||
{% macro setting_default_checkbox(id, left, label, value, desc='') %}
|
||||
@@ -584,31 +728,6 @@ option을 script로 넣을 때 사용
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
<!-- 스케쥴러 스위치 체크박스 전용-->
|
||||
{% macro setting_scheduler_switch(left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지'], is_include='False', is_running='False') %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if is_include == 'True' %}
|
||||
<input id="scheduler_swtich_btn" name="scheduler_swtich_btn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="scheduler_swtich_btn" name="scheduler_swtich_btn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
{% if is_running == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">동작중</span>
|
||||
{% else %}
|
||||
{% if is_include == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">대기중</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
@@ -637,6 +756,26 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% macro select(id, options, col='3', value=None) %}
|
||||
<div class="input-group col-sm-{{col}}" style="padding-left:0px; padding-top:0px">
|
||||
|
||||
<select id="{{id}}" name="{{id}}" class="form-control form-control-sm">
|
||||
{% for item in options %}
|
||||
{% if value is not none and value == item[0] %}
|
||||
<option value="{{ item[0] }}" selected>{{item[1]}}</option>
|
||||
{% else %}
|
||||
<option value="{{ item[0] }}">{{item[1]}}</option>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
<!-- select -->
|
||||
{% macro setting_select(id, title, options, col='9', desc=None, value=None) %}
|
||||
{{ setting_top(title) }}
|
||||
@@ -655,21 +794,6 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro select(id, options, col='3', value=None) %}
|
||||
<div class="input-group col-sm-{{col}}" style="padding-left:0px; padding-top:0px">
|
||||
|
||||
<select id="{{id}}" name="{{id}}" class="form-control form-control-sm">
|
||||
{% for item in options %}
|
||||
{% if value is not none and value == item[0] %}
|
||||
<option value="{{ item[0] }}" selected>{{item[1]}}</option>
|
||||
{% else %}
|
||||
<option value="{{ item[0] }}">{{item[1]}}</option>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- select + 버튼 -->
|
||||
@@ -703,15 +827,6 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--progress-bar-striped progress-bar-animated-->
|
||||
{% macro setting_progress(id, left='', desc='') %}
|
||||
{{ setting_top(left) }}
|
||||
@@ -725,66 +840,6 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- 스케쥴링 작동 버튼-->
|
||||
{% macro setting_scheduler_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if is_include == 'True' %}
|
||||
<input id="scheduler" name="scheduler" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="scheduler" name="scheduler" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
{% if is_running == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">동작중</span>
|
||||
{% else %}
|
||||
{% if is_include == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">대기중</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
|
||||
{% macro setting_global_scheduler_sub_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if is_include == 'True' %}
|
||||
<input id="global_scheduler_sub" name="global_scheduler_sub" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="global_scheduler_sub" name="global_scheduler_sub" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
{% if is_running == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">동작중</span>
|
||||
{% else %}
|
||||
{% if is_include == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">대기중</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro setting_global_scheduler_sublogic_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-3">
|
||||
{% if is_include == 'True' %}
|
||||
<input id="global_scheduler_sublogic" name="global_scheduler_sublogic" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
{% else %}
|
||||
<input id="global_scheduler_sublogic" name="global_scheduler_sublogic" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
|
||||
{% endif %}
|
||||
{% if is_running == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">동작중</span>
|
||||
{% else %}
|
||||
{% if is_include == 'True' %}
|
||||
<span style="padding-left:10px; padding-top: 8px;">대기중</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
@@ -803,14 +858,7 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro m_hr_head_top() %}
|
||||
<div class="d-inline-block"></div>
|
||||
<hr style="width: 100%; margin:0px; background-color:#808080;">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_hr_head_bottom() %}
|
||||
<hr style="width: 100%; margin:0px; margin-bottom:10px; margin-top:2px; background-color:#808080; height:2px" />
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_button(id, text) %}
|
||||
<button id="{{id}}" name="{{id}}" class="btn btn-sm btn-outline-primary">{{text}}</button>
|
||||
@@ -836,59 +884,6 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
|
||||
|
||||
|
||||
{% macro m_modal_start(id, title, size) %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="{{id}}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog {{size}}">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="{{id}}_title">{{title}}</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body" id="modal_body" style="word-break:break-all;">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_modal_end() %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
|
||||
</div>
|
||||
<div class="loading" id="modal_loading">
|
||||
<img src="/static/img/loading.gif" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal end -->
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro m_modal_start2(id, title, size) %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="{{id}}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog {{size}}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="{{id}}_title">{{title}}</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="loading" id="modal_loading">
|
||||
<img src="/static/img/loading.gif" />
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_modal_end2() %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal end -->
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% macro row_start(padding='10') %}
|
||||
<div class='row' style="padding-top: {{padding}}px; padding-bottom:{{padding}}px; align-items: center;">
|
||||
@@ -1002,16 +997,3 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 다른이름으로 정의함. 나중에 삭제 -->
|
||||
{% macro buttons(buttons, left='', desc='') %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-9">
|
||||
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">
|
||||
{% for b in buttons %}
|
||||
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary">{{b[1]}}</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
{% endmacro %}
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="modal-body" id="modal_body" style="word-break:break-all;">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
|
||||
<button type="button" class="btn btn-warning" data-dismiss="modal">닫기</button>
|
||||
<!--<button type="button" class="btn btn-primary">Save changes</button>-->
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,7 +70,7 @@
|
||||
<div class="modal-footer">
|
||||
<button type="button" id='select_local_file_modal_confirm_btn' class="btn btn-success" data-dismiss="modal">선택
|
||||
</button>
|
||||
<button type="button" id='select_local_file_modal_cancel_btn' class="btn btn-default" data-dismiss="modal">닫기
|
||||
<button type="button" id='select_local_file_modal_cancel_btn' class="btn btn-warning" data-dismiss="modal">닫기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{% if 'uri' in category and category['uri'].startswith('http') %}
|
||||
<li class="nav-item"> <a class="nav-link" href="{{ category['uri']}}" target="_blank">{{category['name']}}</a></li>
|
||||
{% elif 'uri' in category and category['uri'].startswith('http') == False %}
|
||||
<li class="nav-item"> <a class="nav-link" href="{{ category['uri']}}">{{category['name']}}</a></li>
|
||||
<li class="nav-item"> <a class="nav-link" href="/{{ category['uri']}}">{{category['name']}}</a></li>
|
||||
{% else %}
|
||||
<!--{{ category }}-->
|
||||
<li class="nav-item dropdown">
|
||||
@@ -134,10 +134,11 @@
|
||||
{% if current_menu[0] == plugin['uri'] and 'list' in plugin %}
|
||||
{% for module in plugin['list'] %}
|
||||
{% if module['uri'] == current_menu[1] and 'list' in module%}
|
||||
<!--{{ module }}-->
|
||||
<!-- {{ module }} -->
|
||||
<ul class="nav nav-pills bg-light shadow text-dark">
|
||||
{% for page in module['list'] %}
|
||||
{% if current_menu[2] == page['uri'] %}
|
||||
<!--{{ current_menu }}-->
|
||||
{% if current_menu[2]!= None and page['uri'].startswith(current_menu[2]) %}
|
||||
<li class="nav-item"><a class="nav-link active" href="/{{ current_menu[0] }}/{{ current_menu[1] }}/{{ page['uri'] }}">{{page['name']}}</a></li>
|
||||
{% else %}
|
||||
<li class="nav-item"><a class="nav-link" href="/{{ current_menu[0] }}/{{ current_menu[1] }}/{{ page['uri'] }}">{{page['name']}}</a></li>
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% filter markdown %}
|
||||
{{ data }}
|
||||
{% endfilter %}
|
||||
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js?autorun=true&lang=css&lang=python&skin=sunburst"></script>
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
img{
|
||||
<script src="{{ url_for('static', filename='js/showdown_2.1.0.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/showdown-prettify.js') }}"></script>
|
||||
<link href="{{ url_for('static', filename='css/showdown.css') }}" rel="stylesheet">
|
||||
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="md_div" data-url="{{ arg }}"></div>
|
||||
<div id="content_div" data-url="{{ arg }}"></div>
|
||||
|
||||
<meta id="text" data-text="{{data}}">
|
||||
<div id="text_div"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
//$('#main_container').attr('class', 'container-fluid');
|
||||
});
|
||||
$(document).ready(function(){
|
||||
var converter = new showdown.Converter({extensions: ['prettify']});
|
||||
converter.setOption('tables', true);
|
||||
converter.setOption('strikethrough', true);
|
||||
converter.setOption('ghCodeBlocks',true);
|
||||
|
||||
text = $('#text').data('text');
|
||||
if (window.location.href.endsWith('.yaml')) {
|
||||
text = "```" + text + "```";
|
||||
}
|
||||
html = converter.makeHtml(text);
|
||||
$('#text_div').html(html);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
30
lib/framework/templates/manual_old.html
Normal file
30
lib/framework/templates/manual_old.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% filter markdown %}
|
||||
{{ data }}
|
||||
{% endfilter %}
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
img{
|
||||
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="md_div" data-url="{{ arg }}"></div>
|
||||
<div id="content_div" data-url="{{ arg }}"></div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
//$('#main_container').attr('class', 'container-fluid');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
<html>
|
||||
<title>{{data['play_title']}}</title>
|
||||
<script src="https://vjs.zencdn.net/7.11.4/video.min.js"></script>
|
||||
<link href="https://vjs.zencdn.net/7.11.4/video-js.css" rel="stylesheet" />
|
||||
@@ -63,3 +65,4 @@ player.ready(function(){
|
||||
|
||||
player.play();
|
||||
</script>
|
||||
</html>
|
||||
|
||||
90
lib/framework/templates/videojs_discord.html
Normal file
90
lib/framework/templates/videojs_discord.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<title>aaaa</title>
|
||||
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport">
|
||||
<meta content="ie=edge" http-equiv="X-UA-Compatible">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<link href="/media/avatar.png" rel="icon" type="image/jpeg">
|
||||
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" rel="stylesheet" />
|
||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet" />
|
||||
<link href="https://unpkg.com/swiper@7/swiper-bundle.min.css" rel="stylesheet" />
|
||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||
<link href="/css/style.css" rel="stylesheet" />
|
||||
<link href="/css/style_dark.css" rel="stylesheet" />
|
||||
<link href="/media/favicon.png" rel="icon" type="image/jpeg">
|
||||
<meta property="og:site_name" content="aaaaaaaaaaaaaaaaa" />
|
||||
<meta property="og:url" content="https://ff.soju6jan.synology.me/gds_tool/api/route/streaming.mp4?apikey=ooo5298ooo&type=file&id=1gtpG7CAUKTWu6wxWtCKx-XN01PMz70v8" />
|
||||
<meta property="og:type" content="video.other" />
|
||||
<meta property="og:title" content="Mini rengar xD" />
|
||||
<meta property="og:image" content="https://outplays.eu/Q5THkfY3/thumbnail.png" />
|
||||
<meta property="og:video" content="https://ff.soju6jan.synology.me/gds_tool/api/route/streaming.mp4?apikey=ooo5298ooo&type=file&id=1gtpG7CAUKTWu6wxWtCKx-XN01PMz70v8" />
|
||||
<meta property="og:video:type" content="video/mp4" />
|
||||
<meta property="og:video:secure_url" content="https://ff.soju6jan.synology.me/gds_tool/api/route/streaming.mp4?apikey=ooo5298ooo&type=file&id=1gtpG7CAUKTWu6wxWtCKx-XN01PMz70v8" />
|
||||
<meta property="og:video:height" content="1080" />
|
||||
<meta property="og:video:width" content="1920" />
|
||||
<meta property="og:image:height" content="1080" />
|
||||
<meta property="og:image:width" content="1920" />
|
||||
|
||||
|
||||
|
||||
<script src="https://vjs.zencdn.net/7.11.4/video.min.js"></script>
|
||||
<link href="https://vjs.zencdn.net/7.11.4/video-js.css" rel="stylesheet" />
|
||||
|
||||
<body bgcolor='black'>
|
||||
<video id=player width=960 height=540 class="video-js vjs-default-skin vjs-16-9" autoplay controls>
|
||||
<source
|
||||
src="https://ff.soju6jan.synology.me/gds_tool/api/route/streaming.mp4?apikey=ooo5298ooo&type=file&id=1gtpG7CAUKTWu6wxWtCKx-XN01PMz70v8"
|
||||
type="application/x-mpegURL" />
|
||||
</video>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var subtitle_src = "aaaa";
|
||||
let options = {
|
||||
html5: {
|
||||
nativeTextTracks: false
|
||||
},
|
||||
playbackRates: [.5, .75, 1, 1.5, 2],
|
||||
controls: true,
|
||||
preload: "auto",
|
||||
controlBar: {
|
||||
playToggle: false,
|
||||
pictureInPictureToggle: false,
|
||||
remainingTimeDisplay: true,
|
||||
qualitySelector: true,
|
||||
}
|
||||
};
|
||||
let player = videojs('player', options);
|
||||
|
||||
player.ready(function(){
|
||||
// set subtitle track
|
||||
if (subtitle_src != "") {
|
||||
var suburl = subtitle_src.replace(/&/g, '&');
|
||||
let captionOption = {
|
||||
kind: 'captions',
|
||||
srclang: 'ko',
|
||||
label: 'Korean',
|
||||
src: suburl,
|
||||
mode: 'showing'
|
||||
};
|
||||
player.addRemoteTextTrack(captionOption);
|
||||
|
||||
var settings = this.textTrackSettings;
|
||||
settings.setValues({
|
||||
"backgroundColor": "#000",
|
||||
"backgroundOpacity": "0",
|
||||
"edgeStyle": "uniform",
|
||||
});
|
||||
settings.updateDisplay();
|
||||
}
|
||||
else {
|
||||
var tracks = player.textTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
var track = tracks[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
player.play();
|
||||
</script>
|
||||
@@ -34,8 +34,8 @@ class Util(object):
|
||||
paging['count'] = count
|
||||
F.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page'])
|
||||
return paging
|
||||
except Exception as exception:
|
||||
F.logger.debug('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.debug(f"Exception:{str(e)}")
|
||||
F.logger.debug(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ class Util(object):
|
||||
ret['dirname'] = max_filename.replace('/%s' % ret['filename'], '')
|
||||
ret['max_size'] = max_size
|
||||
return ret
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -81,8 +81,8 @@ class Util(object):
|
||||
import shutil
|
||||
shutil.rmtree(zip_path)
|
||||
return True
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@@ -92,12 +92,12 @@ class Util(object):
|
||||
def make_apikey(url):
|
||||
from framework import SystemModelSetting
|
||||
url = url.format(ddns=SystemModelSetting.get('ddns'))
|
||||
if SystemModelSetting.get_bool('auth_use_apikey'):
|
||||
if SystemModelSetting.get_bool('use_apikey'):
|
||||
if url.find('?') == -1:
|
||||
url += '?'
|
||||
else:
|
||||
url += '&'
|
||||
url += 'apikey=%s' % SystemModelSetting.get('auth_apikey')
|
||||
url += 'apikey=%s' % SystemModelSetting.get('apikey')
|
||||
return url
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
VERSION="4.0.47"
|
||||
VERSION="4.1.40"
|
||||
@@ -1,5 +1,6 @@
|
||||
# 순서 바꾸지 말 것
|
||||
import os, sys, traceback, re, threading, time, queue
|
||||
import os, sys, traceback, re, threading, time, queue, json, shutil, yaml
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from flask import Blueprint, render_template, jsonify, redirect, request
|
||||
from sqlalchemy import desc, or_
|
||||
|
||||
@@ -94,8 +94,8 @@ class FfmpegQueue(object):
|
||||
self.download_thread = threading.Thread(target=self.download_thread_function, args=())
|
||||
self.download_thread.daemon = True
|
||||
self.download_thread.start()
|
||||
except Exception as exception:
|
||||
self.P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f"Exception:{str(e)}")
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -107,8 +107,8 @@ class FfmpegQueue(object):
|
||||
if self.current_ffmpeg_count < self.max_ffmpeg_count:
|
||||
break
|
||||
time.sleep(5)
|
||||
except Exception as exception:
|
||||
self.P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f"Exception:{str(e)}")
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
self.P.logger.error('current_ffmpeg_count : %s', self.current_ffmpeg_count)
|
||||
self.P.logger.error('max_ffmpeg_count : %s', self.max_ffmpeg_count)
|
||||
@@ -153,8 +153,8 @@ class FfmpegQueue(object):
|
||||
f.start()
|
||||
self.current_ffmpeg_count += 1
|
||||
self.download_queue.task_done()
|
||||
except Exception as exception:
|
||||
self.P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f"Exception:{str(e)}")
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
def ffmpeg_listener(self, **arg):
|
||||
@@ -203,8 +203,8 @@ class FfmpegQueue(object):
|
||||
self.entity_list.append(entity)
|
||||
self.download_queue.put(entity)
|
||||
return True
|
||||
except Exception as exception:
|
||||
self.P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f"Exception:{str(e)}")
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@@ -270,8 +270,8 @@ class FfmpegQueue(object):
|
||||
self.entity_list = new_list
|
||||
ret['ret'] = 'refresh'
|
||||
return ret
|
||||
except Exception as exception:
|
||||
self.P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f"Exception:{str(e)}")
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ from support import SupportYaml
|
||||
from . import (Logic, default_route, default_route_single_module,
|
||||
get_model_setting)
|
||||
|
||||
from loguru import logger as logger1
|
||||
|
||||
|
||||
class PluginBase(object):
|
||||
package_name = None
|
||||
@@ -52,6 +54,7 @@ class PluginBase(object):
|
||||
self.home_module = setting.get('home_module')
|
||||
self.status = "init_success"
|
||||
self.config = {}
|
||||
self.recent_menu_plugin_except_list = setting.get('recent_menu_plugin_except_list', [])
|
||||
except Exception as e:
|
||||
self.logger.error(f'Exception:{str(e)}')
|
||||
self.logger.error(traceback.format_exc())
|
||||
@@ -59,10 +62,16 @@ class PluginBase(object):
|
||||
|
||||
def set_module_list(self, mod_list):
|
||||
try:
|
||||
# self.module_list = []
|
||||
for mod in mod_list:
|
||||
logger1.debug(mod)
|
||||
mod_ins = mod(self)
|
||||
# self.logger.debug(mod_ins)
|
||||
logger1.debug(mod_ins)
|
||||
self.module_list.append(mod_ins)
|
||||
|
||||
if self.home_module == None:
|
||||
self.home_module = self.module_list[0].name
|
||||
except Exception as e:
|
||||
F.logger.error(f'[{self.package_name}] Exception:{str(e)}')
|
||||
F.logger.error(traceback.format_exc())
|
||||
@@ -77,16 +86,31 @@ class PluginBase(object):
|
||||
|
||||
|
||||
def plugin_load(self):
|
||||
self.logic.plugin_load()
|
||||
if self.logic:
|
||||
self.logic.plugin_load()
|
||||
|
||||
def plugin_load_celery(self):
|
||||
if self.logic:
|
||||
self.logic.plugin_load_celery()
|
||||
|
||||
|
||||
def plugin_unload(self):
|
||||
self.logic.plugin_unload()
|
||||
if self.logic:
|
||||
self.logic.plugin_unload()
|
||||
|
||||
def get_first_manual_path(self):
|
||||
for __ in self.menu['list']:
|
||||
if __['uri'] == 'manual' and len(__['list']) > 0:
|
||||
return __['list'][0]['uri']
|
||||
|
||||
def get_module(self, sub):
|
||||
try:
|
||||
for module in self.module_list:
|
||||
if module.name == sub:
|
||||
return module
|
||||
except Exception as e:
|
||||
self.logger.error(f'Exception:{str(e)}')
|
||||
#self.P.logger.error(traceback.format_exc())
|
||||
|
||||
def create_plugin_instance(config):
|
||||
ins = PluginBase(config)
|
||||
|
||||
@@ -32,14 +32,39 @@ class Logic(object):
|
||||
if self.P.ModelSetting is not None:
|
||||
for module in self.P.module_list:
|
||||
key = f'{module.name}_auto_start'
|
||||
if self.P.ModelSetting.has_key(key) and self.P.ModelSetting.get_bool(key):
|
||||
key2 = f'{module.name}_interval'
|
||||
if self.P.ModelSetting.has_key(key) and self.P.ModelSetting.get_bool(key) and self.P.ModelSetting.has_key(key2):
|
||||
self.scheduler_start(module.name)
|
||||
|
||||
if module.page_list is not None:
|
||||
for page_instance in module.page_list:
|
||||
key = f'{module.name}_{page_instance.name}_auto_start'
|
||||
if self.P.ModelSetting.has_key(key) and self.P.ModelSetting.get_bool(key):
|
||||
self.scheduler_start_sub(module.name, page_instance.name)
|
||||
|
||||
key1 = f'{module.name}_db_auto_delete'
|
||||
key2 = f'{module.name}_db_delete_day'
|
||||
if self.P.ModelSetting.has_key(key1) and self.P.ModelSetting.has_key(key2) and self.P.ModelSetting.get_bool(key1):
|
||||
try: module.db_delete(self.P.ModelSetting.get_int(key2))
|
||||
except: pass
|
||||
if module.page_list == None:
|
||||
continue
|
||||
for page_instance in module.page_list:
|
||||
key1 = f'{module.name}_{page_instance.name}_db_auto_delete'
|
||||
key2 = f'{module.name}_{page_instance.name}_db_delete_day'
|
||||
if self.P.ModelSetting.has_key(key1) and self.P.ModelSetting.has_key(key2) and self.P.ModelSetting.get_bool(key1):
|
||||
try: page_instance.db_delete(self.P.ModelSetting.get_int(key2))
|
||||
except: pass
|
||||
|
||||
def plugin_load_celery(self):
|
||||
self.P.logger.debug('%s plugin_load_celery', self.P.package_name)
|
||||
for module in self.P.module_list:
|
||||
module.plugin_load_celery()
|
||||
if module.page_list is not None:
|
||||
for page_instance in module.page_list:
|
||||
page_instance.plugin_load_celery()
|
||||
|
||||
|
||||
|
||||
def db_init(self):
|
||||
try:
|
||||
@@ -88,7 +113,7 @@ class Logic(object):
|
||||
try:
|
||||
job_id = '%s_%s' % (self.P.package_name, module_name)
|
||||
module = self.get_module(module_name)
|
||||
job = Job(self.P.package_name, job_id, module.get_scheduler_interval(), self.scheduler_function, module.get_scheduler_desc(), args=module_name)
|
||||
job = Job(self.P.package_name, job_id, module.get_scheduler_interval(), self.scheduler_function, module.get_scheduler_desc(), args=(module_name,))
|
||||
F.scheduler.add_job_instance(job)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
@@ -112,15 +137,25 @@ class Logic(object):
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
def reset_db(self, module_name):
|
||||
|
||||
def db_delete(self, module_name, page_name, day):
|
||||
try:
|
||||
module = self.get_module(module_name)
|
||||
return module.reset_db()
|
||||
if module == None:
|
||||
return False
|
||||
if page_name != None:
|
||||
page = module.get_page(page_name)
|
||||
if page != None:
|
||||
return page.db_delete(day)
|
||||
else:
|
||||
return module.db_delete(day)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
|
||||
def one_execute(self, module_name):
|
||||
self.P.logger.debug('one_execute :%s', module_name)
|
||||
try:
|
||||
@@ -166,6 +201,7 @@ class Logic(object):
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
"""
|
||||
def process_telegram_data(self, data, target=None):
|
||||
try:
|
||||
for module in self.P.module_list:
|
||||
@@ -174,7 +210,7 @@ class Logic(object):
|
||||
except Exception as e:
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -303,12 +339,23 @@ class Logic(object):
|
||||
|
||||
|
||||
def arg_to_dict(self, arg):
|
||||
"""
|
||||
import urllib.parse
|
||||
|
||||
tmp = urllib.parse.unquote(arg)
|
||||
tmps = tmp.split('&')
|
||||
ret = {}
|
||||
for tmp in tmps:
|
||||
_ = tmp.split('=')
|
||||
_ = tmp.split('=', 1)
|
||||
ret[_[0]] = _[1]
|
||||
return ret
|
||||
"""
|
||||
import html
|
||||
import urllib.parse
|
||||
char = '||!||'
|
||||
arg = arg.replace('&', char)
|
||||
tmp = html.unescape(arg)
|
||||
tmp = urllib.parse.unquote(tmp)
|
||||
tmp = dict(urllib.parse.parse_qs(tmp, keep_blank_values=True))
|
||||
ret = {k: v[0].replace(char, '&') for k, v in tmp.items()}
|
||||
return ret
|
||||
|
||||
@@ -39,6 +39,8 @@ class PluginModuleBase(object):
|
||||
|
||||
def get_page(self, page_name):
|
||||
try:
|
||||
if self.page_list == None:
|
||||
return
|
||||
for page in self.page_list:
|
||||
if page_name == page.name:
|
||||
return page
|
||||
@@ -48,10 +50,22 @@ class PluginModuleBase(object):
|
||||
|
||||
|
||||
def process_menu(self, page, req):
|
||||
if self.page_list is not None:
|
||||
page_ins = self.get_page(page)
|
||||
if page_ins != None:
|
||||
return page_ins.process_menu(req)
|
||||
from framework import F
|
||||
|
||||
try:
|
||||
if self.page_list is not None:
|
||||
page_ins = self.get_page(page)
|
||||
if page_ins != None:
|
||||
return page_ins.process_menu(req)
|
||||
|
||||
arg = self.P.ModelSetting.to_dict() if self.P.ModelSetting != None else {}
|
||||
arg['path_data'] = F.config['path_data']
|
||||
arg['is_include'] = F.scheduler.is_include(self.get_scheduler_name())
|
||||
arg['is_running'] = F.scheduler.is_running(self.get_scheduler_name())
|
||||
return render_template(f'{self.P.package_name}_{self.name}_{page}.html', arg=arg)
|
||||
except Exception as e:
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
return render_template('sample.html', title=f"PluginModuleBase-process_menu{self.P.package_name}/{self.name}/{page}")
|
||||
|
||||
def process_ajax(self, sub, req):
|
||||
@@ -69,12 +83,18 @@ class PluginModuleBase(object):
|
||||
def scheduler_function(self):
|
||||
pass
|
||||
|
||||
def reset_db(self):
|
||||
pass
|
||||
def db_delete(self, day):
|
||||
if self.web_list_model != None:
|
||||
return self.web_list_model.delete_all(day)
|
||||
|
||||
|
||||
def plugin_load(self):
|
||||
pass
|
||||
|
||||
def plugin_load_celery(self):
|
||||
pass
|
||||
|
||||
|
||||
def plugin_unload(self):
|
||||
pass
|
||||
|
||||
@@ -115,12 +135,37 @@ class PluginModuleBase(object):
|
||||
pass
|
||||
|
||||
|
||||
def arg_to_dict(self, arg):
|
||||
return self.P.logic.arg_to_dict(arg)
|
||||
|
||||
def get_scheduler_name(self):
|
||||
return f'{self.P.package_name}_{self.name}'
|
||||
|
||||
|
||||
def process_discord_data(self, data):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
def start_celery(self, func, on_message=None, *args, page=None):
|
||||
from framework import F
|
||||
if F.config['use_celery']:
|
||||
result = func.apply_async(args)
|
||||
try:
|
||||
if on_message != None:
|
||||
ret = result.get(on_message=on_message, propagate=True)
|
||||
else:
|
||||
ret = result.get()
|
||||
except:
|
||||
ret = result.get()
|
||||
else:
|
||||
if on_message == None:
|
||||
ret = func(*args)
|
||||
else:
|
||||
if page == None:
|
||||
ret = func(self, *args)
|
||||
else:
|
||||
ret = func(page, *args)
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -153,7 +198,8 @@ class PluginPageBase(object):
|
||||
arg = self.P.ModelSetting.to_dict()
|
||||
return render_template(f'{self.P.package_name}_{self.parent.name}_{self.name}.html', arg=arg)
|
||||
except Exception as e:
|
||||
pass
|
||||
self.P.logger.error(f'Exception:{str(e)}')
|
||||
self.P.logger.error(traceback.format_exc())
|
||||
|
||||
return render_template('sample.html', title=f"PluginPageBase-process_menu --- {self.P.package_name}/{self.parent.name}/{self.name}")
|
||||
|
||||
@@ -176,6 +222,9 @@ class PluginPageBase(object):
|
||||
def plugin_load(self):
|
||||
pass
|
||||
|
||||
def plugin_load_celery(self):
|
||||
pass
|
||||
|
||||
# logic
|
||||
def plugin_unload(self):
|
||||
pass
|
||||
@@ -207,3 +256,41 @@ class PluginPageBase(object):
|
||||
pass
|
||||
|
||||
|
||||
def arg_to_dict(self, arg):
|
||||
return self.P.logic.arg_to_dict(arg)
|
||||
|
||||
|
||||
def get_page(self, page_name):
|
||||
return self.parent.get_page(page_name)
|
||||
|
||||
|
||||
def get_module(self, module_name):
|
||||
return self.parent.get_module(module_name)
|
||||
|
||||
def process_discord_data(self, data):
|
||||
pass
|
||||
|
||||
def db_delete(self, day):
|
||||
if self.web_list_model != None:
|
||||
return self.web_list_model.delete_all(day)
|
||||
|
||||
|
||||
def start_celery(self, func, on_message, *args):
|
||||
return self.parent.start_celery(func, on_message, *args, page=self)
|
||||
|
||||
"""
|
||||
def start_celery(self, func, on_message=None, *args):
|
||||
from framework import F
|
||||
if F.config['use_celery']:
|
||||
result = func.apply_async(args)
|
||||
try:
|
||||
if on_message != None:
|
||||
ret = result.get(on_message=on_message, propagate=True)
|
||||
else:
|
||||
ret = result.get()
|
||||
except:
|
||||
ret = result.get()
|
||||
else:
|
||||
ret = func(*args)
|
||||
return ret
|
||||
"""
|
||||
@@ -1,3 +1,4 @@
|
||||
import sqlite3
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
@@ -45,7 +46,7 @@ class ModelBase(F.db.Model):
|
||||
paging['next_page'] = False
|
||||
paging['current_page'] = current_page
|
||||
paging['count'] = count
|
||||
F.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page'])
|
||||
#F.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page'])
|
||||
return paging
|
||||
except Exception as e:
|
||||
cls.P.logger.error(f'Exception:{str(e)}')
|
||||
@@ -89,24 +90,30 @@ class ModelBase(F.db.Model):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def delete_all(cls, days=None):
|
||||
def delete_all(cls, day=None):
|
||||
count = -1
|
||||
try:
|
||||
with F.app.app_context():
|
||||
if days == None:
|
||||
F.db.session.query(cls).delete()
|
||||
F.db.session.commit()
|
||||
if day == None or day in [0, '0']:
|
||||
count = F.db.session.query(cls).delete()
|
||||
else:
|
||||
now = datetime.now()
|
||||
ago = now - timedelta(days=int(days))
|
||||
ago = now - timedelta(days=int(day))
|
||||
#ago.hour = 0
|
||||
#ago.minute = 0
|
||||
count = F.db.session.query(cls).filter(cls.created_time > ago).delete()
|
||||
cls.P.logger.info(f"delete_all {days=} {count=}")
|
||||
return True
|
||||
count = F.db.session.query(cls).filter(cls.created_time < ago).delete()
|
||||
cls.P.logger.info(f"delete_all {day=} {count=}")
|
||||
F.db.session.commit()
|
||||
|
||||
db_file = F.app.config['SQLALCHEMY_BINDS'][cls.P.package_name].replace('sqlite:///', '').split('?')[0]
|
||||
connection = sqlite3.connect(db_file)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('VACUUM;')
|
||||
connection.close()
|
||||
except Exception as e:
|
||||
cls.P.logger.error(f'Exception:{str(e)}')
|
||||
cls.P.logger.error(traceback.format_exc())
|
||||
return False
|
||||
return count
|
||||
|
||||
|
||||
@classmethod
|
||||
@@ -135,12 +142,12 @@ class ModelBase(F.db.Model):
|
||||
if cls.P.ModelSetting is not None and cls.__tablename__ is not None:
|
||||
cls.P.ModelSetting.set(f'{cls.__tablename__}_last_list_option', f'{order}|{page}|{search}|{option1}|{option2}')
|
||||
except Exception as e:
|
||||
F.logger.error('Exception:%s', e)
|
||||
F.logger.error(f"Exception:{str(e)}")
|
||||
F.logger.error(traceback.format_exc())
|
||||
F.logger.error(f'{cls.__tablename__}_last_list_option ERROR!' )
|
||||
return ret
|
||||
except Exception as e:
|
||||
cls.P.logger.error('Exception:%s', e)
|
||||
cls.P.logger.error(f"Exception:{str(e)}")
|
||||
cls.P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -149,6 +156,10 @@ class ModelBase(F.db.Model):
|
||||
def make_query(cls, req, order='desc', search='', option1='all', option2='all'):
|
||||
with F.app.app_context():
|
||||
query = F.db.session.query(cls)
|
||||
if order == 'desc':
|
||||
query = query.order_by(desc(cls.id))
|
||||
else:
|
||||
query = query.order_by(cls.id)
|
||||
return query
|
||||
|
||||
|
||||
@@ -171,3 +182,16 @@ class ModelBase(F.db.Model):
|
||||
query = query.filter(field.like('%'+search+'%'))
|
||||
#query = query1.union(query2)
|
||||
return query
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_list_by_status(cls, status):
|
||||
try:
|
||||
with F.app.app_context():
|
||||
query = F.db.session.query(cls).filter(
|
||||
cls.status == status,
|
||||
)
|
||||
query = query.order_by(cls.id)
|
||||
return query.all()
|
||||
except:
|
||||
pass
|
||||
@@ -107,6 +107,8 @@ def get_model_setting(package_name, logger, table_name=None):
|
||||
if ModelSetting.get(key) != value:
|
||||
change_list.append(key)
|
||||
entity = F.db.session.query(ModelSetting).filter_by(key=key).with_for_update().first()
|
||||
if entity == None:
|
||||
logger.warning(f"NOT exist setting key: {key}")
|
||||
entity.value = value
|
||||
F.db.session.commit()
|
||||
return True, change_list
|
||||
@@ -117,7 +119,8 @@ def get_model_setting(package_name, logger, table_name=None):
|
||||
return False, []
|
||||
|
||||
@staticmethod
|
||||
def get_list(key, delimeter='\n', comment=' #'):
|
||||
def get_list(key, delimeter='\n', comment='#'):
|
||||
value = None
|
||||
try:
|
||||
value = ModelSetting.get(key).replace('\n', delimeter)
|
||||
if comment is None:
|
||||
|
||||
@@ -27,7 +27,12 @@ def default_route(P):
|
||||
def first_menu(sub):
|
||||
try:
|
||||
if P.ModelSetting is not None and (P.package_name == 'system' and sub != 'home'):
|
||||
P.ModelSetting.set('recent_menu_plugin', '{}'.format(sub))
|
||||
current_menu = sub
|
||||
for except_menu in P.recent_menu_plugin_except_list:
|
||||
if current_menu.startswith(except_menu) or current_menu == except_menu:
|
||||
break
|
||||
else:
|
||||
P.ModelSetting.set('recent_menu_plugin', current_menu)
|
||||
for module in P.module_list:
|
||||
if sub == module.name:
|
||||
first_menu = module.get_first_menu()
|
||||
@@ -45,8 +50,8 @@ def default_route(P):
|
||||
return redirect(f"/{P.package_name}/manual/{P.get_first_manual_path()}")
|
||||
|
||||
return render_template('sample.html', title='%s - %s' % (P.package_name, sub))
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/manual/<path:path>', methods=['GET', 'POST'])
|
||||
@@ -57,16 +62,43 @@ def default_route(P):
|
||||
filepath = os.path.join(plugin_root, *path.split('/'))
|
||||
from support import SupportFile
|
||||
data = SupportFile.read_file(filepath)
|
||||
if filepath.endswith('.mdf'):
|
||||
try:
|
||||
from support import SupportSC
|
||||
data = SupportSC.decode(data)
|
||||
except:
|
||||
pass
|
||||
return render_template('manual.html', data=data)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/<module_name>/manual/<path:path>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def module_manual(module_name, path):
|
||||
try:
|
||||
plugin_root = os.path.dirname(P.blueprint.template_folder)
|
||||
filepath = os.path.join(plugin_root, *path.split('/'))
|
||||
from support import SupportFile
|
||||
data = SupportFile.read_file(filepath)
|
||||
return render_template('manual.html', data=data)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@P.blueprint.route('/<sub>/<sub2>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def second_menu(sub, sub2):
|
||||
if sub2 == 'null':
|
||||
return
|
||||
if P.ModelSetting is not None:
|
||||
P.ModelSetting.set('recent_menu_plugin', '{}|{}'.format(sub, sub2))
|
||||
current_menu = f"{sub}|{sub2}"
|
||||
for except_menu in P.recent_menu_plugin_except_list:
|
||||
if current_menu.startswith(except_menu) or current_menu == except_menu:
|
||||
break
|
||||
else:
|
||||
P.ModelSetting.set('recent_menu_plugin', current_menu)
|
||||
try:
|
||||
for module in P.module_list:
|
||||
if sub == module.name:
|
||||
@@ -74,8 +106,8 @@ def default_route(P):
|
||||
if sub == 'log':
|
||||
return render_template('log.html', package=P.package_name)
|
||||
return render_template('sample.html', title='%s - %s' % (P.package_name, sub))
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
#########################################################
|
||||
@@ -84,7 +116,7 @@ def default_route(P):
|
||||
@P.blueprint.route('/ajax/<sub>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def ajax(sub):
|
||||
P.logger.debug('AJAX %s %s', P.package_name, sub)
|
||||
#P.logger.debug('AJAX %s %s', P.package_name, sub)
|
||||
try:
|
||||
# global
|
||||
if sub == 'setting_save':
|
||||
@@ -93,8 +125,8 @@ def default_route(P):
|
||||
module.setting_save_after(change_list)
|
||||
return jsonify(ret)
|
||||
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/ajax/<module_name>/<cmd>', methods=['GET', 'POST'])
|
||||
@@ -109,8 +141,9 @@ def default_route(P):
|
||||
else:
|
||||
P.logic.scheduler_stop(module_name)
|
||||
return jsonify(go)
|
||||
elif cmd == 'reset_db':
|
||||
ret = P.logic.reset_db(module_name)
|
||||
elif cmd == 'db_delete':
|
||||
day = request.form['day']
|
||||
ret = P.logic.db_delete(module_name, None, day)
|
||||
return jsonify(ret)
|
||||
elif cmd == 'one_execute':
|
||||
ret = P.logic.one_execute(module_name)
|
||||
@@ -118,18 +151,25 @@ def default_route(P):
|
||||
elif cmd == 'immediately_execute':
|
||||
ret = P.logic.immediately_execute(module_name)
|
||||
return jsonify(ret)
|
||||
elif cmd == 'web_list':
|
||||
model = P.logic.get_module(module_name).web_list_model
|
||||
if model != None:
|
||||
return jsonify(model.web_list(request))
|
||||
|
||||
|
||||
if module_name == module.name:
|
||||
if cmd == 'command':
|
||||
return module.process_command(request.form['command'], request.form.get('arg1'), request.form.get('arg2'), request.form.get('arg3'), request)
|
||||
elif cmd == 'web_list':
|
||||
model = P.logic.get_module(module_name).web_list_model
|
||||
if model != None:
|
||||
return jsonify(model.web_list(request))
|
||||
elif cmd == 'db_delete_item':
|
||||
db_id = request.form['db_id']
|
||||
ret = False
|
||||
if module.web_list_model != None:
|
||||
ret = module.web_list_model.delete_by_id(db_id)
|
||||
return jsonify(ret)
|
||||
else:
|
||||
return module.process_ajax(cmd, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/ajax/<module_name>/<page_name>/<command>', methods=['GET', 'POST'])
|
||||
@@ -148,10 +188,10 @@ def default_route(P):
|
||||
else:
|
||||
P.logic.scheduler_stop_sub(module_name, page_name)
|
||||
return jsonify(go)
|
||||
#elif command == 'reset_db':
|
||||
# sub = request.form['sub']
|
||||
# ret = P.logic.reset_db(sub)
|
||||
# return jsonify(ret)
|
||||
elif command == 'db_delete':
|
||||
day = request.form['day']
|
||||
ret = P.logic.db_delete(module_name, page_name, day)
|
||||
return jsonify(ret)
|
||||
elif command == 'one_execute':
|
||||
ret = P.logic.one_execute_sub(module_name, page_name)
|
||||
return jsonify(ret)
|
||||
@@ -160,25 +200,30 @@ def default_route(P):
|
||||
return jsonify(ret)
|
||||
elif command == 'command':
|
||||
return ins_page.process_command(request.form['command'], request.form.get('arg1'), request.form.get('arg2'), request.form.get('arg3'), request)
|
||||
elif command == 'db_delete_item':
|
||||
db_id = request.form['db_id']
|
||||
ret = False
|
||||
if ins_page.web_list_model != None:
|
||||
ret = ins_page.web_list_model.delete_by_id(db_id)
|
||||
return jsonify(ret)
|
||||
else:
|
||||
return ins_page.process_ajax(command, request)
|
||||
P.logger.error(f"not process ajax : {P.package_name} {module_name} {page_name} {command}")
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
#########################################################
|
||||
# API - 외부
|
||||
#########################################################
|
||||
# 단일 모듈인 경우 모듈이름을 붙이기 불편하여 추가.
|
||||
@P.blueprint.route('/api/<sub2>', methods=['GET', 'POST'])
|
||||
@P.blueprint.route('/api/<sub>', methods=['GET', 'POST'])
|
||||
@F.check_api
|
||||
def api_first(sub2):
|
||||
def api_first(sub):
|
||||
try:
|
||||
for module in P.module_list:
|
||||
return module.process_api(sub2, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
return P.module_list[0].process_api(sub, request)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/api/<sub>/<sub2>', methods=['GET', 'POST'])
|
||||
@@ -188,8 +233,16 @@ def default_route(P):
|
||||
for module in P.module_list:
|
||||
if sub == module.name:
|
||||
return module.process_api(sub2, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/normal/<sub>', methods=['GET', 'POST'])
|
||||
def normal_first(sub):
|
||||
try:
|
||||
return P.module_list[0].process_normal(sub, request)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/normal/<sub>/<sub2>', methods=['GET', 'POST'])
|
||||
@@ -198,13 +251,13 @@ def default_route(P):
|
||||
for module in P.module_list:
|
||||
if sub == module.name:
|
||||
return module.process_normal(sub2, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
|
||||
# default_route 끝
|
||||
|
||||
|
||||
|
||||
@@ -266,18 +319,14 @@ def default_route_single_module(P):
|
||||
else:
|
||||
P.logic.scheduler_stop(sub)
|
||||
return jsonify(go)
|
||||
elif sub == 'reset_db':
|
||||
sub = request.form['sub']
|
||||
ret = P.logic.reset_db(sub)
|
||||
return jsonify(ret)
|
||||
elif sub == 'one_execute':
|
||||
sub = request.form['sub']
|
||||
ret = P.logic.one_execute(sub)
|
||||
return jsonify(ret)
|
||||
else:
|
||||
return P.module_list[0].process_ajax(sub, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/api/<sub>', methods=['GET', 'POST'])
|
||||
@@ -285,16 +334,16 @@ def default_route_single_module(P):
|
||||
def api(sub):
|
||||
try:
|
||||
return P.module_list[0].process_api(sub, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
@P.blueprint.route('/normal/<sub>', methods=['GET', 'POST'])
|
||||
def normal(sub):
|
||||
try:
|
||||
return P.module_list[0].process_normal(sub, request)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -330,14 +379,15 @@ def default_route_socketio_module(module, attach=''):
|
||||
module.socketio_list = []
|
||||
|
||||
@F.socketio.on('connect', namespace=f'/{P.package_name}/{module.name}{attach}')
|
||||
@F.login_required
|
||||
def connect():
|
||||
try:
|
||||
P.logger.debug(f'socket_connect : {P.package_name} - {module.name}{attach}')
|
||||
module.socketio_list.append(request.sid)
|
||||
socketio_callback('start', '')
|
||||
module.socketio_connect()
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -347,8 +397,8 @@ def default_route_socketio_module(module, attach=''):
|
||||
P.logger.debug(f'socket_disconnect : {P.package_name} - {module.name}{attach}')
|
||||
module.socketio_list.remove(request.sid)
|
||||
module.socketio_disconnect()
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
P.logger.error(f"Exception:{str(e)}")
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -357,7 +407,7 @@ def default_route_socketio_module(module, attach=''):
|
||||
if encoding:
|
||||
data = json.dumps(data, cls=AlchemyEncoder)
|
||||
data = json.loads(data)
|
||||
F.socketio.emit(cmd, data, namespace=f'/{P.package_name}/{module.name}{attach}', broadcast=True)
|
||||
F.socketio.emit(cmd, data, namespace=f'/{P.package_name}/{module.name}{attach}')
|
||||
|
||||
module.socketio_callback = socketio_callback
|
||||
|
||||
@@ -392,9 +442,10 @@ def default_route_socketio_page(page):
|
||||
page.socketio_list = []
|
||||
|
||||
@F.socketio.on('connect', namespace=f'/{P.package_name}/{module.name}/{page.name}')
|
||||
@F.login_required
|
||||
def page_socketio_connect():
|
||||
try:
|
||||
P.logger.debug(f'socket_connect : {P.package_name}/{module.name}/{page.name}')
|
||||
#P.logger.debug(f'socket_connect : {P.package_name}/{module.name}/{page.name}')
|
||||
page.socketio_list.append(request.sid)
|
||||
page_socketio_socketio_callback('start', '')
|
||||
except Exception as e:
|
||||
@@ -405,7 +456,7 @@ def default_route_socketio_page(page):
|
||||
@F.socketio.on('disconnect', namespace=f'/{P.package_name}/{module.name}/{page.name}')
|
||||
def page_socketio_disconnect():
|
||||
try:
|
||||
P.logger.debug(f'socket_disconnect : {P.package_name}/{module.name}/{page.name}')
|
||||
#P.logger.debug(f'socket_disconnect : {P.package_name}/{module.name}/{page.name}')
|
||||
page.socketio_list.remove(request.sid)
|
||||
except Exception as e:
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
@@ -417,6 +468,6 @@ def default_route_socketio_page(page):
|
||||
if encoding:
|
||||
data = json.dumps(data, cls=AlchemyEncoder)
|
||||
data = json.loads(data)
|
||||
F.socketio.emit(cmd, data, namespace=f'/{P.package_name}/{module.name}/{page.name}', broadcast=True)
|
||||
F.socketio.emit(cmd, data, namespace=f'/{P.package_name}/{module.name}/{page.name}')
|
||||
|
||||
page.socketio_callback = page_socketio_socketio_callback
|
||||
|
||||
BIN
lib/support/.DS_Store
vendored
Normal file
BIN
lib/support/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -10,15 +10,19 @@ def d(data):
|
||||
|
||||
from .logger import get_logger
|
||||
|
||||
logger = get_logger()
|
||||
#logger = get_logger()
|
||||
import logging
|
||||
logger = logging.getLogger('support')
|
||||
|
||||
from .base.aes import SupportAES
|
||||
from .base.discord import SupportDiscord
|
||||
from .base.file import SupportFile
|
||||
from .base.os_command import SupportOSCommand
|
||||
from .base.string import SupportString
|
||||
from .base.sub_process import SupportSubprocess
|
||||
from .base.support_sc import SupportSC
|
||||
from .base.telegram import SupportTelegram
|
||||
from .base.slack import SupportSlack
|
||||
from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil,
|
||||
default_headers, pt)
|
||||
from .base.yaml import SupportYaml
|
||||
|
||||
@@ -18,8 +18,8 @@ class SupportAES(object):
|
||||
def encrypt(cls, raw, mykey=None):
|
||||
try:
|
||||
Random.atfork()
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
raw = pad(raw)
|
||||
@@ -31,8 +31,8 @@ class SupportAES(object):
|
||||
cipher = AES.new(key if mykey is None else mykey, AES.MODE_CBC, iv )
|
||||
try:
|
||||
tmp = cipher.encrypt( raw )
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
tmp = cipher.encrypt( raw.encode() )
|
||||
ret = base64.b64encode( iv + tmp )
|
||||
@@ -64,8 +64,8 @@ class SupportAES(object):
|
||||
def encrypt_(cls, raw, mykey=None, iv=None):
|
||||
try:
|
||||
Random.atfork()
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
raw = pad(raw)
|
||||
if type(raw) == type(''):
|
||||
@@ -79,8 +79,8 @@ class SupportAES(object):
|
||||
cipher = AES.new(key if mykey is None else mykey, AES.MODE_CBC, iv )
|
||||
try:
|
||||
tmp = cipher.encrypt( raw )
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
tmp = cipher.encrypt( raw.encode() )
|
||||
ret = base64.b64encode( tmp )
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import io
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
@@ -11,31 +10,36 @@ try:
|
||||
except:
|
||||
os.system('pip3 install discord-webhook')
|
||||
|
||||
# 2023-10-13 by flaskfarm
|
||||
# 웹훅 URL이 git에 노출되면 중단.
|
||||
# base64로 인코딩.
|
||||
import base64
|
||||
|
||||
from discord_webhook import DiscordEmbed, DiscordWebhook
|
||||
|
||||
from . import logger
|
||||
|
||||
webhook_list = [
|
||||
#'https://discord.com/api/webhooks/933908493612744705/DGPWBQN8LiMnt2cnCSNVy6rCc5Gi_vj98QpJ3ZEeihohzsfOsCWvcixJU1A2fQuepGFq', # 1
|
||||
#'https://discord.com/api/webhooks/932754078839234731/R2iFzQ7P8IKV-MGWp820ToWX07s5q8X-st-QsUJs7j3JInUj6ZlI4uDYKeR_cwIi98mf', # 2
|
||||
#'https://discord.com/api/webhooks/932754171835351131/50RLrYa_B69ybk4BWoLruNqU7YlZ3pl3gpPr9bwuankWyTIGtRGbgf0CJ9ExJWJmvXwo', # 3
|
||||
'https://discord.com/api/webhooks/794661043863027752/A9O-vZSHIgfQ3KX7wO5_e2xisqpLw5TJxg2Qs1stBHxyd5PK-Zx0IJbAQXmyDN1ixZ-n', # 4
|
||||
'https://discord.com/api/webhooks/810373348776476683/h_uJLBBlHzD0w_CG0nUajFO-XEh3fvy-vQofQt1_8TMD7zHiR7a28t3jF-xBCP6EVlow', # 5
|
||||
'https://discord.com/api/webhooks/810373405508501534/wovhf-1pqcxW5h9xy7iwkYaf8KMDjHU49cMWuLKtBWjAnj-tzS1_j8RJ7tsMyViDbZCE', # 6
|
||||
'https://discord.com/api/webhooks/796558388326039552/k2VV356S1gKQa9ht-JuAs5Dqw5eVkxgZsLUzFoxmFG5lW6jqKl7zCBbbKVhs3pcLOetm', # 7
|
||||
'https://discord.com/api/webhooks/810373566452858920/Qf2V8BoLOy2kQzlZGHy5HZ1nTj7lK72ol_UFrR3_eHKEOK5fyR_fQ8Yw8YzVh9EQG54o', # 8
|
||||
'https://discord.com/api/webhooks/810373654411739157/SGgdO49OCkTNIlc_BSMSy7IXQwwXVonG3DsVfvBVE6luTCwvgCqEBpEk30WBeMMieCyI', # 9
|
||||
'https://discord.com/api/webhooks/810373722341900288/FwcRJ4YxYjpyHpnRwF5f2an0ltEm8JPqcWeZqQi3Qz4QnhEY-kR2sjF9fo_n6stMGnf_', # 10
|
||||
'https://discord.com/api/webhooks/931779811691626536/vvwCm1YQvE5tW4QJ4SNKRmXhQQrmOQxbjsgRjbTMMXOSiclB66qipiZaax5giAqqu2IB', # 11
|
||||
'https://discord.com/api/webhooks/931779905631420416/VKlDwfxWQPJfIaj94-ww_hM1MNEayRKoMq0adMffCC4WQS60yoAub_nqPbpnfFRR3VU5', # 12
|
||||
'https://discord.com/api/webhooks/931779947914231840/22amQuHSOI7wPijSt3U01mXwd5hTo_WHfVkeaowDQMawCo5tXVfeEMd6wAWf1n7CseiG', # 13
|
||||
'https://discord.com/api/webhooks/810374294416654346/T3-TEdKIg7rwMZeDzNr46KPDvO7ZF8pRdJ3lfl39lJw2XEZamAG8uACIXagbNMX_B0YN', # 14
|
||||
'https://discord.com/api/webhooks/810374337403289641/_esFkQXwlPlhxJWtlqDAdLg2Nujo-LjGPEG3mUmjiRZto69NQpkBJ0F2xtSNrCH4VAgb', # 15
|
||||
'https://discord.com/api/webhooks/810374384736534568/mH5-OkBVpi7XqJioaQ8Ma-NiL-bOx7B5nYJpL1gZ03JaJaUaIW4bCHeCt5O_VGLJwAtj', # 16
|
||||
'https://discord.com/api/webhooks/810374428604104724/Z1Tdxz3mb0ytWq5LHWi4rG5CeJnr9KWXy5aO_waeD0NcImQnhRXe7h7ra7UrIDRQ2jOg', # 17
|
||||
'https://discord.com/api/webhooks/810374475773509643/QCPPN4djNzhuOmbS3DlrGBunK0SVR5Py9vMyCiPL-0T2VPgitFZS4YM6GCLfM2fkrn4-', # 18
|
||||
'https://discord.com/api/webhooks/810374527652855819/5ypaKI_r-hYzwmdDlVmgAU6xNgU833L9tFlPnf3nw4ZDaPMSppjt77aYOiFks4KLGQk8', # 19
|
||||
'https://discord.com/api/webhooks/810374587917402162/lHrG7CEysGUM_41DMnrxL2Q8eh1-xPjJXstYE68WWfLQbuUAV3rOfsNB9adncJzinYKi', # 20
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MTkwMTk2NzE1NTMwNS9uY01aaWZVVDY3ZTRISXdPeG8xM0dLdFBTNFBnVjZZSDBZaU1SQ2FMQkNfMU0yMHo3WmNFRjExM2xnY0NpRTFFdnhEZQ==').decode('utf-8'), # 1
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MjY1NjQwOTA3MTY0Ni9zUjlHZFJMbERrQV9Cc243UkdvQXQ3TmVSMU9SVFRxczVqUEc5UU9PYTJCbjllTTI1YnctV0FXZ2pYT1pYa183U0V4Wg==').decode('utf-8'), # 2
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MjkyNDExOTA0NDE4OC9wX3ZMN211eElKUmFWOXRDVG56S3c4LVJjY0R5V1JaSjdER2dYc1YwaXlLVGFjZEM4MVBiYmctWHFzY0NDTk5jdXpWeQ==').decode('utf-8'), # 3
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MzE2MjE1ODI0MzkwMS9KdDMwZjlTTTR6dWNfVmYwSVlmbzdZNTluOFI5T2RQazNXdTFtNG93MHZxZFJERXlneVZvb25Rdm1QbVRya1lOVTRyag==').decode('utf-8'), # 4
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MzMyNzY1MzE1ODk4Mi82Nk0zZVFyRGpSZG1UTzExaXZlMGhlTFFpNGwtckZUN1lRYTJ3elpmMjNBOGZPYm1CYjJSRmhxR2dNSHNlNUdHSFNLTA==').decode('utf-8'), # 5
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MzU1ODAzNzg5MzIxNC84aWFNLTJIdXJFOW1XM0RqY293dm9tUVhUeUxLOElrbWR5SnhsY1BFRzJ4MjBqOTNIN0FWNnY0dVJIak5XeGprcjg4Tw==').decode('utf-8'), # 6
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1MzczMTQzNDYxMDc0OS9xRktGX0hSWDRYVHFYMVFPRzM5YWNJVkp6dmdRZXBzZjM2TEFEUlpNOWtiZ0pNUHVfd091OXZ4bXdZVVBRMUpkUjhhRg==').decode('utf-8'), # 7
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1Mzg1NTI0NjI3NDYwMS9vWGVxYVdhWENNZktkM19iZktEVjB0Ti1XQzUyLUxpVjU0VjQxWE1jNWd3XzJmQnpnekp4MzJNYS1wOWlvQkFpd1I3Mw==').decode('utf-8'), # 8
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1Mzk5MDgyNzE1MTQ2MS85a0xXTXZCY1FaNzZRcnRpZmVJck9DOXo5SXl1WGl4YnRmbldocHVjSlFRVUJqcGxSd0tIdzdDc0h3THJhQkRQM1h5ag==').decode('utf-8'), # 9
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDExMjQ1NzYzNzg5OC9ZVC1qblZTeWFxcjAxMjFtWUtVZFU1SjJaVFZHS0NOM2djUDI2RXEwWm5hR3RWeFllM3NZa0kyUG81RWhPd211WDd6aw==').decode('utf-8'), # 10
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDI1Mzg1MTk1NTMxMS9RVUt1cU5uWFFiaWkwU01FMWxkU0lEakxhZXh5RDRUZEZuLWdXejFuSXRlYy1mSFVCU3dxUDd3WHNBbDB1dXd2VVJTRw==').decode('utf-8'), # 11
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDM3NDMyNDgxMzkyNS9VR1Jsc3liY2dPQ3hoMVQ1Z0J0QVc2RGQyZ0dPaGVOXzcydy15QTBvZzU5aU1BcnB3WWxVRzhka0ZXTUxSVUZpaHFScw==').decode('utf-8'), # 12
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDUxNjE5NzI3Nzc2Ny9iOEFIN1FtY2JPSl9XcUVHZmtMOVNPbXBJMWluVThvcDF4amQwWGFjRXFFZW82ZURzbS0yYkpZYllmQ1RYclMxbHhUdQ==').decode('utf-8'), # 13
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDY0MDIzMTIzOTcwMS90bkFSTzFvYWo1SWRmb0U4UEVJejRZUVMxNFhKXzdpc0I5Q1otdzVyaXdDN0U0cVVzQ1B6V2pLRnM3WE9OazBvVEo5Qg==').decode('utf-8'), # 14
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDc1NTcxNzIwMTk4MS9WLWQwc0hvNl9QakJTdFpLVmtuSTdDS0RuQks1QzRhS2dPZUZ4azEwam41VE5oZk1PdFNOSFNHN3BpaGNWLVh6Y0kxZg==').decode('utf-8'), # 15
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NDg4NDc4NDMyNDYxOS9XVEpHWWVjcjVKOHhtN0hTaUpCbmdnU01Uc3JkMUxiaDVwQzB2Vm5tYVptZWlvd2RRZWZQRHRuZHowRmViWE9xYkNoeA==').decode('utf-8'), # 16
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NTAxMTIxMzA5OTEyOS9neHVVenpsMTBpMUV4NWZtdU5jZGlOQ2FocHBEM3liQlpxaTR3Y3phdlpGeG1OUGx2VFRadU9CalZCMTBOZzJ2QWpLcA==').decode('utf-8'), # 17
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NTEzMjg4OTczMTE1My9YcTU4cXdCTGlOOEF4S1djQTl0MFJERkhIT0NDNjg4MlQ1aXBKbkJxY3VSOFVxMGowSzF4Rko3dUZWaGhRR0RFTjc3bw==').decode('utf-8'), # 18
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NTI5NzYzNzc5MzgxMy9pV3hoZkxRN190dHhkNENIVnNPWjA2ZHFOUjlkVTZUdlNfdHA2OHVnNlI2WmRIa2dESzJKb28xUVNSa3NrRDhLUXRyTg==').decode('utf-8'), # 19
|
||||
base64.b64decode(b'aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTM5NDk1NTQ0NDk0MjAxMjQ4OC9zandtaFNDYjI0ZElYbjBVMWhwMmdJRzZDV2REcC1Kb3M0OW1Oc05jQllGenNDNm1KYVZJOVpoQm11dGt4cXd1bDc1ZA==').decode('utf-8'), # 20
|
||||
]
|
||||
|
||||
|
||||
@@ -44,20 +48,60 @@ class SupportDiscord(object):
|
||||
@classmethod
|
||||
def send_discord_message(cls, text, image_url=None, webhook_url=None):
|
||||
try:
|
||||
"""
|
||||
webhook = DiscordWebhook(url=webhook_url, content=text)
|
||||
if image_url is not None:
|
||||
embed = DiscordEmbed()
|
||||
embed.set_timestamp()
|
||||
embed.set_image(url=image_url)
|
||||
webhook.add_embed(embed)
|
||||
response = webhook.execute()
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
"""
|
||||
try:
|
||||
|
||||
if image_url is not None:
|
||||
webhook = DiscordWebhook(url=webhook_url)
|
||||
embed = DiscordEmbed()
|
||||
embed.set_timestamp()
|
||||
embed.set_image(url=image_url)
|
||||
tmp = text.split('\n', 1)
|
||||
embed.set_title(tmp[0])
|
||||
embed.set_description(tmp[1])
|
||||
webhook.add_embed(embed)
|
||||
else:
|
||||
if 'http://' in text or 'https://' in text:
|
||||
webhook = DiscordWebhook(url=webhook_url, content= text)
|
||||
else:
|
||||
webhook = DiscordWebhook(url=webhook_url, content='```' + text + '```')
|
||||
webhook.execute()
|
||||
return True
|
||||
except:
|
||||
webhook = DiscordWebhook(url=webhook_url, content=text)
|
||||
if image_url is not None:
|
||||
embed = DiscordEmbed()
|
||||
embed.set_timestamp()
|
||||
embed.set_image(url=image_url)
|
||||
webhook.add_embed(embed)
|
||||
webhook.execute()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
@classmethod
|
||||
def send_discord_bot_message(cls, text, webhook_url, encryped=True):
|
||||
try:
|
||||
from support import SupportAES
|
||||
if encryped:
|
||||
text = '^' + SupportAES.encrypt(text)
|
||||
return cls.send_discord_message(text, webhook_url=webhook_url)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def discord_proxy_image(cls, image_url, webhook_url=None, retry=True):
|
||||
@@ -95,21 +139,21 @@ class SupportDiscord(object):
|
||||
return image_url
|
||||
else:
|
||||
raise Exception(str(data))
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
if retry:
|
||||
time.sleep(1)
|
||||
return cls.discord_proxy_image(image_url, webhook_url=None, retry=False)
|
||||
return cls.discord_proxy_image(image_url, webhook_url=webhook_url, retry=False)
|
||||
else:
|
||||
return image_url
|
||||
|
||||
|
||||
@classmethod
|
||||
def discord_proxy_image_localfile(cls, filepath, retry=True):
|
||||
def discord_proxy_image_localfile(cls, filepath, webhook_url=None, retry=True):
|
||||
data = None
|
||||
webhook_url = webhook_list[random.randint(0,len(webhook_list)-1)]
|
||||
|
||||
if webhook_url is None or webhook_url == '':
|
||||
webhook_url = webhook_list[random.randint(0,len(webhook_list)-1)]
|
||||
try:
|
||||
webhook = DiscordWebhook(url=webhook_url, content='')
|
||||
import io
|
||||
@@ -133,8 +177,8 @@ class SupportDiscord(object):
|
||||
if retry:
|
||||
time.sleep(1)
|
||||
return cls.discord_proxy_image_localfile(filepath, retry=False)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
if retry:
|
||||
@@ -143,15 +187,15 @@ class SupportDiscord(object):
|
||||
|
||||
|
||||
@classmethod
|
||||
def discord_proxy_image_bytes(cls, bytes, retry=True):
|
||||
def discord_proxy_image_bytes(cls, bytes, retry=True, format='jpg', webhook_url=None):
|
||||
data = None
|
||||
idx = random.randint(0,len(webhook_list)-1)
|
||||
webhook_url = webhook_list[idx]
|
||||
if webhook_url is None or webhook_url == '':
|
||||
webhook_url = webhook_list[random.randint(0,len(webhook_list)-1)]
|
||||
try:
|
||||
webhook = DiscordWebhook(url=webhook_url, content='')
|
||||
webhook.add_file(file=bytes, filename='image.jpg')
|
||||
webhook.add_file(file=bytes, filename=f'image.{format}')
|
||||
embed = DiscordEmbed()
|
||||
embed.set_image(url="attachment://image.jpg")
|
||||
embed.set_image(url=f"attachment://image.{format}")
|
||||
response = webhook.execute()
|
||||
data = None
|
||||
if type(response) == type([]):
|
||||
@@ -168,8 +212,8 @@ class SupportDiscord(object):
|
||||
if retry:
|
||||
time.sleep(1)
|
||||
return cls.discord_proxy_image_bytes(bytes, retry=False)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
if retry:
|
||||
@@ -181,7 +225,7 @@ class SupportDiscord(object):
|
||||
|
||||
# RSS에서 자막 올린거
|
||||
@classmethod
|
||||
def discord_cdn(cls, byteio=None, filepath=None, filename=None, webhook_url=None, content='', retry=True):
|
||||
def discord_cdn(cls, byteio=None, filepath=None, filename=None, webhook_url="https://discord.com/api/webhooks/1050549730964410470/ttge1ggOfIxrCSeTmYbIIsUWyMGAQj-nN6QBgwZTqLcHtUKcqjZ8wFWSWAhHmZne57t7", content='', retry=True):
|
||||
data = None
|
||||
if webhook_url is None:
|
||||
webhook_url = webhook_list[random.randint(0,9)]
|
||||
@@ -210,8 +254,8 @@ class SupportDiscord(object):
|
||||
if retry:
|
||||
time.sleep(1)
|
||||
return cls.discord_proxy_image_localfile(filepath, retry=False)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
if retry:
|
||||
time.sleep(1)
|
||||
|
||||
@@ -3,6 +3,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
import zipfile
|
||||
|
||||
from . import logger
|
||||
|
||||
@@ -16,8 +17,8 @@ class SupportFile(object):
|
||||
data = ifp.read()
|
||||
ifp.close()
|
||||
return data
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
@@ -27,8 +28,8 @@ class SupportFile(object):
|
||||
ofp = codecs.open(filename, mode, encoding='utf8')
|
||||
ofp.write(data)
|
||||
ofp.close()
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
@@ -37,8 +38,8 @@ class SupportFile(object):
|
||||
with open(filepath, "r", encoding='utf8') as json_file:
|
||||
data = json.load(json_file)
|
||||
return data
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
@@ -48,8 +49,8 @@ class SupportFile(object):
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
with open(filepath, "w", encoding='utf8') as json_file:
|
||||
json.dump(data, json_file, indent=4, ensure_ascii=False)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -71,8 +72,8 @@ class SupportFile(object):
|
||||
response = requests.get(url, headers=headers) # get request
|
||||
file_is.write(response.content) # write to file
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.debug('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.debug(f"Exception:{str(e)}")
|
||||
logger.debug(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@@ -82,118 +83,18 @@ class SupportFile(object):
|
||||
#text = text.replace('/', '')
|
||||
# 2021-07-31 X:X
|
||||
#text = text.replace(':', ' ')
|
||||
text = re.sub('[\\/:*?\"<>|]', ' ', text).strip()
|
||||
text = re.sub('[\\/:*?\"<>|:]', ' ', text).strip()
|
||||
text = re.sub("\s{2,}", ' ', text)
|
||||
return text
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def size(cls, start_path = '.'):
|
||||
total_size = 0
|
||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
if not os.path.islink(fp):
|
||||
total_size += os.path.getsize(fp)
|
||||
return total_size
|
||||
def unzip(cls, zip_filepath, extract_folderpath):
|
||||
with zipfile.ZipFile(zip_filepath, 'r') as zip_ref:
|
||||
zip_ref.extractall(extract_folderpath)
|
||||
|
||||
|
||||
# 파일처리에서 사용. 중복이면 시간값
|
||||
@classmethod
|
||||
def file_move(cls, source_path, target_dir, target_filename):
|
||||
try:
|
||||
@@ -208,9 +109,160 @@ class SupportFile(object):
|
||||
new_target_filename = f"{tmp[0]} {str(time.time()).split('.')[0]}{tmp[1]}"
|
||||
target_path = os.path.join(target_dir, new_target_filename)
|
||||
shutil.move(source_path, target_path)
|
||||
except Exception as exception:
|
||||
logger.debug('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.debug(f"Exception:{str(e)}")
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def size(cls, start_path = '.'):
|
||||
if os.path.exists(start_path):
|
||||
if os.path.isdir(start_path):
|
||||
total_size = 0
|
||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
if not os.path.islink(fp):
|
||||
total_size += os.path.getsize(fp)
|
||||
return total_size
|
||||
else:
|
||||
return os.path.getsize(start_path)
|
||||
return 0
|
||||
|
||||
|
||||
@classmethod
|
||||
def size_info(cls, start_path = '.'):
|
||||
ret = {
|
||||
'size':0,
|
||||
'file_count':0,
|
||||
'folder_count':0.
|
||||
}
|
||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
if not os.path.islink(fp):
|
||||
ret['size'] += os.path.getsize(fp)
|
||||
ret['folder_count'] += len(dirnames)
|
||||
ret['file_count'] += len(filenames)
|
||||
return ret
|
||||
|
||||
|
||||
@classmethod
|
||||
def rmtree(cls, folderpath):
|
||||
import shutil
|
||||
try:
|
||||
for root, dirs, files in os.walk(folderpath):
|
||||
for name in files:
|
||||
os.remove(os.path.join(root, name))
|
||||
for name in dirs:
|
||||
shutil.rmtree(os.path.join(root, name))
|
||||
shutil.rmtree(folderpath)
|
||||
except Exception as e:
|
||||
logger.debug(f"Exception:{str(e)}")
|
||||
logger.debug(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
@classmethod
|
||||
def file_move(cls, source_path, target_dir, target_filename):
|
||||
try:
|
||||
import shutil
|
||||
import time
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
target_path = os.path.join(target_dir, target_filename)
|
||||
if source_path != target_path:
|
||||
if os.path.exists(target_path):
|
||||
tmp = os.path.splitext(target_filename)
|
||||
new_target_filename = f"{tmp[0]} {str(time.time()).split('.')[0]}{tmp[1]}"
|
||||
target_path = os.path.join(target_dir, new_target_filename)
|
||||
shutil.move(source_path, target_path)
|
||||
except Exception as e:
|
||||
logger.debug(f"Exception:{str(e)}")
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
@@ -234,13 +286,13 @@ class SupportFile(object):
|
||||
import shutil
|
||||
shutil.rmtree(zip_path)
|
||||
return zipfilepath
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
def rmtree(cls, folderpath):
|
||||
import shutil
|
||||
@@ -253,19 +305,9 @@ class SupportFile(object):
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
"""
|
||||
|
||||
|
||||
@classmethod
|
||||
def rmtree2(cls, folderpath):
|
||||
import shutil
|
||||
try:
|
||||
for root, dirs, files in os.walk(folderpath):
|
||||
for name in files:
|
||||
os.remove(os.path.join(root, name))
|
||||
for name in dirs:
|
||||
shutil.rmtree(os.path.join(root, name))
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -279,8 +321,8 @@ class SupportFile(object):
|
||||
try:
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(data)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@@ -361,8 +403,8 @@ class SupportFile(object):
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode('utf-8')
|
||||
return data
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import os, sys, traceback, requests
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from io import BytesIO
|
||||
|
||||
import requests
|
||||
|
||||
from . import logger
|
||||
|
||||
|
||||
class SupportImage(object):
|
||||
|
||||
@classmethod
|
||||
@@ -20,6 +26,6 @@ class SupportImage(object):
|
||||
from . import SupportDiscord
|
||||
return SupportDiscord.discord_proxy_image_bytes(img_byte_arr)
|
||||
except Exception as e:
|
||||
logger.error('Exception:%s', e)
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
44
lib/support/base/os_command.py
Normal file
44
lib/support/base/os_command.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from . import logger
|
||||
|
||||
|
||||
class SupportOSCommand(object):
|
||||
|
||||
@classmethod
|
||||
def get_size(cls, path):
|
||||
from support import SupportFile, SupportSubprocess, SupportUtil
|
||||
if platform.system() == 'Windows':
|
||||
#https://docs.microsoft.com/en-us/sysinternals/downloads/du
|
||||
"""
|
||||
bin = r'C:\SJVA3\data\bin\du64.exe'
|
||||
command = [bin, '-c', '-nobanner', f'"{path}"']
|
||||
data = ToolSubprocess.execute_command_return(command, force_log=True)
|
||||
logger.warning(data)
|
||||
ret = {}
|
||||
tmp = data.split('\t')
|
||||
ret['target'] = tmp[1].strip()
|
||||
ret['size'] = int(tmp[0].strip())
|
||||
ret['sizeh'] = ToolUtil.sizeof_fmt(ret['size'])
|
||||
"""
|
||||
ret = {}
|
||||
ret['target'] = path
|
||||
if os.path.exists(path):
|
||||
if os.path.isdir(path):
|
||||
ret['size'] = SupportFile.size(start_path=path)
|
||||
else:
|
||||
ret['size'] = os.stat(path).st_size
|
||||
ret['sizeh'] = SupportUtil.sizeof_fmt(ret['size'])
|
||||
return ret
|
||||
|
||||
else:
|
||||
command = ['du', '-bs', path]
|
||||
data = SupportSubprocess.execute_command_return(command)
|
||||
ret = {}
|
||||
tmp = data['log'].split('\t')
|
||||
ret['target'] = tmp[1].strip()
|
||||
ret['size'] = int(tmp[0].strip())
|
||||
ret['sizeh'] = SupportUtil.sizeof_fmt(ret['size'])
|
||||
return ret
|
||||
|
||||
25
lib/support/base/slack.py
Normal file
25
lib/support/base/slack.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
import traceback
|
||||
|
||||
try:
|
||||
from slack_sdk.webhook import WebhookClient
|
||||
except:
|
||||
os.system('pip3 install slack-sdk')
|
||||
from slack_sdk.webhook import WebhookClient
|
||||
from . import logger
|
||||
|
||||
class SupportSlack:
|
||||
@classmethod
|
||||
def send_slack_message(cls, text, webhook_url=None, image_url=None, disable_notification=None):
|
||||
try:
|
||||
if webhook_url is None:
|
||||
return False
|
||||
webhook = WebhookClient(webhook_url)
|
||||
if image_url is not None:
|
||||
webhook.send(text=text, blocks=[{"type": "image", "title": {"type": "plain_text", "text": "Image", "emoji": True}, "image_url": image_url, "alt_text": "Image"}])
|
||||
webhook.send(text=text)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
@@ -1,3 +1,6 @@
|
||||
import re
|
||||
import traceback
|
||||
|
||||
from . import logger
|
||||
|
||||
|
||||
@@ -5,22 +8,78 @@ class SupportString(object):
|
||||
@classmethod
|
||||
def get_cate_char_by_first(cls, title): # get_first
|
||||
value = ord(title[0].upper())
|
||||
if value >= ord('0') and value <= ord('9'): return '0Z'
|
||||
elif value >= ord('A') and value <= ord('Z'): return '0Z'
|
||||
elif value >= ord('가') and value < ord('나'): return '가'
|
||||
elif value < ord('다'): return '나'
|
||||
elif value < ord('라'): return '다'
|
||||
elif value < ord('마'): return '라'
|
||||
elif value < ord('바'): return '마'
|
||||
elif value < ord('사'): return '바'
|
||||
elif value < ord('아'): return '사'
|
||||
elif value < ord('자'): return '아'
|
||||
elif value < ord('차'): return '자'
|
||||
elif value < ord('카'): return '차'
|
||||
elif value < ord('타'): return '카'
|
||||
elif value < ord('파'): return '타'
|
||||
elif value < ord('하'): return '파'
|
||||
elif value <= ord('힣'): return '하'
|
||||
else: return '0Z'
|
||||
if ord('가') <= value < ord('나'): return '가'
|
||||
if ord('나') <= value < ord('다'): return '나'
|
||||
if ord('다') <= value < ord('라'): return '다'
|
||||
if ord('라') <= value < ord('마'): return '라'
|
||||
if ord('마') <= value < ord('바'): return '마'
|
||||
if ord('바') <= value < ord('사'): return '바'
|
||||
if ord('사') <= value < ord('아'): return '사'
|
||||
if ord('아') <= value < ord('자'): return '아'
|
||||
if ord('자') <= value < ord('차'): return '자'
|
||||
if ord('차') <= value < ord('카'): return '차'
|
||||
if ord('카') <= value < ord('타'): return '카'
|
||||
if ord('타') <= value < ord('파'): return '타'
|
||||
if ord('파') <= value < ord('하'): return '파'
|
||||
if ord('하') <= value < ord('힣'): return '하'
|
||||
return '0Z'
|
||||
|
||||
|
||||
@classmethod
|
||||
def is_include_hangul(cls, text):
|
||||
try:
|
||||
hanCount = len(re.findall(u'[\u3130-\u318F\uAC00-\uD7A3]+', text))
|
||||
return hanCount > 0
|
||||
except:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def language_info(cls, text):
|
||||
try:
|
||||
text = text.strip().replace(' ', '')
|
||||
all_count = len(text)
|
||||
han_count = len(re.findall('[\u3130-\u318F\uAC00-\uD7A3]', text))
|
||||
eng_count = len(re.findall('[a-zA-Z]', text))
|
||||
etc_count = len(re.findall('[0-9]', text))
|
||||
etc_count += len(re.findall('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…》:]', text))
|
||||
if all_count == etc_count:
|
||||
return (0,0)
|
||||
han_percent = int(han_count * 100 / (all_count-etc_count))
|
||||
eng_percent = int(eng_count * 100 / (all_count-etc_count))
|
||||
return (han_percent, eng_percent)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def remove_special_char(cls, text):
|
||||
return re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…》:]', '', text)
|
||||
|
||||
|
||||
@classmethod
|
||||
def remove_emoji(cls, text, char=''):
|
||||
import re
|
||||
emoji_pattern = re.compile("["
|
||||
u"\U0001F600-\U0001F64F" # emoticons
|
||||
u"\U0001F300-\U0001F5FF" # symbols & pictographs
|
||||
u"\U0001F680-\U0001F6FF" # transport & map symbols
|
||||
u"\U0001F1E0-\U0001F1FF" # flags (iOS)
|
||||
u"\U00002500-\U00002BEF" # chinese char
|
||||
u"\U00002702-\U000027B0"
|
||||
u"\U00002702-\U000027B0"
|
||||
#u"\U000024C2-\U0001F251"
|
||||
u"\U0001f926-\U0001f937"
|
||||
u"\U00010000-\U0010ffff"
|
||||
u"\u2640-\u2642"
|
||||
u"\u2600-\u2B55"
|
||||
u"\u200d"
|
||||
u"\u23cf"
|
||||
u"\u23e9"
|
||||
u"\u231a"
|
||||
u"\ufe0f" # dingbats
|
||||
u"\u3030"
|
||||
"]+", flags=re.UNICODE)
|
||||
# Remove emojis from the text
|
||||
text = emoji_pattern.sub(char, text)
|
||||
return text
|
||||
@@ -1,5 +1,6 @@
|
||||
import io
|
||||
import json
|
||||
import locale
|
||||
import os
|
||||
import platform
|
||||
import queue
|
||||
@@ -20,7 +21,7 @@ def demote(user_uid, user_gid):
|
||||
class SupportSubprocess(object):
|
||||
|
||||
@classmethod
|
||||
def command_for_windows(cls, command: list) -> str or list:
|
||||
def command_for_windows(cls, command: list):
|
||||
if platform.system() == 'Windows':
|
||||
tmp = []
|
||||
if type(command) == type([]):
|
||||
@@ -43,17 +44,32 @@ class SupportSubprocess(object):
|
||||
|
||||
iter_arg = ''
|
||||
if platform.system() == 'Windows':
|
||||
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=shell, env=env, encoding='utf8')
|
||||
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=shell, env=env, encoding='utf8', bufsize=0)
|
||||
else:
|
||||
if uid == None:
|
||||
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=shell, env=env, encoding='utf8')
|
||||
else:
|
||||
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=shell, env=env, preexec_fn=demote(uid, gid), encoding='utf8')
|
||||
|
||||
|
||||
|
||||
new_ret = {'status':'finish', 'log':None}
|
||||
|
||||
def func(ret):
|
||||
with process.stdout:
|
||||
try:
|
||||
for line in iter(process.stdout.readline, iter_arg):
|
||||
ret.append(line.strip())
|
||||
if log:
|
||||
logger.debug(ret[-1])
|
||||
except:
|
||||
pass
|
||||
|
||||
result = []
|
||||
thread = threading.Thread(target=func, args=(result,))
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
#thread.join()
|
||||
|
||||
try:
|
||||
#process.communicate()
|
||||
process_ret = process.wait(timeout=timeout) # wait for the subprocess to exit
|
||||
except:
|
||||
import psutil
|
||||
@@ -62,14 +78,17 @@ class SupportSubprocess(object):
|
||||
proc.kill()
|
||||
process.kill()
|
||||
new_ret['status'] = "timeout"
|
||||
#logger.error(process_ret)
|
||||
thread.join()
|
||||
#ret = []
|
||||
#with process.stdout:
|
||||
# for line in iter(process.stdout.readline, iter_arg):
|
||||
# ret.append(line.strip())
|
||||
# if log:
|
||||
# logger.debug(ret[-1])
|
||||
|
||||
ret = []
|
||||
with process.stdout:
|
||||
for line in iter(process.stdout.readline, iter_arg):
|
||||
ret.append(line.strip())
|
||||
if log:
|
||||
logger.debug(ret[-1])
|
||||
|
||||
ret = result
|
||||
#logger.error(ret)
|
||||
if format is None:
|
||||
ret2 = '\n'.join(ret)
|
||||
elif format == 'json':
|
||||
@@ -82,20 +101,29 @@ class SupportSubprocess(object):
|
||||
break
|
||||
ret2 = json.loads(''.join(ret[index:]))
|
||||
except:
|
||||
ret2 = None
|
||||
ret2 = ret
|
||||
|
||||
new_ret['log'] = ret2
|
||||
return new_ret
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error('command : %s', command)
|
||||
finally:
|
||||
try:
|
||||
if process.stdout:
|
||||
process.stdout.close()
|
||||
if process.stdin:
|
||||
process.stdin.close()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
__instance_list = []
|
||||
|
||||
|
||||
def __init__(self, command, print_log=False, shell=False, env=None, timeout=None, uid=None, gid=None, stdout_callback=None, call_id=None):
|
||||
def __init__(self, command, print_log=False, shell=False, env=None, timeout=None, uid=None, gid=None, stdout_callback=None, call_id=None, callback_line=True):
|
||||
self.command = command
|
||||
self.print_log = print_log
|
||||
self.shell = shell
|
||||
@@ -108,6 +136,7 @@ class SupportSubprocess(object):
|
||||
self.stdout_queue = None
|
||||
self.call_id = call_id
|
||||
self.timestamp = time.time()
|
||||
self.callback_line = callback_line
|
||||
|
||||
|
||||
def start(self, join=True):
|
||||
@@ -127,13 +156,15 @@ class SupportSubprocess(object):
|
||||
self.command = self.command_for_windows(self.command)
|
||||
logger.debug(f"{self.command=}")
|
||||
if platform.system() == 'Windows':
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||
|
||||
else:
|
||||
if self.uid == None:
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||
else:
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, preexec_fn=demote(self.uid, self.gid), encoding='utf8', bufsize=0)
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, preexec_fn=demote(self.uid, self.gid), encoding='utf8', bufsize=0)
|
||||
SupportSubprocess.__instance_list.append(self)
|
||||
self.send_stdout_callback(self.call_id, 'START', None)
|
||||
self.__start_communicate()
|
||||
self.__start_send_callback()
|
||||
if self.process is not None:
|
||||
@@ -142,17 +173,18 @@ class SupportSubprocess(object):
|
||||
self.process_close()
|
||||
else:
|
||||
self.process.wait()
|
||||
self.remove_instance(self)
|
||||
logger.info(f"{self.command} END")
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
logger.warning(self.command)
|
||||
if self.stdout_callback != None:
|
||||
self.stdout_callback('error', str(e))
|
||||
self.stdout_callback('error', str(traceback.format_exc()))
|
||||
self.send_stdout_callback(self.call_id, 'ERROR', str(e))
|
||||
self.send_stdout_callback(self.call_id, 'ERROR', str(traceback.format_exc()))
|
||||
finally:
|
||||
if self.stdout_callback != None:
|
||||
self.stdout_callback('thread_end', None)
|
||||
#self.stdout_callback(self.call_id, 'thread_end', None)
|
||||
pass
|
||||
|
||||
|
||||
def __start_communicate(self):
|
||||
@@ -164,7 +196,11 @@ class SupportSubprocess(object):
|
||||
|
||||
def rdr():
|
||||
while True:
|
||||
buf = self.process.stdout.read(1)
|
||||
try:
|
||||
buf = self.process.stdout.read(1)
|
||||
except:
|
||||
continue
|
||||
#print(buf)
|
||||
if buf:
|
||||
_queue.put( buf )
|
||||
else:
|
||||
@@ -192,7 +228,9 @@ class SupportSubprocess(object):
|
||||
if r is not None:
|
||||
#print(f"{r=}")
|
||||
self.stdout_queue.put(r)
|
||||
self.stdout_queue.put('\n')
|
||||
self.stdout_queue.put('<END>')
|
||||
self.stdout_queue.put('\n')
|
||||
for tgt in [rdr, clct]:
|
||||
th = threading.Thread(target=tgt)
|
||||
th.setDaemon(True)
|
||||
@@ -204,16 +242,36 @@ class SupportSubprocess(object):
|
||||
def func():
|
||||
while self.stdout_queue:
|
||||
line = self.stdout_queue.get()
|
||||
#logger.error(line)
|
||||
if line == '<END>':
|
||||
if self.stdout_callback != None:
|
||||
self.stdout_callback('end', None)
|
||||
self.send_stdout_callback(self.call_id, 'END', None)
|
||||
break
|
||||
else:
|
||||
if self.stdout_callback != None:
|
||||
self.stdout_callback('log', line)
|
||||
self.send_stdout_callback(self.call_id, 'LOG', line)
|
||||
self.remove_instance(self)
|
||||
|
||||
th = threading.Thread(target=func, args=())
|
||||
def func_callback_line():
|
||||
previous = ''
|
||||
while self.stdout_queue:
|
||||
receive = previous + self.stdout_queue.get()
|
||||
lines = receive.split('\n')
|
||||
previous = lines[-1]
|
||||
|
||||
for line in lines[:-1]:
|
||||
line = line.strip()
|
||||
# TODO
|
||||
#logger.error(line)
|
||||
if line == '<END>':
|
||||
self.send_stdout_callback(self.call_id, 'END', None)
|
||||
break
|
||||
else:
|
||||
self.send_stdout_callback(self.call_id, 'LOG', line)
|
||||
self.remove_instance(self)
|
||||
|
||||
if self.callback_line:
|
||||
th = threading.Thread(target=func_callback_line, args=())
|
||||
else:
|
||||
th = threading.Thread(target=func, args=())
|
||||
th.setDaemon(True)
|
||||
th.start()
|
||||
|
||||
@@ -243,6 +301,15 @@ class SupportSubprocess(object):
|
||||
self.process.stdin.write(f'{cmd}\n')
|
||||
self.process.stdin.flush()
|
||||
|
||||
def send_stdout_callback(self, call_id, mode, data):
|
||||
try:
|
||||
if self.stdout_callback != None:
|
||||
self.stdout_callback(self.call_id, mode, data)
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(f"[{call_id}] [{mode}] [{data}]")
|
||||
#logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def all_process_close(cls):
|
||||
@@ -272,3 +339,6 @@ class SupportSubprocess(object):
|
||||
if instance.call_id == call_id:
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
def get_list(cls):
|
||||
return cls.__instance_list
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import requests
|
||||
from telepot_mod import Bot
|
||||
|
||||
from . import logger
|
||||
@@ -12,11 +14,21 @@ class SupportTelegram:
|
||||
try:
|
||||
bot = Bot(bot_token)
|
||||
if image_url is not None:
|
||||
bot.sendPhoto(chat_id, image_url, disable_notification=disable_notification)
|
||||
logger.debug(image_url)
|
||||
for i in range(5):
|
||||
if requests.get(image_url).status_code == 200:
|
||||
break
|
||||
else:
|
||||
time.sleep(3)
|
||||
try:
|
||||
bot.sendPhoto(chat_id, image_url, disable_notification=disable_notification)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
bot.sendMessage(chat_id, text, disable_web_page_preview=True, disable_notification=disable_notification)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
logger.debug('Chatid:%s', chat_id)
|
||||
return False
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import os, traceback, io, re, json, codecs
|
||||
import json
|
||||
import time
|
||||
import traceback
|
||||
from functools import wraps
|
||||
|
||||
from . import logger
|
||||
|
||||
from functools import wraps
|
||||
import time
|
||||
|
||||
def pt(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwds):
|
||||
@@ -26,9 +29,9 @@ class SupportUtil(object):
|
||||
def sizeof_fmt(cls, num, suffix='Bytes'):
|
||||
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
||||
if abs(num) < 1024.0:
|
||||
return "%3.1f%s%s" % (num, unit, suffix)
|
||||
return "%3.1f %s%s" % (num, unit, suffix)
|
||||
num /= 1024.0
|
||||
return "%.1f%s%s" % (num, 'Y', suffix)
|
||||
return "%.1f %s%s" % (num, 'Y', suffix)
|
||||
|
||||
@classmethod
|
||||
def is_arm(cls):
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import os
|
||||
import traceback
|
||||
|
||||
import yaml
|
||||
|
||||
from . import logger
|
||||
|
||||
|
||||
class SupportYaml(object):
|
||||
@classmethod
|
||||
def write_yaml(cls, filepath, data):
|
||||
@@ -11,3 +17,41 @@ class SupportYaml(object):
|
||||
with open(filepath, encoding='utf8') as file:
|
||||
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||
return data
|
||||
|
||||
|
||||
@classmethod
|
||||
def copy_section(cls, source_file, target_file, section_name):
|
||||
from support import SupportFile
|
||||
try:
|
||||
if os.path.exists(source_file) == False:
|
||||
return 'not_exist_source_file'
|
||||
if os.path.exists(target_file) == False:
|
||||
return 'not_exist_target_file'
|
||||
lines = SupportFile.read_file(source_file).split('\n')
|
||||
section = {}
|
||||
current_section_name = None
|
||||
current_section_data = None
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith('# SECTION START : '):
|
||||
current_section_name = line.split(':')[1].strip()
|
||||
current_section_data = []
|
||||
if current_section_data is not None:
|
||||
current_section_data.append(line)
|
||||
if line.startswith('# SECTION END'):
|
||||
section[current_section_name] = current_section_data
|
||||
current_section_name = current_section_data = None
|
||||
|
||||
if section_name not in section:
|
||||
return 'not_include_section'
|
||||
|
||||
data = '\n'.join(section[section_name])
|
||||
source_data = SupportFile.read_file(target_file)
|
||||
source_data = source_data + f"\n{data}\n"
|
||||
SupportFile.write_file(target_file, source_data)
|
||||
return 'success'
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return 'exception'
|
||||
@@ -116,7 +116,11 @@ class SupportFfmpeg(object):
|
||||
header_count = 0
|
||||
if self.proxy is None:
|
||||
if self.headers is None:
|
||||
command = [self.__ffmpeg_path, '-y', '-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
if platform.system() == 'Windows':
|
||||
command = [self.__ffmpeg_path, '-y', '-i', f'"{self.url}"', '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
else:
|
||||
command = [self.__ffmpeg_path, '-y', '-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
|
||||
else:
|
||||
headers_command = []
|
||||
tmp = ""
|
||||
@@ -136,9 +140,15 @@ class SupportFfmpeg(object):
|
||||
if len(tmp) > 0:
|
||||
headers_command.append('-headers')
|
||||
headers_command.append(f'{tmp}')
|
||||
command = [self.__ffmpeg_path, '-y'] + headers_command + ['-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
if platform.system() == 'Windows':
|
||||
command = [self.__ffmpeg_path, '-y'] + headers_command + ['-i', f'"{self.url}"', '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
else:
|
||||
command = [self.__ffmpeg_path, '-y'] + headers_command + ['-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
else:
|
||||
command = [self.__ffmpeg_path, '-y', '-http_proxy', self.proxy, '-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
if platform.system() == 'Windows':
|
||||
command = [self.__ffmpeg_path, '-y', '-http_proxy', self.proxy, '-i', f'"{self.url}"', '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
else:
|
||||
command = [self.__ffmpeg_path, '-y', '-http_proxy', self.proxy, '-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
@@ -159,7 +169,7 @@ class SupportFfmpeg(object):
|
||||
return
|
||||
except:
|
||||
pass
|
||||
#logger.error(' '.join(command))
|
||||
logger.error(' '.join(command))
|
||||
command = SupportSubprocess.command_for_windows(command)
|
||||
|
||||
if platform.system() == 'Windows' and header_count > 1:
|
||||
@@ -216,8 +226,8 @@ SET CRLF=^
|
||||
else:
|
||||
if os.path.exists(self.temp_fullpath):
|
||||
os.remove(self.temp_fullpath)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
arg = {'type':'last', 'status':self.status, 'data' : self.get_data()}
|
||||
@@ -347,6 +357,7 @@ SET CRLF=^
|
||||
return data
|
||||
|
||||
def send_to_listener(self, **arg):
|
||||
print(arg)
|
||||
if self.total_callback_function != None:
|
||||
self.total_callback_function(**arg)
|
||||
if self.callback_function is not None and self.callback_function != self.total_callback_function:
|
||||
|
||||
27
lib/support/expand/ffprobe.py
Normal file
27
lib/support/expand/ffprobe.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import traceback
|
||||
|
||||
from support import SupportSubprocess, logger
|
||||
|
||||
|
||||
class SupportFfprobe:
|
||||
__ffprobe_path = 'ffprobe'
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, __ffprobe_path):
|
||||
cls.__ffprobe_path = __ffprobe_path
|
||||
|
||||
@classmethod
|
||||
def ffprobe(cls, filepath, ffprobe_path=None, option=None):
|
||||
try:
|
||||
if ffprobe_path == None:
|
||||
ffprobe_path = cls.__ffprobe_path
|
||||
|
||||
command = [ffprobe_path, '-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', filepath]
|
||||
if option is not None:
|
||||
command += option
|
||||
logger.warning(' '.join(command))
|
||||
ret = SupportSubprocess.execute_command_return(command, format='json')
|
||||
return ret['log']
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
@@ -204,9 +204,9 @@ class GoogleSheetBase:
|
||||
break
|
||||
except gspread.exceptions.APIError:
|
||||
self.sleep_exception()
|
||||
except Exception as exception:
|
||||
except Exception as e:
|
||||
logger.error(f"{key} - {value}")
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error(self.header_info)
|
||||
self.sleep_exception()
|
||||
|
||||
197
lib/support/expand/rclone.py
Normal file
197
lib/support/expand/rclone.py
Normal file
@@ -0,0 +1,197 @@
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from support import SupportSubprocess, d, logger
|
||||
|
||||
|
||||
class SupportRclone(object):
|
||||
__instance_list = []
|
||||
|
||||
__rclone_path = 'rclone'
|
||||
__rclone_config_path = 'rclone.conf'
|
||||
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, __rclone_path, __rclone_config_path):
|
||||
cls.__rclone_path = __rclone_path
|
||||
cls.__rclone_config_path = __rclone_config_path
|
||||
|
||||
@classmethod
|
||||
def get_rclone_path(cls):
|
||||
return cls.__rclone_path
|
||||
|
||||
@classmethod
|
||||
def __get_cmd(cls, config_path=None):
|
||||
command = [cls.__rclone_path]
|
||||
if config_path == None:
|
||||
command += ['--config', cls.__rclone_config_path]
|
||||
else:
|
||||
command += ['--config', config_path]
|
||||
return command
|
||||
|
||||
|
||||
@classmethod
|
||||
def rclone_cmd(cls):
|
||||
return [cls.__rclone_path, '--config', cls.__rclone_config_path]
|
||||
|
||||
@classmethod
|
||||
def get_version(cls, rclone_path=None):
|
||||
try:
|
||||
if rclone_path == None:
|
||||
rclone_path = cls.__rclone_path
|
||||
cmd = [rclone_path, '--version']
|
||||
result = SupportSubprocess.execute_command_return(cmd)
|
||||
if result != None and result['status'] == 'finish':
|
||||
return result['log']
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def config_list(cls, rclone_path=None, rclone_config_path=None, option=None):
|
||||
try:
|
||||
if rclone_path == None:
|
||||
rclone_path = cls.__rclone_path
|
||||
if rclone_config_path == None:
|
||||
rclone_config_path = cls.__rclone_config_path
|
||||
if os.path.exists(rclone_config_path) == False:
|
||||
return
|
||||
command = [rclone_path, '--config', rclone_config_path, 'config', 'dump']
|
||||
if option is not None:
|
||||
command += option
|
||||
result = SupportSubprocess.execute_command_return(command, format='json')
|
||||
for key, value in result['log'].items():
|
||||
if 'token' in value and value['token'].startswith('{'):
|
||||
value['token'] = json.loads(value['token'])
|
||||
return result['log']
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
def get_config(cls, remote_name, rclone_path=None, rclone_config_path=None, option=None):
|
||||
try:
|
||||
data = cls.config_list(rclone_path=rclone_path, rclone_config_path=rclone_config_path, option=option)
|
||||
return data.get(remote_name, None)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def lsjson(cls, remote_path, config_path=None, option=None):
|
||||
return cls.__execute_one_param('lsjson', remote_path, config_path=config_path, option=option, format='json')
|
||||
|
||||
|
||||
@classmethod
|
||||
def lsf(cls, remote_path, config_path=None, option=None):
|
||||
if option == None:
|
||||
option = ['--max-depth=1']
|
||||
return cls.__execute_one_param('lsf', remote_path, config_path=config_path, option=option, format='json')
|
||||
|
||||
|
||||
@classmethod
|
||||
def size(cls, remote_path, config_path=None, option=None):
|
||||
if option == None:
|
||||
option = ['--json']
|
||||
return cls.__execute_one_param('size', remote_path, config_path=config_path, option=option, format='json')
|
||||
|
||||
|
||||
@classmethod
|
||||
def mkdir(cls, remote_path, config_path=None, option=None):
|
||||
return cls.__execute_one_param('mkdir', remote_path, config_path=config_path, option=option, format='json')
|
||||
|
||||
@classmethod
|
||||
def purge(cls, remote_path, config_path=None, option=None):
|
||||
return cls.__execute_one_param('purge', remote_path, config_path=config_path, option=option, format='json')
|
||||
|
||||
|
||||
@classmethod
|
||||
def __execute_one_param(cls, command, remote_path, config_path=None, option=None, format=None):
|
||||
try:
|
||||
command = cls.__get_cmd(config_path) + [command, remote_path]
|
||||
if option is not None:
|
||||
command += option
|
||||
result = SupportSubprocess.execute_command_return(command, format=format)
|
||||
ret = None
|
||||
if result != None and result['status'] == 'finish':
|
||||
ret = result['log']
|
||||
return ret
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def copy(cls, src, tar, config_path=None, option=None):
|
||||
return cls.__execute_two_param('copy', src, tar, config_path=config_path, option=option)
|
||||
|
||||
@classmethod
|
||||
def copy_server_side(cls, src, tar, config_path=None, option=None):
|
||||
if option == None:
|
||||
option = ['--drive-server-side-across-configs=true', '--delete-empty-src-dirs']
|
||||
return cls.__execute_two_param('copy', src, tar, config_path=config_path, option=option)
|
||||
|
||||
@classmethod
|
||||
def move(cls, src, tar, config_path=None, option=None):
|
||||
return cls.__execute_two_param('move', src, tar, config_path=config_path, option=option)
|
||||
|
||||
@classmethod
|
||||
def move_server_side(cls, src, tar, config_path=None, option=None):
|
||||
if option == None:
|
||||
option = ['--drive-server-side-across-configs=true', '--delete-empty-src-dirs']
|
||||
return cls.__execute_two_param('move', src, tar, config_path=config_path, option=option)
|
||||
|
||||
@classmethod
|
||||
def __execute_two_param(cls, command, src, tar, config_path=None, option=None, format=None):
|
||||
try:
|
||||
command = cls.__get_cmd(config_path) + [command, src, tar]
|
||||
if option is not None:
|
||||
command += option
|
||||
result = SupportSubprocess.execute_command_return(command, format=format)
|
||||
ret = None
|
||||
if result != None and result['status'] == 'finish':
|
||||
ret = result['log']
|
||||
return ret
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def getid(cls, remote_path, config_path=None, option=None):
|
||||
try:
|
||||
command = cls.__get_cmd(config_path) + ['backend', 'getid', remote_path]
|
||||
if option is not None:
|
||||
command += option
|
||||
result = SupportSubprocess.execute_command_return(command)
|
||||
ret = None
|
||||
if result != None and result['status'] == 'finish':
|
||||
ret = result['log']
|
||||
if ret is not None and (len(ret.split(' ')) > 1 or ret == ''):
|
||||
ret = None
|
||||
return ret
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def chpar(cls, src, tar, config_path=None, option=None):
|
||||
try:
|
||||
command = cls.__get_cmd(config_path) + ['backend', 'chpar', src, tar, '-o', 'depth=1', '-o', 'delete-empty-src-dir', '--drive-use-trash=false']
|
||||
if option is not None:
|
||||
command += option
|
||||
result = SupportSubprocess.execute_command_return(command)
|
||||
ret = None
|
||||
if result != None and result['status'] == 'finish':
|
||||
ret = result['log']
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
114
lib/support/expand/simple_selenium.py
Normal file
114
lib/support/expand/simple_selenium.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import os
|
||||
|
||||
from support import d, logger
|
||||
|
||||
try:
|
||||
from selenium import webdriver
|
||||
except:
|
||||
os.system("pip install --upgrade selenium")
|
||||
from selenium import webdriver
|
||||
|
||||
import base64
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import UnexpectedAlertPresentException
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import Select, WebDriverWait
|
||||
from webdriver_manager.chrome import ChromeDriverManager
|
||||
|
||||
|
||||
class SupportSimpleSelenium(object):
|
||||
|
||||
def __init__(self, P, mode="local", headless=False, remote=None):
|
||||
self.P = P
|
||||
self.driver = None
|
||||
self.timeout = 5
|
||||
self.driver_init(mode=mode, headless=headless, remote=remote)
|
||||
|
||||
|
||||
def driver_init(self, mode='local', headless=False, remote=None):
|
||||
if mode == 'local':
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
options = Options()
|
||||
if headless:
|
||||
options.add_argument('headless')
|
||||
self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
|
||||
elif mode == 'remote':
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
options = Options()
|
||||
#options.set_preference("general.useragent.override", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
|
||||
#options.set_preference("general.platform.override", "Win32")
|
||||
self.driver = webdriver.Remote(remote, options=options)
|
||||
|
||||
|
||||
def get_pagesoruce(self, url, wait_xpath="/html/body", retry=True):
|
||||
try:
|
||||
self.driver.get(url)
|
||||
WebDriverWait(self.driver, self.timeout).until(lambda driver: driver.find_element(By.XPATH, wait_xpath))
|
||||
return self.driver.page_source
|
||||
except Exception as e:
|
||||
logger.error('Exception:%s', e)
|
||||
logger.error(traceback.format_exc())
|
||||
self.driver_quit()
|
||||
if retry:
|
||||
return self.get_pagesoruce(url, wait_xpath=wait_xpath, retry=False)
|
||||
|
||||
|
||||
def driver_quit(self):
|
||||
if self.driver != None:
|
||||
def func():
|
||||
self.driver.quit()
|
||||
self.driver = None
|
||||
#self.logger.debug('driver quit..')
|
||||
th = threading.Thread(target=func, args=())
|
||||
th.setDaemon(True)
|
||||
th.start()
|
||||
|
||||
def get_downloaded_files(self):
|
||||
if not self.driver.current_url.startswith("chrome://downloads"):
|
||||
self.driver.get("chrome://downloads/")
|
||||
#driver.implicitly_wait(4)
|
||||
self.driver.implicitly_wait(2)
|
||||
return self.driver.execute_script( \
|
||||
"return document.querySelector('downloads-manager') "
|
||||
" .shadowRoot.querySelector('#downloadsList') "
|
||||
" .items.filter(e => e.state === 'COMPLETE') "
|
||||
" .map(e => e.filePath || e.file_path || e.fileUrl || e.file_url); ")
|
||||
|
||||
|
||||
|
||||
def get_file_content(self, path):
|
||||
elem = self.driver.execute_script( \
|
||||
"var input = window.document.createElement('INPUT'); "
|
||||
"input.setAttribute('type', 'file'); "
|
||||
"input.hidden = true; "
|
||||
"input.onchange = function (e) { e.stopPropagation() }; "
|
||||
"return window.document.documentElement.appendChild(input); " )
|
||||
|
||||
elem._execute('sendKeysToElement', {'value': [ path ], 'text': path})
|
||||
|
||||
result = self.driver.execute_async_script( \
|
||||
"var input = arguments[0], callback = arguments[1]; "
|
||||
"var reader = new FileReader(); "
|
||||
"reader.onload = function (ev) { callback(reader.result) }; "
|
||||
"reader.onerror = function (ex) { callback(ex.message) }; "
|
||||
"reader.readAsDataURL(input.files[0]); "
|
||||
"input.remove(); "
|
||||
, elem)
|
||||
|
||||
if not result.startswith('data:') :
|
||||
raise Exception("Failed to get file content: %s" % result)
|
||||
|
||||
return base64.b64decode(result[result.find('base64,') + 7:])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# docker run -d --name selenium_chromium -it -p 4446:4444 -p 5902:5900 -p 7902:7900 --shm-size 2g seleniarm/standalone-chromium:latest
|
||||
BIN
lib/support/libsc/sc.cpython-311-aarch64-linux-gnu.so
Normal file
BIN
lib/support/libsc/sc.cpython-311-aarch64-linux-gnu.so
Normal file
Binary file not shown.
BIN
lib/support/libsc/sc.cpython-311_32.so
Normal file
BIN
lib/support/libsc/sc.cpython-311_32.so
Normal file
Binary file not shown.
BIN
lib/support/libsc/sc.cpython-311_64.so
Normal file
BIN
lib/support/libsc/sc.cpython-311_64.so
Normal file
Binary file not shown.
@@ -36,7 +36,7 @@ class CustomFormatter(logging.Formatter):
|
||||
# pathname filename
|
||||
#format = "[%(asctime)s|%(name)s|%(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
|
||||
|
||||
__format = '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset} %(pathname)s:%(lineno)s] {color}%(message)s{reset}' if os.environ.get('LOGGER_PATHNAME', "False") == "True" else '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset} %(filename)s:%(lineno)s] {color}%(message)s{reset}'
|
||||
__format = '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset}|%(pathname)s:%(lineno)s] {color}%(message)s{reset}' if os.environ.get('LOGGER_PATHNAME', "False") == "True" else '[{yellow}%(asctime)s{reset}|{color}%(levelname)s{reset}|{green}%(name)s{reset}|%(filename)s:%(lineno)s] {color}%(message)s{reset}'
|
||||
|
||||
FORMATS = {
|
||||
logging.DEBUG: __format.format(color=grey, reset=reset, yellow=yellow, green=green),
|
||||
@@ -53,15 +53,13 @@ class CustomFormatter(logging.Formatter):
|
||||
|
||||
|
||||
def get_logger(name=None, log_path=None):
|
||||
if os.environ.get('FF') == 'true':
|
||||
name = 'framework'
|
||||
if name == None:
|
||||
name = sys.argv[0].rsplit('.', 1)[0]
|
||||
logger = logging.getLogger(name)
|
||||
if not logger.handlers:
|
||||
level = logging.DEBUG
|
||||
logger.setLevel(level)
|
||||
formatter = logging.Formatter(u'[%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s] %(message)s')
|
||||
formatter = logging.Formatter(u'[%(asctime)s|%(levelname)s|%(name)s|%(filename)s:%(lineno)s] %(message)s')
|
||||
def customTime(*args):
|
||||
utc_dt = utc.localize(datetime.utcnow())
|
||||
my_tz = timezone("Asia/Seoul")
|
||||
|
||||
@@ -4,6 +4,7 @@ from support import SupportSC
|
||||
|
||||
try:
|
||||
if os.path.exists(os.path.join(os.path.dirname(__file__), 'tving.py')):
|
||||
#from .cppl import SupportCppl
|
||||
from .kakaotv import SupportKakaotv
|
||||
from .seezn import SupportSeezn
|
||||
from .tving import SupportTving
|
||||
@@ -13,5 +14,6 @@ try:
|
||||
SupportWavve = SupportSC.load_module_f(__file__, 'wavve').SupportWavve
|
||||
SupportSeezn = SupportSC.load_module_f(__file__, 'seezn').SupportSeezn
|
||||
SupportKakaotv = SupportSC.load_module_f(__file__, 'kakaotv').SupportKakaotv
|
||||
#SupportCppl = SupportSC.load_module_f(__file__, 'cppl').SupportCppl
|
||||
except:
|
||||
pass
|
||||
|
||||
1
lib/support/site/cppl.pyf
Normal file
1
lib/support/site/cppl.pyf
Normal file
File diff suppressed because one or more lines are too long
277
lib/system/files/all_plugin.yaml
Normal file
277
lib/system/files/all_plugin.yaml
Normal file
@@ -0,0 +1,277 @@
|
||||
- name: "기본"
|
||||
list:
|
||||
- title: "파일 매니저"
|
||||
package_name: "flaskfilemanager"
|
||||
developer: "stevelittlefish"
|
||||
home: "https://github.com/flaskfarm/flaskfilemanager"
|
||||
description: "RichFilemanager를 Flask에서 동작하도록 한 FlaskFileManager 포크"
|
||||
- title: "터미널"
|
||||
package_name: "terminal"
|
||||
developer: "joyful"
|
||||
home: "https://github.com/flaskfarm/terminal"
|
||||
description: "리눅스 전용 심플 터미널. xterm.js client"
|
||||
- title: "편집기"
|
||||
package_name: "flaskcode"
|
||||
developer: "sujeetkv"
|
||||
home: "https://github.com/flaskfarm/flaskcode"
|
||||
description: "flaskcode를 fork한 문서 편집기"
|
||||
- name: "보조 & 라이브러리"
|
||||
list:
|
||||
- title: "번역"
|
||||
package_name: "trans"
|
||||
developer: "FlaskFarm"
|
||||
home: "https://github.com/flaskfarm/trans"
|
||||
description: "번역 관련 설정 및 API 제공"
|
||||
- title: "support_site"
|
||||
package_name: "support_site"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/support_site"
|
||||
description: "사이트 크롤링 라이브러리"
|
||||
- title: "Gdrive 라이브러리 for FlaskFarm"
|
||||
package_name: "libgdrive"
|
||||
developer: "orial"
|
||||
home: "https://github.com/byorial/libgdrive"
|
||||
description: "구글드라이브 API 라이브러리"
|
||||
- name: "외부 연결"
|
||||
list:
|
||||
- title: "Rclone"
|
||||
package_name: "rclone"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/rclone"
|
||||
description: "Rclone을 좀 더 쉽게 사용하기 위한 플러그인"
|
||||
- title: "PLEX MATE"
|
||||
package_name: "plex_mate"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/plex_mate"
|
||||
description: "PLEX와 동일 기기에서 동작하는 툴"
|
||||
- title: "vnStat"
|
||||
package_name: "vnStat"
|
||||
developer: "by275"
|
||||
home: "https://github.com/by275/vnStat"
|
||||
description: "vnStat 정보를 보여주는 플러그인"
|
||||
- title: "115 TOOL"
|
||||
package_name: "tool_115"
|
||||
developer: "soju6jan"
|
||||
home: "https://github.com/soju6jan/tool_115"
|
||||
description: "115 TOOL"
|
||||
- name: "도구"
|
||||
list:
|
||||
- title: "디스코드 봇"
|
||||
package_name: "discord_bot"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/discord_bot"
|
||||
description: "디스코드 봇"
|
||||
- title: "정적 호스트"
|
||||
package_name: "static_host"
|
||||
developer: "by275"
|
||||
description: "정적 웹을 호스팅 하는 플러그인"
|
||||
home: "https://github.com/by275/static_host"
|
||||
- title: 'Flaskfarmaider'
|
||||
package_name: 'flaskfarmaider'
|
||||
developer: 'halfaider'
|
||||
description: 'Flaskfarm 보조 플러그인'
|
||||
home: 'https://github.com/halfaider/flaskfarmaider'
|
||||
- title: "메타데이터"
|
||||
package_name: "metadata"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/metadata"
|
||||
description: "VIDEO 메타데이터 제공"
|
||||
- title: "lotto"
|
||||
package_name: "lotto"
|
||||
developer: "honeypig5"
|
||||
home: "https://github.com/hudulgi/lotto"
|
||||
description: "로또 구매"
|
||||
- title: "핫딜 알람"
|
||||
package_name: "hotdeal_alarm"
|
||||
developer: "dbswnschl"
|
||||
home: "https://github.com/dbswnschl/hotdeal_alarm"
|
||||
description: "핫딜 알리미"
|
||||
- name: "파일처리"
|
||||
list:
|
||||
- title: "국내TV 파일처리"
|
||||
package_name: "fp_ktv"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/fp_ktv"
|
||||
description: "국내TV 영상 파일 전용 파일처리"
|
||||
- title: "영화 파일처리"
|
||||
package_name: "fp_movie"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/fp_movie"
|
||||
description: "영화 파일처리"
|
||||
- title: "musicProc2"
|
||||
package_name: "musicProc2"
|
||||
developer: "dyllislev"
|
||||
home: "https://github.com/dyllisLev/musicProc2"
|
||||
description: "음악정리"
|
||||
- title: "해외TV 파일처리"
|
||||
package_name: "fp_ftv"
|
||||
developer: "kihyyo"
|
||||
home: "https://github.com/kihyyo/fp_ftv"
|
||||
description: "해외TV 영상 파일 전용 파일처리"
|
||||
- title: "자막 툴"
|
||||
package_name: "subtitle_tool"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/subtitle_tool"
|
||||
description: "자막 관련 툴"
|
||||
- name: "토렌트 & RSS"
|
||||
list:
|
||||
- title: "토렌트 정보"
|
||||
package_name: "torrent_info"
|
||||
developer: "by275"
|
||||
home: "https://github.com/by275/torrent_info"
|
||||
description: "토렌트 마그넷/파일 정보를 보여주는 플러그인"
|
||||
- title: "티프리카"
|
||||
package_name: "tfreeca"
|
||||
developer: "by275"
|
||||
home: "https://github.com/by275/tfreeca"
|
||||
description: "티프리카 게시판 뷰어"
|
||||
- name: "OTT"
|
||||
list:
|
||||
- title: "티빙 검색"
|
||||
package_name: "tving_search"
|
||||
developer: "by275"
|
||||
home: "https://github.com/by275/tving_search"
|
||||
description: "티빙 검색 플러그인"
|
||||
- title: "FFMPEG"
|
||||
package_name: "ffmpeg"
|
||||
developer: "FlaskFarm"
|
||||
home: "https://github.com/flaskfarm/ffmpeg"
|
||||
description: "m3u8 다운로드 & 비디오파일 분석"
|
||||
- title: "웨이브"
|
||||
package_name: "wavve"
|
||||
developer: "halfaider"
|
||||
home: "https://github.com/halfaider/wavve"
|
||||
description: "웨이브 VOD 다운로더"
|
||||
- title: "티빙"
|
||||
package_name: "tving"
|
||||
developer: "kihyyo"
|
||||
home: "https://github.com/kihyyo/tving"
|
||||
description: "티빙 VOD 다운로더"
|
||||
- title: "쿠팡플레이"
|
||||
package_name: "cppl"
|
||||
developer: "soju6jan"
|
||||
home: "https://github.com/soju6jan/cppl"
|
||||
description: "쿠팡플레이 VOD 다운로더"
|
||||
- title: "DRM 다운로드"
|
||||
package_name: "wv_tool"
|
||||
developer: "soju6jan"
|
||||
home: "https://github.com/soju6jan/wv_tool"
|
||||
description: "DRM 영상 다운로드"
|
||||
- title: "make_yaml"
|
||||
package_name: "make_yaml"
|
||||
developer: "kihyyo"
|
||||
home: "https://github.com/kihyyo/make_yaml"
|
||||
description: "OTT 정보로 직접 yaml로 만드는 플러그인"
|
||||
- title: "RE_tool"
|
||||
package_name: "RE_tool"
|
||||
developer: "kihyyo"
|
||||
home: "https://github.com/kihyyo/RE_tool"
|
||||
description: "M3U8_RE 다운로드"
|
||||
- name: "라이브"
|
||||
list:
|
||||
- title: "ALive"
|
||||
package_name: "alive"
|
||||
developer: "by275"
|
||||
home: "https://github.com/by275/alive"
|
||||
description: "라이브 방송 플러그인"
|
||||
- title: "EPG"
|
||||
package_name: "epg"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/epg"
|
||||
description: "EPG 생성"
|
||||
- title: "MY EPG"
|
||||
package_name: "myepg"
|
||||
developer: "include"
|
||||
home: "https://github.com/myepg/myepg"
|
||||
description: "EPG API 플러그인"
|
||||
- title: "HDHomerun"
|
||||
package_name: "hdhomerun"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/flaskfarm/hdhomerun"
|
||||
description: "HDHomerun 도구"
|
||||
- title: "스포TV"
|
||||
package_name: "ff_spotv"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_spotv"
|
||||
description: "스포TV 방송 FF플러그인"
|
||||
- title: "삼성TV플러스"
|
||||
package_name: "ff_sstvplus"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_sstvplus"
|
||||
description: "삼성TV플러스 방송 FF플러그인"
|
||||
- title: "네이버스포츠"
|
||||
package_name: "ff_nsports"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_nsports"
|
||||
description: "네이버스포츠 방송 FF플러그인"
|
||||
- title: "쿠팡플레이"
|
||||
package_name: "ff_cpp"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_cpp"
|
||||
description: "쿠팡플레이 방송 FF플러그인"
|
||||
- title: "아프리카TV"
|
||||
package_name: "ff_afrtv"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_afrtv"
|
||||
description: "아프리카TV 방송 FF플러그인"
|
||||
- title: "REYSTREAM"
|
||||
package_name: "ff_reystream"
|
||||
developer: "ssagajikorea"
|
||||
description: "REYSTREAM 방송 FF플러그인"
|
||||
home: "https://github.com/ssagajikorea/ff_reystream"
|
||||
- title: "팝콘TV"
|
||||
package_name: "ff_pktv"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_pktv"
|
||||
description: "팝콘TV 방송 FF플러그인"
|
||||
- title: "팬더TV"
|
||||
package_name: "ff_pdtv"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_pdtv"
|
||||
description: "팬더TV 방송 FF플러그인"
|
||||
- title: "NEXTCAST"
|
||||
package_name: "ff_nextcast"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_nextcast"
|
||||
description: "NEXTCAST 방송 FF플러그인"
|
||||
- title: "LIFETV365"
|
||||
package_name: "ff_lifetv365"
|
||||
developer: "ssagajikorea"
|
||||
home: "https://github.com/ssagajikorea/ff_lifetv365"
|
||||
description: "LIFETV365 방송 FF플러그인"
|
||||
- title: "KLive+"
|
||||
package_name: "klive_plus"
|
||||
developer: "soju6jan"
|
||||
home: "https://github.com/soju6jan/klive_plus"
|
||||
description: "KLive+"
|
||||
- name: "SJVA"
|
||||
list:
|
||||
- title: "SJVA"
|
||||
package_name: "sjva"
|
||||
developer: "soju6jan"
|
||||
home: "https://github.com/soju6jan/sjva"
|
||||
description: "SJVA 인증 & 사이트 연동"
|
||||
- title: "구드공 툴"
|
||||
package_name: "gds_tool"
|
||||
developer: "soju6jan"
|
||||
home: "https://github.com/soju6jan/gds_tool"
|
||||
description: "복사요청, 제공, GDS 변경사항"
|
||||
- title: "봇 다운로더"
|
||||
package_name: "bot_downloader"
|
||||
developer: "flaskfarm"
|
||||
home: "https://github.com/soju6jan/bot_downloader"
|
||||
description: "봇 다운로더"
|
||||
# name: "기타"
|
||||
# list:
|
||||
# - title: "숫자 야구"
|
||||
# package_name: "number_baseball"
|
||||
# developer: "FlaskFarm"
|
||||
# home: "https://github.com/flaskfarm/number_baseball"
|
||||
# description: "숫자 야구 - 샘플 플러그인"
|
||||
# - title: "샘플 플러그인"
|
||||
# package_name: "sample"
|
||||
# developer: "FlaskFarm"
|
||||
# home: "https://github.com/flaskfarm/sample"
|
||||
# description: "샘플 플러그인"
|
||||
|
||||
|
||||
9
lib/system/files/매뉴얼_설정.md
Normal file
9
lib/system/files/매뉴얼_설정.md
Normal file
@@ -0,0 +1,9 @@
|
||||
### 알림
|
||||
|
||||
##### 텔레그램
|
||||
|
||||
* @BotFather 에게서 Bot 생성
|
||||
* https://api.telegram.org/bot봇토큰/getUpdates 접속
|
||||
* 본인의 봇과 대화
|
||||
* https://api.telegram.org/bot봇토큰/getUpdates 접속
|
||||
"from":{"id":879500000, 숫자가 본인의 Chat ID
|
||||
@@ -31,7 +31,7 @@ class ModuleHome(PluginModuleBase):
|
||||
for key, value in F.app.config.items():
|
||||
if key not in ['SECRET_KEY']:
|
||||
data[key] = str(value)
|
||||
ret = {'json':{'Framework':F.config, 'Flask':data}}
|
||||
ret = {'json':{'Framework':F.config, 'Flask':data}, 'title':'config'}
|
||||
return jsonify(ret)
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class ModuleHome(PluginModuleBase):
|
||||
ret = {}
|
||||
ret['system'] = self.get_info()
|
||||
ret['scheduler'] = scheduler.get_job_list_info()
|
||||
F.socketio.emit("status", ret, namespace=f'/{P.package_name}/{name}', broadcast=True)
|
||||
F.socketio.emit("status", ret, namespace=f'/{P.package_name}/{name}')
|
||||
|
||||
|
||||
def get_info(self, mode=''):
|
||||
|
||||
@@ -22,14 +22,11 @@ class ModuleLog(PluginModuleBase):
|
||||
log_list.append(x)
|
||||
arg['log_list'] = '|'.join(log_list)
|
||||
arg['all_list'] = '|'.join(log_files)
|
||||
arg['filename'] = 'framework.log'
|
||||
print(request.form)
|
||||
print(request.form)
|
||||
print(request.form)
|
||||
print(request.form)
|
||||
arg['filename'] = 'all.log'
|
||||
|
||||
if 'filename' in request.form:
|
||||
arg['filename'] = request.form['filename']
|
||||
arg['filename'] = req.args.get('filename', arg['filename'])
|
||||
return render_template(f'{__package__}_{name}.html', arg=arg)
|
||||
except Exception as e:
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import shutil
|
||||
|
||||
from support import SupportFile
|
||||
from support import SupportFile, SupportYaml
|
||||
|
||||
from .setup import *
|
||||
|
||||
name = 'plugin'
|
||||
|
||||
class ModulePlugin(PluginModuleBase):
|
||||
|
||||
def __init__(self, P):
|
||||
super(ModulePlugin, self).__init__(P, name=name, first_menu='list')
|
||||
self.all_plugin_list = None
|
||||
|
||||
|
||||
def process_menu(self, page, req):
|
||||
@@ -36,14 +38,16 @@ class ModulePlugin(PluginModuleBase):
|
||||
"""
|
||||
for name, entity in F.PluginManager.all_package_list.items():
|
||||
try:
|
||||
if entity.get('version') == '3':
|
||||
#data.append(entity)
|
||||
data.append({'package_name':name})
|
||||
else:
|
||||
if 'P' in entity:
|
||||
data.append(entity['P'].plugin_info)
|
||||
data[-1]['loading'] = entity.get('loading')
|
||||
data[-1]['status'] = entity.get('status')
|
||||
data[-1]['log'] = entity.get('log')
|
||||
else:
|
||||
data.append({'package_name':name})
|
||||
data[-1]['loading'] = entity.get('loading')
|
||||
data[-1]['status'] = entity.get('status')
|
||||
data[-1]['log'] = entity.get('log')
|
||||
except Exception as e:
|
||||
data.append({'package_name':name})
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
@@ -64,6 +68,36 @@ class ModulePlugin(PluginModuleBase):
|
||||
else:
|
||||
ret['msg'] = info['path'] + "<br>폴더가 없습니다."
|
||||
ret['ret'] = 'danger'
|
||||
elif command == 'get_plugin_list_all':
|
||||
if self.all_plugin_list == None:
|
||||
filepath = os.path.join(os.path.dirname(__file__), 'files', 'all_plugin.yaml')
|
||||
self.all_plugin_list = SupportYaml.read_yaml(filepath)
|
||||
|
||||
def get_plugin(_name):
|
||||
for _cate in self.all_plugin_list:
|
||||
for _plugin in _cate['list']:
|
||||
if _plugin['package_name'] == _name:
|
||||
P.logger.info(_name)
|
||||
if _name == 'ff_reystream':
|
||||
P.logger.info(_name)
|
||||
return _plugin
|
||||
|
||||
|
||||
for name, entity in F.PluginManager.all_package_list.items():
|
||||
try:
|
||||
_plugin = get_plugin(name)
|
||||
if _plugin != None:
|
||||
_plugin['loading'] = entity.get('loading')
|
||||
_plugin['status'] = entity.get('status')
|
||||
_plugin['log'] = entity.get('log')
|
||||
|
||||
_plugin['version'] = entity['P'].plugin_info['version']
|
||||
|
||||
except Exception as e:
|
||||
data.append({'package_name':name})
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
P.logger.error(traceback.format_exc())
|
||||
ret['data'] = self.all_plugin_list
|
||||
return jsonify(ret)
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class ModuleRoute(PluginModuleBase):
|
||||
|
||||
@F.socketio.on('connect', namespace=f'/{P.package_name}/restart')
|
||||
def restart_socket_connect():
|
||||
F.socketio.emit('connect', {}, namespace='/{P.package_name}/restart', broadcast=True)
|
||||
F.socketio.emit('connect', {}, namespace='/{P.package_name}/restart')
|
||||
|
||||
|
||||
def process_menu(self, page, req):
|
||||
|
||||
@@ -2,8 +2,8 @@ import random
|
||||
import string
|
||||
import time
|
||||
|
||||
from support import (SupportDiscord, SupportFile, SupportSubprocess,
|
||||
SupportTelegram)
|
||||
from support import (SupportDiscord, SupportFile, SupportSlack,
|
||||
SupportSubprocess, SupportTelegram, SupportYaml)
|
||||
from tool import ToolModalCommand
|
||||
|
||||
from .setup import *
|
||||
@@ -12,7 +12,7 @@ name = 'setting'
|
||||
|
||||
class ModuleSetting(PluginModuleBase):
|
||||
db_default = {
|
||||
'db_version' : '1',
|
||||
'db_version' : '1.1',
|
||||
'port' : '9999',
|
||||
'ddns' : 'http://localhost:9999',
|
||||
'use_login' : 'False',
|
||||
@@ -34,6 +34,8 @@ class ModuleSetting(PluginModuleBase):
|
||||
'notify_telegram_disable_notification' : 'False',
|
||||
'notify_discord_use' : 'False',
|
||||
'notify_discord_webhook' : '',
|
||||
'notify_slack_use' : 'False',
|
||||
'notify_slack_webhook' : '',
|
||||
'notify_advaned_use' : 'False',
|
||||
'notify.yaml': '', #직접 사용하지 않으나 저장 편의상.
|
||||
'command_text': '',
|
||||
@@ -91,10 +93,15 @@ class ModuleSetting(PluginModuleBase):
|
||||
ret['msg'] = 'export.sh 파일이 없습니다.'
|
||||
elif command == 'menu_save':
|
||||
SupportFile.write_file(F.config['menu_yaml_filepath'], arg1 )
|
||||
ret['msg'] = '저장하였습니다.'
|
||||
from framework.init_menu import MenuManager
|
||||
MenuManager.init_menu()
|
||||
F.socketio.emit("refresh", {}, namespace='/framework', broadcast=True)
|
||||
try:
|
||||
SupportYaml.read_yaml(F.config['menu_yaml_filepath'])
|
||||
ret['msg'] = '저장하였습니다.'
|
||||
from framework.init_menu import MenuManager
|
||||
MenuManager.init_menu()
|
||||
F.socketio.emit("refresh", {}, namespace='/framework')
|
||||
except:
|
||||
ret['ret'] = "danger"
|
||||
ret['msg'] = "yaml 형식에 맞지 않습니다"
|
||||
elif command == 'notify_test':
|
||||
if arg1 == 'telegram':
|
||||
token, chatid, sound, text = arg2.split('||')
|
||||
@@ -104,6 +111,9 @@ class ModuleSetting(PluginModuleBase):
|
||||
elif arg1 == 'discord':
|
||||
SupportDiscord.send_discord_message(arg3, webhook_url=arg2)
|
||||
ret['msg'] = '메시지를 전송했습니다.'
|
||||
elif arg1 == 'slack':
|
||||
SupportSlack.send_slack_message(arg3, webhook_url=arg2)
|
||||
ret['msg'] = '메시지를 전송했습니다.'
|
||||
elif arg1 == 'advanced':
|
||||
from tool import ToolNotify
|
||||
ToolNotify.send_advanced_message(arg3, message_id=arg2)
|
||||
@@ -122,7 +132,31 @@ class ModuleSetting(PluginModuleBase):
|
||||
ret['type'] = 'warning'
|
||||
elif command == 'command_run':
|
||||
ret['msg'] = arg1
|
||||
pass
|
||||
SystemModelSetting.set('command_text', arg1)
|
||||
# db이름 set/get key value
|
||||
try:
|
||||
tmp = arg1.strip().split(' ')
|
||||
if tmp[0].startswith('setting'):
|
||||
plugin = F.PluginManager.get_plugin_instance(tmp[1])
|
||||
if len(tmp) == 2 or tmp[2] == 'all':
|
||||
ret['json'] = plugin.ModelSetting.to_dict()
|
||||
ret['ret'] = 'success'
|
||||
elif tmp[2] == 'get':
|
||||
ret['msg'] = plugin.ModelSetting.get(tmp[3])
|
||||
ret['ret'] = 'success'
|
||||
elif tmp[2] == 'set':
|
||||
value = ""
|
||||
if len(tmp) == 5:
|
||||
value = tmp[4]
|
||||
plugin.ModelSetting.set(tmp[3], value)
|
||||
ret['msg'] = f"{tmp[1]} DB에 {tmp[3]}={value} 저장"
|
||||
|
||||
except Exception as e:
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
P.logger.error(traceback.format_exc())
|
||||
ret['msg'] = f"실행 실패: {str(e)}"
|
||||
ret['type'] = 'danger'
|
||||
|
||||
elif command == 'celery_execute':
|
||||
self.celery_execute(arg1, mode='foreground')
|
||||
elif command == 'celery_execute_back':
|
||||
@@ -138,8 +172,7 @@ class ModuleSetting(PluginModuleBase):
|
||||
try:
|
||||
if F.config['run_flask'] == False:
|
||||
return
|
||||
if SystemModelSetting.get_bool('celery_start_by_web'):
|
||||
self.celery_execute()
|
||||
|
||||
if F.config['arg_repeat'] == 0 or SystemModelSetting.get('system_start_time') == '':
|
||||
SystemModelSetting.set('system_start_time', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
|
||||
SystemModelSetting.set('repeat', str(F.config['arg_repeat']))
|
||||
@@ -162,7 +195,11 @@ class ModuleSetting(PluginModuleBase):
|
||||
from tool import ToolNotify
|
||||
msg = f"시스템이 시작되었습니다.\n재시작: {F.config['arg_repeat']}"
|
||||
ToolNotify.send_message(msg, message_id='system_start')
|
||||
|
||||
if SystemModelSetting.get_bool('celery_start_by_web'):
|
||||
# 2022-11-14 DB는 flask가 만드는데 만들기전 celery를 실행해버림
|
||||
from threading import Timer
|
||||
Timer(10, self.celery_execute).start()
|
||||
#self.celery_execute()
|
||||
|
||||
except Exception as e:
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
@@ -173,9 +210,14 @@ class ModuleSetting(PluginModuleBase):
|
||||
|
||||
def setting_save_after(self, change_list):
|
||||
if 'theme' in change_list or 'web_title' in change_list:
|
||||
F.socketio.emit("refresh", {}, namespace='/framework', broadcast=True)
|
||||
F.socketio.emit("refresh", {}, namespace='/framework')
|
||||
elif 'notify.yaml' in change_list:
|
||||
SupportFile.write_file(F.config['notify_yaml_filepath'], SystemModelSetting.get('notify.yaml'))
|
||||
try:
|
||||
SupportFile.write_file(F.config['notify_yaml_filepath'], SystemModelSetting.get('notify.yaml'))
|
||||
SupportYaml.read_yaml(F.config['notify_yaml_filepath'])
|
||||
except:
|
||||
data = {'type':'danger', 'msg' : "알림 정책이 yaml 형식에 맞지 않습니다."}
|
||||
F.socketio.emit("notify", data, namespace='/framework')
|
||||
elif 'web_pw' in change_list:
|
||||
import hashlib
|
||||
enc = hashlib.md5()
|
||||
@@ -185,6 +227,9 @@ class ModuleSetting(PluginModuleBase):
|
||||
if SystemModelSetting.get('restart_interval') == '':
|
||||
SystemModelSetting.set('restart_interval', '0')
|
||||
self.__set_restart_scheduler()
|
||||
elif 'log_level' in change_list:
|
||||
F.set_level(SystemModelSetting.get_int('log_level'))
|
||||
|
||||
|
||||
|
||||
def __set_restart_scheduler(self):
|
||||
@@ -258,6 +303,7 @@ class ModuleSetting(PluginModuleBase):
|
||||
try:
|
||||
time.sleep(1)
|
||||
data = '정상입니다. 이 메시지는 celery 에서 반환됩니다. '
|
||||
P.logger.info(data)
|
||||
return data
|
||||
except Exception as e:
|
||||
P.logger.error(f'Exception:{str(e)}')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import queue
|
||||
import shlex
|
||||
|
||||
from support import SupportSubprocess
|
||||
from tool import ToolModalCommand
|
||||
@@ -25,7 +26,7 @@ class PageCommand(PluginPageBase):
|
||||
ret = {'ret':'success'}
|
||||
if command == 'foreground_command':
|
||||
P.ModelSetting.set(f'{self.parent.name}_{self.name}_recent', arg1)
|
||||
self.__foreground_execute(arg1, arg1.split(' '))
|
||||
self.__foreground_execute(arg1, shlex.split(arg1))
|
||||
|
||||
return jsonify('')
|
||||
elif command == 'job_new':
|
||||
@@ -54,7 +55,7 @@ class PageCommand(PluginPageBase):
|
||||
elif command == 'job_fore_execute':
|
||||
db_item = ModelCommand.get_by_id(arg1)
|
||||
cmd = (db_item.command + ' ' + db_item.args).strip()
|
||||
self.__foreground_execute(f"Command ID: {db_item.id}", cmd.split(' '), db_item.id)
|
||||
self.__foreground_execute(f"Command ID: {db_item.id}", shlex.split(cmd), db_item.id)
|
||||
elif command == 'job_back_execute':
|
||||
self.execute_thread_start(arg1)
|
||||
ret['msg'] = "실행 요청을 하였습니다.<br>로그를 확인하세요."
|
||||
@@ -64,8 +65,8 @@ class PageCommand(PluginPageBase):
|
||||
ret['ret'] = 'danger'
|
||||
ret['msg'] = "로그 파일이 없습니다."
|
||||
elif command == 'task_sched':
|
||||
job_id = req.form['arg1']
|
||||
flag = (req.form['arg2'] == 'true')
|
||||
job_id = arg1
|
||||
flag = (arg2 == 'true')
|
||||
scheduler_id = f'command_{job_id}'
|
||||
if flag and F.scheduler.is_include(scheduler_id):
|
||||
ret['msg'] = '이미 스케쥴러에 등록되어 있습니다.'
|
||||
@@ -92,7 +93,7 @@ class PageCommand(PluginPageBase):
|
||||
if command[0] != 'LOAD':
|
||||
ToolModalCommand.start(title, [command])
|
||||
else:
|
||||
F.socketio.emit("command_modal_show", title, namespace='/framework', broadcast=True)
|
||||
F.socketio.emit("command_modal_show", title, namespace='/framework')
|
||||
def start_communicate_load(load_log_list):
|
||||
def func():
|
||||
while True:
|
||||
@@ -100,7 +101,7 @@ class PageCommand(PluginPageBase):
|
||||
load_log_list.truncate(0)
|
||||
if logs:
|
||||
P.logger.error(logs)
|
||||
F.socketio.emit("command_modal_add_text", logs.strip() + '\n', namespace='/framework', broadcast=True)
|
||||
F.socketio.emit("command_modal_add_text", logs.strip() + '\n', namespace='/framework')
|
||||
if logs == '<<END>>':
|
||||
break
|
||||
time.sleep(0.3)
|
||||
@@ -154,11 +155,12 @@ class PageCommand(PluginPageBase):
|
||||
th = threading.Thread(target=self.execute_thread_function_by_job_id, args=(job_id,))
|
||||
th.setDaemon(True)
|
||||
th.start()
|
||||
return th
|
||||
|
||||
|
||||
def execute_thread_function_by_job_id(self, *args, **kwargs):
|
||||
P.logger.error(d(args))
|
||||
P.logger.error(d(kwargs))
|
||||
#P.logger.error(d(args))
|
||||
#P.logger.error(d(kwargs))
|
||||
db_item = ModelCommand.get_by_id(args[0])
|
||||
kwargs['id'] = args[0]
|
||||
self.execute_thread_function((db_item.command + ' ' + db_item.args).strip(), **kwargs)
|
||||
@@ -166,7 +168,7 @@ class PageCommand(PluginPageBase):
|
||||
|
||||
def execute_thread_function(self, command, **kwargs):
|
||||
try:
|
||||
cmd = command.split(' ')
|
||||
cmd = shlex.split(command)
|
||||
|
||||
if cmd[0] == 'LOAD':
|
||||
command_logger = F.get_logger(f"command_{kwargs['id']}")
|
||||
@@ -177,8 +179,8 @@ class PageCommand(PluginPageBase):
|
||||
def __init__(self, logger):
|
||||
self.logger = logger
|
||||
|
||||
def stdout_callback(self, mode, text):
|
||||
if mode == 'log':
|
||||
def stdout_callback(self, call_id, mode, text):
|
||||
if mode == 'LOG':
|
||||
self.logger.debug(text)
|
||||
else:
|
||||
self.logger.debug(mode)
|
||||
@@ -194,14 +196,17 @@ class PageCommand(PluginPageBase):
|
||||
def plugin_load(self):
|
||||
def plugin_load_thread():
|
||||
try:
|
||||
while F.config['loading_completed'] == False:
|
||||
time.sleep(1)
|
||||
|
||||
db_items = ModelCommand.get_list()
|
||||
for db_item in db_items:
|
||||
if db_item.schedule_mode == 'startup':
|
||||
self.execute_thread_start(db_item.id)
|
||||
elif db_item.schedule_mode == 'scheduler' and db_item.schedule_auto_start:
|
||||
self.__sched_add(db_item.id, db_item=db_item)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
try:
|
||||
th = threading.Thread(target=plugin_load_thread)
|
||||
@@ -219,7 +224,7 @@ class PageCommand(PluginPageBase):
|
||||
job_id = f"command_{db_item.id}"
|
||||
if scheduler.is_include(job_id):
|
||||
return
|
||||
job = Job(self.P.package_name, job_id, db_item.schedule_interval, self.execute_thread_function_by_job_id, db_item.description, args=db_item.id)
|
||||
job = Job(self.P.package_name, job_id, db_item.schedule_interval, self.execute_thread_function_by_job_id, db_item.description, args=(db_item.id,))
|
||||
scheduler.add_job_instance(job)
|
||||
return True
|
||||
except Exception as e:
|
||||
@@ -286,6 +291,6 @@ class ModelCommand(ModelBase):
|
||||
item['process'] = (SupportSubprocess.get_instance_by_call_id(f"command_{item['id']}") != None)
|
||||
|
||||
return data
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@@ -14,6 +14,7 @@ __menu = {
|
||||
{'uri': 'export', 'name': 'export.sh 파일'},
|
||||
{'uri': 'celery', 'name': '비동기 작업(celery)'},
|
||||
{'uri': 'notify', 'name': '알림'},
|
||||
{'uri': 'manual/files/매뉴얼_설정.md', 'name': '매뉴얼'},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -22,7 +23,7 @@ __menu = {
|
||||
'list': [
|
||||
#{'uri': 'setting', 'name': '설정'},
|
||||
{'uri': 'list', 'name': '로딩 플러그인'},
|
||||
#{'uri': 'all', 'name': '플러그인 목록'},
|
||||
{'uri': 'all', 'name': '전체 플러그인 목록'},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,33 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
</style>
|
||||
<div>
|
||||
{{ macros.setting_select_empty('log_select1', '로그 파일 선택 (.log)') }}
|
||||
<!--{{ macros.setting_select_empty('log_select2', '로그 파일 선택 (.logX)') }}-->
|
||||
<nav>
|
||||
{{ macros.m_tab_head_start() }}
|
||||
{{ macros.m_tab_head('old', '이전', true) }}
|
||||
{{ macros.m_tab_head('new', '실시간', false) }}
|
||||
{{ macros.m_tab_head_end() }}
|
||||
</nav>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
{{ macros.m_tab_content_start('old', true) }}
|
||||
<div>
|
||||
<textarea id="log" class="col-md-12" rows="30" charswidth="23" disabled style="background-color:#ffffff;visibility:hidden"></textarea>
|
||||
</div>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
{{ macros.m_tab_content_start('new', false) }}
|
||||
<div>
|
||||
<textarea id="add" class="col-md-12" rows="30" charswidth="23" disabled style="background-color:#ffffff;visibility:visible"></textarea>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
<label class="form-check-label" for="auto_scroll">자동 스크롤</label>
|
||||
<input id="auto_scroll" name="auto_scroll" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
|
||||
<span class='text-left' style="padding-left:25px; padding-top:0px">
|
||||
<button id="clear" class="btn btn-sm btn-outline-success">리셋</button>
|
||||
</span>
|
||||
</div>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
<div id="log_div" class="bg-dark" style="overflow:auto; border-color: blue; border: solid 1px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,14 +32,6 @@ function make_form() {
|
||||
str += '<option value="' + data[i] + '">' + data[i] + '</option>';
|
||||
}
|
||||
$("#log_select1_div").html(str);
|
||||
/*
|
||||
str = '<select id="log_select" name="log_select" class="form-control form-control-sm">';
|
||||
data = all_list.split('|')
|
||||
for(var i in data) {
|
||||
str += '<option value="' + data[i] + '">' + data[i] + '</option>';
|
||||
}
|
||||
$("#log_select2_div").html(str);
|
||||
*/
|
||||
}
|
||||
|
||||
$("body").on('change', '#log_select', function(e){
|
||||
@@ -75,11 +43,9 @@ $("body").on('change', '#log_select', function(e){
|
||||
socket.emit("start", {'filename':filename} );
|
||||
});
|
||||
|
||||
|
||||
function ResizeTextAreaAllLog() {
|
||||
ClientHeight = window.innerHeight
|
||||
$("#log").height(ClientHeight-300);
|
||||
$("#add").height(ClientHeight-320);
|
||||
$("#log_div").height(ClientHeight-180);
|
||||
}
|
||||
|
||||
$(window).resize(function() {
|
||||
@@ -88,17 +54,20 @@ $(window).resize(function() {
|
||||
|
||||
|
||||
socket.on('on_start', function(data){
|
||||
document.getElementById("log").innerHTML += data.data;
|
||||
document.getElementById("log").scrollTop = document.getElementById("log").scrollHeight;
|
||||
document.getElementById("log").style.visibility = 'visible';
|
||||
lines = splitLines(data.data);
|
||||
var html = '';
|
||||
for (i in lines) {
|
||||
html += logline(lines[i]);
|
||||
}
|
||||
$('#log_div').html(html)
|
||||
document.getElementById("log_div").scrollTop = document.getElementById("log_div").scrollHeight;
|
||||
$('#loading').hide();
|
||||
});
|
||||
|
||||
socket.on('add', function(data){
|
||||
if (data.filename == current_filename) {
|
||||
var chk = $('#auto_scroll').is(":checked");
|
||||
document.getElementById("add").innerHTML += data.data;
|
||||
if (chk) document.getElementById("add").scrollTop = document.getElementById("add").scrollHeight;
|
||||
$('#log_div').append(logline(data.data.trim()));
|
||||
document.getElementById("log_div").scrollTop = document.getElementById("log_div").scrollHeight;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<canvas id="mycanvas" height="100vh"></canvas>
|
||||
<h3>시스템</h3>
|
||||
<hr>
|
||||
{{ macros.info_text_and_buttons('python_version', 'Python', [['globalLinkBtn', '패키지 관리', [('url','/system/tool/python')]]], info['python_version']) }}
|
||||
@@ -45,21 +45,102 @@
|
||||
|
||||
<h3>스케쥴</h3>
|
||||
<div id="scheduler_list_div"></div>
|
||||
</div> <!--전체-->
|
||||
</div>
|
||||
<!--전체-->
|
||||
<script src="{{ url_for('static', filename='js/chartjs-utils.js') }}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||||
<script>
|
||||
const Utils = ChartUtils.init()
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/luxon@3.0.4"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.2.0"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@2.0.0"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
var socket = io.connect(window.location.href);
|
||||
Chart.defaults.set("plugins.streaming", {
|
||||
duration: 20000,
|
||||
})
|
||||
|
||||
socket.on('start', function(data){
|
||||
});
|
||||
// used for example purposes
|
||||
function getRandomIntInclusive(min, max) {
|
||||
min = Math.ceil(min)
|
||||
max = Math.floor(max)
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
}
|
||||
|
||||
socket.on('status', function(data) {
|
||||
make_system(data.system);
|
||||
make_scheduler_list(data.scheduler);
|
||||
});
|
||||
});
|
||||
$(document).ready(function () {
|
||||
var socket = io.connect(window.location.href)
|
||||
var postId = 1
|
||||
socket.on("start", function (data) {})
|
||||
|
||||
socket.on("status", function (data) {
|
||||
console.log(data.system.cpu_percent)
|
||||
const now = Date.now()
|
||||
//myChart.data.labels.push("T " + postId++)
|
||||
/* */
|
||||
myChart.data.datasets[0].data.push({ x: now, y: data.system.cpu_percent.replace(/.%/g, "") })
|
||||
/* */
|
||||
myChart.update()
|
||||
make_system(data.system)
|
||||
make_scheduler_list(data.scheduler)
|
||||
})
|
||||
|
||||
/* const onRefresh = (chart) => {
|
||||
const now = Date.now()
|
||||
chart.data.datasets.forEach((dataset) => {
|
||||
dataset.data.push({
|
||||
x: now,
|
||||
y: Utils.rand(0, 100),
|
||||
})
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
var ctx_live = document.getElementById("mycanvas")
|
||||
var myChart = new Chart(ctx_live, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "CPU",
|
||||
backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5),
|
||||
borderColor: Utils.CHART_COLORS.blue,
|
||||
cubicInterpolationMode: "monotone",
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
plugins: {
|
||||
// Change options for ALL axes of THIS CHART
|
||||
streaming: {
|
||||
duration: 20000,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "realtime",
|
||||
realtime: {
|
||||
duration: 60000,
|
||||
refresh: 1000,
|
||||
delay: 1000,
|
||||
//onRefresh: onRefresh,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
//text: "Value",
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
$("body").on('click', '#recent_version_btn', function(e){
|
||||
e.preventDefault();
|
||||
@@ -70,7 +151,7 @@ $("body").on('click', '#recent_version_btn', function(e){
|
||||
|
||||
$("body").on('click', '#config_show_btn', function(e){
|
||||
e.preventDefault();
|
||||
globalSendCommand('get_config', null, null, null, 'Config');
|
||||
globalSendCommand('get_config');
|
||||
});
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user