@@ -5,8 +5,26 @@ | |||
</component> | |||
<component name="ChangeListManager"> | |||
<list default="true" id="4f7dccd9-8f92-4a6e-90cc-33890d102263" name="Changes" comment="Changes"> | |||
<change afterPath="$PROJECT_DIR$/vodsdk/AliyunVodUploader.py" afterDir="false" /> | |||
<change afterPath="$PROJECT_DIR$/vodsdk/AliyunVodUtils.py" afterDir="false" /> | |||
<change afterPath="$PROJECT_DIR$/vodsdk/UploadAttachedMediaRequest.py" afterDir="false" /> | |||
<change afterPath="$PROJECT_DIR$/vodsdk/UploadImageRequest.py" afterDir="false" /> | |||
<change afterPath="$PROJECT_DIR$/vodsdk/UploadVideoRequest.py" afterDir="false" /> | |||
<change afterPath="$PROJECT_DIR$/vodsdk/__init__.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/common/Constant.py" beforeDir="false" afterPath="$PROJECT_DIR$/common/Constant.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/concurrency/CommonThread.py" beforeDir="false" afterPath="$PROJECT_DIR$/concurrency/CommonThread.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/concurrency/FileUploadThread.py" beforeDir="false" afterPath="$PROJECT_DIR$/concurrency/FileUploadThread.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/concurrency/IntelligentRecognitionProcess.py" beforeDir="false" afterPath="$PROJECT_DIR$/concurrency/IntelligentRecognitionProcess.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/enums/BaiduSdkEnum.py" beforeDir="false" afterPath="$PROJECT_DIR$/enums/BaiduSdkEnum.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/enums/ExceptionEnum.py" beforeDir="false" afterPath="$PROJECT_DIR$/enums/ExceptionEnum.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/test/aliyun/vod.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/aliyun/vod.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/test/aliyun/vodTest.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/aliyun/vodTest.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/test/aliyun/voddemo.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/aliyun/voddemo.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/test/aliyun/vodtest2.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/aliyun/vodtest2.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/test/序列化/Test.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/序列化/Test.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/test/线程/Test.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/线程/Test.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/util/AliyunSdk.py" beforeDir="false" afterPath="$PROJECT_DIR$/util/AliyunSdk.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/util/Cv2Utils.py" beforeDir="false" afterPath="$PROJECT_DIR$/util/Cv2Utils.py" afterDir="false" /> | |||
<change beforePath="$PROJECT_DIR$/util/ModelUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/util/ModelUtils.py" afterDir="false" /> | |||
</list> | |||
<option name="SHOW_DIALOG" value="false" /> | |||
@@ -136,7 +154,7 @@ | |||
"WebServerToolWindowPanel.toolwindow.show.date": "false", | |||
"WebServerToolWindowPanel.toolwindow.show.permissions": "false", | |||
"WebServerToolWindowPanel.toolwindow.show.size": "false", | |||
"last_opened_file_path": "D:/tuoheng/fanbojiaoyu", | |||
"last_opened_file_path": "D:/tuoheng/codenew/tuoheng_dsp", | |||
"node.js.detected.package.eslint": "true", | |||
"node.js.detected.package.tslint": "true", | |||
"node.js.selected.package.eslint": "(autodetect)", | |||
@@ -150,11 +168,11 @@ | |||
}]]></component> | |||
<component name="RecentsManager"> | |||
<key name="CopyFile.RECENT_KEYS"> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg" /> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg\test\color" /> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg\test\cuda" /> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg\util" /> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg\test" /> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg\test\aliyun" /> | |||
</key> | |||
<key name="MoveFile.RECENT_KEYS"> | |||
<recent name="D:\tuoheng\codenew\tuoheng_alg\font" /> | |||
@@ -163,8 +181,8 @@ | |||
<recent name="D:\work\alg\tuoheng_alg\image" /> | |||
</key> | |||
</component> | |||
<component name="RunManager" selected="Python.color_test"> | |||
<configuration name="IntelligentRecognitionProcess" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<component name="RunManager" selected="Python.Test (1)"> | |||
<configuration name="AliyunSdk" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
@@ -172,12 +190,12 @@ | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/concurrency" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/concurrency/IntelligentRecognitionProcess.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/AliyunSdk.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -186,7 +204,7 @@ | |||
<option name="INPUT_FILE" value="" /> | |||
<method v="2" /> | |||
</configuration> | |||
<configuration name="color_test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<configuration name="Test (1)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
@@ -194,12 +212,12 @@ | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/color" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/线程" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/color/color_test.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/线程/Test.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -208,20 +226,20 @@ | |||
<option name="INPUT_FILE" value="" /> | |||
<method v="2" /> | |||
</configuration> | |||
<configuration name="editImage" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true"> | |||
<configuration name="Test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
<envs> | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="$PROJECT_DIR$/../../../software/anaconda/envs/test/python.exe" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/editimage" /> | |||
<option name="IS_MODULE_SDK" value="false" /> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/序列化" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/editimage/editImage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/序列化/Test.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -230,7 +248,7 @@ | |||
<option name="INPUT_FILE" value="" /> | |||
<method v="2" /> | |||
</configuration> | |||
<configuration name="mysqltest" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true"> | |||
<configuration name="color_test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
@@ -238,12 +256,12 @@ | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/color" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/mysqltest.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/color/color_test.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -252,20 +270,20 @@ | |||
<option name="INPUT_FILE" value="" /> | |||
<method v="2" /> | |||
</configuration> | |||
<configuration name="test (2)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<configuration name="editImage" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
<envs> | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/进程" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="SDK_HOME" value="$PROJECT_DIR$/../../../software/anaconda/envs/test/python.exe" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/editimage" /> | |||
<option name="IS_MODULE_SDK" value="false" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/进程/test.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/editimage/editImage.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -274,7 +292,7 @@ | |||
<option name="INPUT_FILE" value="" /> | |||
<method v="2" /> | |||
</configuration> | |||
<configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<configuration name="mysqltest" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
@@ -282,12 +300,12 @@ | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/集合" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/集合/test.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/mysqltest.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -296,7 +314,7 @@ | |||
<option name="INPUT_FILE" value="" /> | |||
<method v="2" /> | |||
</configuration> | |||
<configuration name="test1" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<configuration name="test (2)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> | |||
<module name="tuoheng_alg" /> | |||
<option name="INTERPRETER_OPTIONS" value="" /> | |||
<option name="PARENT_ENVS" value="true" /> | |||
@@ -304,12 +322,12 @@ | |||
<env name="PYTHONUNBUFFERED" value="1" /> | |||
</envs> | |||
<option name="SDK_HOME" value="" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/cuda" /> | |||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/进程" /> | |||
<option name="IS_MODULE_SDK" value="true" /> | |||
<option name="ADD_CONTENT_ROOTS" value="true" /> | |||
<option name="ADD_SOURCE_ROOTS" value="true" /> | |||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/cuda/test1.py" /> | |||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/进程/test.py" /> | |||
<option name="PARAMETERS" value="" /> | |||
<option name="SHOW_COMMAND_LINE" value="false" /> | |||
<option name="EMULATE_TERMINAL" value="false" /> | |||
@@ -321,19 +339,19 @@ | |||
<list> | |||
<item itemvalue="Python.editImage" /> | |||
<item itemvalue="Python.mysqltest" /> | |||
<item itemvalue="Python.Test (1)" /> | |||
<item itemvalue="Python.AliyunSdk" /> | |||
<item itemvalue="Python.Test" /> | |||
<item itemvalue="Python.color_test" /> | |||
<item itemvalue="Python.test (2)" /> | |||
<item itemvalue="Python.IntelligentRecognitionProcess" /> | |||
<item itemvalue="Python.test" /> | |||
<item itemvalue="Python.test1" /> | |||
</list> | |||
<recent_temporary> | |||
<list> | |||
<item itemvalue="Python.Test (1)" /> | |||
<item itemvalue="Python.AliyunSdk" /> | |||
<item itemvalue="Python.Test" /> | |||
<item itemvalue="Python.color_test" /> | |||
<item itemvalue="Python.test (2)" /> | |||
<item itemvalue="Python.IntelligentRecognitionProcess" /> | |||
<item itemvalue="Python.test" /> | |||
<item itemvalue="Python.test1" /> | |||
</list> | |||
</recent_temporary> | |||
</component> | |||
@@ -489,7 +507,11 @@ | |||
<workItem from="1683506530261" duration="919000" /> | |||
<workItem from="1683507482567" duration="15434000" /> | |||
<workItem from="1683591783960" duration="1186000" /> | |||
<workItem from="1683677260592" duration="8827000" /> | |||
<workItem from="1683677260592" duration="21750000" /> | |||
<workItem from="1683762579964" duration="23871000" /> | |||
<workItem from="1683851036596" duration="51000" /> | |||
<workItem from="1683851900729" duration="83000" /> | |||
<workItem from="1683851995142" duration="23673000" /> | |||
</task> | |||
<servers /> | |||
</component> | |||
@@ -557,7 +579,7 @@ | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$dsp_master.coverage" NAME="dsp_master 覆盖结果" MODIFIED="1680503755624" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$test.coverage" NAME="test 覆盖结果" MODIFIED="1682582986112" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/集合" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$IntelligentRecognitionProcess.coverage" NAME="IntelligentRecognitionProcess 覆盖结果" MODIFIED="1682651444560" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/concurrency" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$Test.coverage" NAME="Test 覆盖结果" MODIFIED="1681810213173" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/序列化" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$Test.coverage" NAME="Test 覆盖结果" MODIFIED="1683802532361" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/序列化" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$mysqltest.coverage" NAME="mysqltest Coverage Results" MODIFIED="1660868712851" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$asnyc__1_.coverage" NAME="asnyc (1) Coverage Results" MODIFIED="1663458917599" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$cv2test1.coverage" NAME="cv2test1 覆盖结果" MODIFIED="1665738045603" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="/home/DATA/chenyukun/algSch/test/" /> | |||
@@ -576,6 +598,7 @@ | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$gputest.coverage" NAME="gputest 覆盖结果" MODIFIED="1681950938970" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/gpu" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$1.coverage" NAME="协程1 覆盖结果" MODIFIED="1667866542122" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/协程" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg___$3.coverage" NAME="协程3 覆盖结果" MODIFIED="1668147029048" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/协程" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$AliyunSdk.coverage" NAME="AliyunSdk 覆盖结果" MODIFIED="1683803902993" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$asnyc.coverage" NAME="asnyc Coverage Results" MODIFIED="1663459033435" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$5.coverage" NAME="视频添加图片水印5 Coverage Results" MODIFIED="1661905982885" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$read.coverage" NAME="read Coverage Results" MODIFIED="1663640070956" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test" /> | |||
@@ -583,7 +606,7 @@ | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$TimeUtils.coverage" NAME="TimeUtils Coverage Results" MODIFIED="1661222768678" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/util" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$producer_start1.coverage" NAME="producer_start1 覆盖结果" MODIFIED="1671428635702" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/kafka" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg___$producer_stop.coverage" NAME="producer_stop 覆盖结果" MODIFIED="1668522920533" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="/home/thsw/chenyukun/algSch" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$Test__1_.coverage" NAME="Test (1) 覆盖结果" MODIFIED="1681199611277" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/线程" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$Test__1_.coverage" NAME="Test (1) 覆盖结果" MODIFIED="1683865962957" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/线程" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$ffmpeg13.coverage" NAME="ffmpeg13 覆盖结果" MODIFIED="1675394160900" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/ffmpeg11" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$KafkaUtils.coverage" NAME="KafkaUtils Coverage Results" MODIFIED="1663465345491" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/util" /> | |||
<SUITE FILE_PATH="coverage/tuoheng_alg$test__2_.coverage" NAME="test (2) 覆盖结果" MODIFIED="1683355406740" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/进程" /> |
@@ -4,18 +4,19 @@ from loguru import logger | |||
class Common(Thread): | |||
def __init__(self, content, func, args=()): | |||
def __init__(self, content, func, param1, param2): | |||
super(Common, self).__init__() | |||
self.content = content | |||
self.func = func | |||
self.args = args | |||
self.param1 = param1 | |||
self.param2 = param2 | |||
self.result = None | |||
def get_result(self): | |||
self.join(60 * 60 * 3) | |||
self.join(60 * 60 * 12) | |||
return self.result | |||
def run(self): | |||
logger.info("开始执行线程!") | |||
self.result = self.func(self.args) | |||
self.result = self.func(self.param1, self.param2) | |||
logger.info("线程停止完成!") |
@@ -113,7 +113,7 @@ class ImageFileUpload(FileUpload): | |||
def run(self): | |||
logger.info("启动图片上传线程, requestId:{}", self.msg.get("request_id")) | |||
# 初始化oss客户端 | |||
aliyunOssSdk = AliyunOssSdk(self.content, logger, self.msg.get("request_id")) | |||
aliyunOssSdk = AliyunOssSdk(self.content, self.msg.get("request_id")) | |||
aliyunOssSdk.get_oss_bucket() | |||
high_score_image = {} | |||
with ThreadPoolExecutor(max_workers=5) as t: |
@@ -4,7 +4,7 @@ import json | |||
import os | |||
import time | |||
import copy | |||
from concurrent.futures import ThreadPoolExecutor, as_completed | |||
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED | |||
import cv2 | |||
import numpy as np | |||
@@ -13,6 +13,7 @@ from common import Constant | |||
from multiprocessing import Process, Queue | |||
from loguru import logger | |||
from concurrency.CommonThread import Common | |||
from concurrency.PullStreamThread import RecordingPullStreamThread | |||
from concurrency.PullVideoStreamProcess import OnlinePullVideoStreamProcess, OfflinePullVideoStreamProcess | |||
from concurrency.RecordingHeartbeatThread import RecordingHeartbeat | |||
@@ -250,8 +251,12 @@ class IntelligentRecognitionProcess(Process): | |||
for mod in frame[1]: | |||
result = frame[3].submit(self.analyze, mod, or_frame, frame[2].w) | |||
analyze_result.append(result) | |||
results = wait(analyze_result, timeout=60, return_when=ALL_COMPLETED) | |||
completed_futures = results.done | |||
det_xywh = {} | |||
for r in as_completed(analyze_result): | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
p_result, timeOut, code, allowedList, label_arraylist, rainbows = r.result() | |||
if allowedList is not None: | |||
is_call_model = True | |||
@@ -317,20 +322,25 @@ class OnlineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
self.pull_stream_timeout = int(self.content["service"]["cv2_pull_stream_timeout"]) | |||
# 停止任务方法 | |||
def stop_task(self, cv2tool, pullProcess, snalysisStatus, t): | |||
def stop_task(self, cv2tool, pullProcess, snalysisStatus): | |||
cv2tool.close() | |||
pullProcess.sendCommand({"command": "stop_pull_stream"}) | |||
if not os.path.exists(self.orFilePath) or not os.path.exists(self.aiFilePath): | |||
logger.error("原视频或AI视频不存在!requestId:{}", self.msg.get("request_id")) | |||
raise ServiceException(ExceptionType.PUSH_STREAM_TIME_EXCEPTION.value[0], | |||
ExceptionType.PUSH_STREAM_TIME_EXCEPTION.value[1]) | |||
aliyunVodSdk = ThAliyunVodSdk(self.content, logger, self.msg.get("request_id")) | |||
upload_video_thread_or = t.submit(aliyunVodSdk.get_play_url, self.orFilePath, "orOnLineVideo") | |||
upload_video_thread_ai = t.submit(aliyunVodSdk.get_play_url, self.aiFilePath, "aiOnLineVideo") | |||
url_result = [] | |||
for url in as_completed([upload_video_thread_or, upload_video_thread_ai]): | |||
url_result.append(url.result()) | |||
if len(url_result) != 2: | |||
aliyunVodSdk = ThAliyunVodSdk(self.content, self.msg.get("request_id")) | |||
upload_video_thread_or = Common(self.content, aliyunVodSdk.get_play_url, self.orFilePath, | |||
"or_online_%s" % self.msg.get("request_id")) | |||
upload_video_thread_ai = Common(self.content, aliyunVodSdk.get_play_url, self.aiFilePath, | |||
"ai_online_%s" % self.msg.get("request_id")) | |||
upload_video_thread_or.setDaemon(True) | |||
upload_video_thread_ai.setDaemon(True) | |||
upload_video_thread_or.start() | |||
upload_video_thread_ai.start() | |||
or_url = upload_video_thread_or.get_result() | |||
ai_url = upload_video_thread_ai.get_result() | |||
if or_url is None or ai_url is None: | |||
logger.error("原视频或AI视频播放上传VOD失败!, requestId: {}", self.msg.get("request_id")) | |||
raise ServiceException(ExceptionType.GET_VIDEO_URL_EXCEPTION.value[0], | |||
ExceptionType.GET_VIDEO_URL_EXCEPTION.value[1]) | |||
@@ -339,8 +349,8 @@ class OnlineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
self.sendResult({"feedback": message_feedback(self.msg.get("request_id"), snalysisStatus, | |||
self.analyse_type, | |||
progress=Constant.success_progess, | |||
original_url=url_result[0], | |||
sign_url=url_result[1], | |||
original_url=or_url, | |||
sign_url=ai_url, | |||
analyse_time=TimeUtils.now_date_to_str())}) | |||
def start_pull_stream(self): | |||
@@ -372,7 +382,7 @@ class OnlineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
task_frame = None | |||
with ThreadPoolExecutor(max_workers=10) as t: | |||
with ThreadPoolExecutor(max_workers=4) as tt: | |||
with ThreadPoolExecutor(max_workers=5) as ttt: | |||
with ThreadPoolExecutor(max_workers=10) as ttt: | |||
while True: | |||
self.checkPullProcess(pullProcess) | |||
eBody = self.getEvent() | |||
@@ -399,8 +409,11 @@ class OnlineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
frame_all.get("frame")) | |||
write_ai_video_result = tt.submit(cv2tool.video_ai_write, frame_merge) | |||
res = [push_stream_result, write_or_video_result, write_ai_video_result] | |||
for re in as_completed(res): | |||
re.result() | |||
completed_results = wait(res, timeout=600, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
det_xywh = frame_all.get("det_xywh") | |||
if len(det_xywh) > 0: | |||
self.imageQueue.put({"image": frame_all}) | |||
@@ -409,11 +422,11 @@ class OnlineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
if status.get("status") == "1": | |||
raise ServiceException(status.get("error").get("code"), status.get("error").get("msg")) | |||
elif status.get("status") == "3": | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.TIMEOUT.value, t) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.TIMEOUT.value) | |||
break | |||
elif status.get("status") == "9": | |||
logger.info("实时任务正常结束:requestId: {}", self.msg.get("request_id")) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.SUCCESS.value, t) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.SUCCESS.value) | |||
break | |||
else: | |||
raise Exception("未知拉流状态异常!") | |||
@@ -470,16 +483,19 @@ class OfflineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
self.aiFilePath = "%s%s%s%s%s" % (self.content["video"]["file_path"], random_time, "_on_ai_", | |||
self.msg.get("request_id"), ".mp4") | |||
def stop_task(self, cv2tool, pullProcess, analysisStatus, t): | |||
def stop_task(self, cv2tool, pullProcess, analysisStatus): | |||
cv2tool.close() | |||
pullProcess.sendCommand({"command": "stop_pull_stream"}) | |||
if not os.path.exists(self.aiFilePath): | |||
logger.error("AI视频不存在!requestId:{}", self.msg.get("request_id")) | |||
raise ServiceException(ExceptionType.PUSH_STREAM_TIME_EXCEPTION.value[0], | |||
ExceptionType.PUSH_STREAM_TIME_EXCEPTION.value[1]) | |||
aliyunVodSdk = ThAliyunVodSdk(self.content, logger, self.msg.get("request_id")) | |||
upload_video_thread_ai = t.submit(aliyunVodSdk.get_play_url, self.aiFilePath, "aiOnLineVideo") | |||
ai_play_url = upload_video_thread_ai.result() | |||
aliyunVodSdk = ThAliyunVodSdk(self.content, self.msg.get("request_id")) | |||
upload_video_thread_ai = Common(self.content, aliyunVodSdk.get_play_url, self.aiFilePath, | |||
"ai_offLine_%s" % self.msg.get("request_id")) | |||
upload_video_thread_ai.setDaemon(True) | |||
upload_video_thread_ai.start() | |||
ai_play_url = upload_video_thread_ai.get_result() | |||
if ai_play_url is None: | |||
logger.error("原视频或AI视频播放上传VOD失败!, requestId: {}", self.msg.get("request_id")) | |||
raise ServiceException(ExceptionType.GET_VIDEO_URL_EXCEPTION.value[0], | |||
@@ -514,7 +530,7 @@ class OfflineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
task_frame = None | |||
with ThreadPoolExecutor(max_workers=10) as t: | |||
with ThreadPoolExecutor(max_workers=3) as tt: | |||
with ThreadPoolExecutor(max_workers=5) as ttt: | |||
with ThreadPoolExecutor(max_workers=10) as ttt: | |||
while True: | |||
if not pullProcess.is_alive(): | |||
logger.info("拉流进程停止异常, requestId: {}", self.msg.get("request_id")) | |||
@@ -541,8 +557,11 @@ class OfflineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
push_stream_result = tt.submit(cv2tool.push_stream, frame_merge) | |||
write_ai_video_result = tt.submit(cv2tool.video_ai_write, frame_merge) | |||
res = [push_stream_result, write_ai_video_result] | |||
for re in as_completed(res): | |||
re.result() | |||
completed_results = wait(res, timeout=600, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
det_xywh = frame_all.get("det_xywh") | |||
if len(det_xywh) > 0: | |||
self.imageQueue.put({"image": frame_all}) | |||
@@ -558,14 +577,14 @@ class OfflineIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
if status.get("status") == "1": | |||
raise ServiceException(status.get("error").get("code"), status.get("error").get("msg")) | |||
elif status.get("status") == "2": | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.SUCCESS.value, t) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.SUCCESS.value) | |||
break | |||
elif status.get("status") == "3": | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.TIMEOUT.value, t) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.TIMEOUT.value) | |||
break | |||
elif status.get("status") == "9": | |||
logger.info("离线任务正常结束:requestId: {}", self.msg.get("request_id")) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.SUCCESS.value, t) | |||
self.stop_task(cv2tool, pullProcess, AnalysisStatus.SUCCESS.value) | |||
break | |||
logger.info("离线进程任务完成,requestId:{}", self.msg.get("request_id")) | |||
except ServiceException as s: | |||
@@ -787,8 +806,11 @@ class PhotosIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
for imageUrl in imageUrls: | |||
obj = tt.submit(self.epidemic_prevention, imageUrl, mod, orc, model_type_code) | |||
obj_list.append(obj) | |||
for future in as_completed(obj_list): | |||
future.result() | |||
completed_results = wait(obj_list, timeout=120, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
def image_recognition(self, imageUrl, mod, model_type_code, aliyunOssSdk): | |||
try: | |||
@@ -865,8 +887,11 @@ class PhotosIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
for imageUrl in imageUrls: | |||
obj = tt.submit(self.image_recognition, imageUrl, mod, model_type_code, aliyunOssSdk) | |||
obj_list.append(obj) | |||
for future in as_completed(obj_list): | |||
future.result() | |||
completed_results = wait(obj_list, timeout=120, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
''' | |||
1. imageUrls: 图片url数组,多张图片 | |||
@@ -881,8 +906,11 @@ class PhotosIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
for imageUrl in imageUrls: | |||
obj = tt.submit(self.baidu_recognition, imageUrl, mod, model_type_code, aliyunOssSdk, ttt) | |||
obj_list.append(obj) | |||
for future in as_completed(obj_list): | |||
future.result() | |||
completed_results = wait(obj_list, timeout=120, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
def baidu_recognition(self, imageUrl, mod, model_type_code, aliyunOssSdk, ttt): | |||
try: | |||
@@ -906,8 +934,11 @@ class PhotosIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
reuslt = ttt.submit(self.baidu_method, mod, target, imageUrl, img, aliyunOssSdk, model_type_code, | |||
label_array, rainbows, person_label) | |||
obj_list.append(reuslt) | |||
for future in as_completed(obj_list): | |||
future.result() | |||
completed_results = wait(obj_list, timeout=120, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
except ServiceException as s: | |||
raise s | |||
except Exception as e: | |||
@@ -1034,14 +1065,14 @@ class PhotosIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
def run(self): | |||
with ThreadPoolExecutor(max_workers=5) as t: | |||
with ThreadPoolExecutor(max_workers=5) as tt: | |||
with ThreadPoolExecutor(max_workers=10) as tt: | |||
with ThreadPoolExecutor(max_workers=5) as ttt: | |||
try: | |||
# 初始化日志 | |||
LogUtils.init_log(self.content) | |||
model_array = self.get_model() | |||
imageUrls = self.msg.get("image_urls") | |||
aliyunOssSdk = AliyunOssSdk(self.content, logger, self.msg.get('request_id')) | |||
aliyunOssSdk = AliyunOssSdk(self.content, self.msg.get('request_id')) | |||
aliyunOssSdk.get_oss_bucket() | |||
task_list = [] | |||
for model in model_array: | |||
@@ -1065,8 +1096,11 @@ class PhotosIntelligentRecognitionProcess(IntelligentRecognitionProcess): | |||
aliyunOssSdk, tt) | |||
task_list.append(result) | |||
if len(task_list) > 0: | |||
for future in as_completed(task_list): | |||
future.result() | |||
completed_results = wait(task_list, timeout=120, return_when=ALL_COMPLETED) | |||
completed_futures = completed_results.done | |||
for r in completed_futures: | |||
if r.exception(): | |||
raise r.exception() | |||
logger.info("图片进程任务完成,requestId:{}", self.msg.get("request_id")) | |||
self.sendResult( | |||
{"feedback": message_feedback(self.msg.get("request_id"), AnalysisStatus.SUCCESS.value, | |||
@@ -1167,9 +1201,12 @@ class ScreenRecordingProcess(Process): | |||
logger.error("原视频不存在!requestId:{}", self.msg.get("request_id")) | |||
raise ServiceException(ExceptionType.OR_VIDEO_DO_NOT_EXEIST_EXCEPTION.value[0], | |||
ExceptionType.OR_VIDEO_DO_NOT_EXEIST_EXCEPTION.value[1]) | |||
aliyunVodSdk = ThAliyunVodSdk(self.content, logger, self.msg.get("request_id")) | |||
upload_video_thread_or = t.submit(aliyunVodSdk.get_play_url, self.orFilePath, "orOnLineVideo") | |||
or_play_url = upload_video_thread_or.result() | |||
aliyunVodSdk = ThAliyunVodSdk(self.content, self.msg.get("request_id")) | |||
upload_video_thread_or = Common(self.content, aliyunVodSdk.get_play_url, self.orFilePath, | |||
"or_recording_%s" % self.msg.get("request_id")) | |||
upload_video_thread_or.setDaemon(True) | |||
upload_video_thread_or.start() | |||
or_play_url = upload_video_thread_or.get_result() | |||
if or_play_url is None: | |||
logger.error("原视频上传VOD失败!原视频播放地址:{}, requestId: {}", or_play_url, self.msg.get("request_id")) | |||
raise ServiceException(ExceptionType.GET_VIDEO_URL_EXCEPTION.value[0], | |||
@@ -1244,7 +1281,7 @@ class ScreenRecordingProcess(Process): | |||
raise ServiceException(status.get("error").get("code"), status.get("error").get("msg")) | |||
elif status.get("status") == "2": | |||
cv2tool.close() | |||
self.stop_task(hb, RecordingStatus.RECORDING_SUCCESS.value[0], t) | |||
self.stop_task(hb, RecordingStatus.RECORDING_SUCCESS.value[0]) | |||
pullThread.join(10) | |||
break | |||
else: |
@@ -79,7 +79,7 @@ class BaiduSdkErrorEnum(Enum): | |||
IMAGE_SPLIT_LIMIT_REACHED = (282101, "image split limit reached", "长图片切分数量超限!", 1, 1) | |||
TARGET_DETECT_ERROR = (282102, "target detect error", "未检测到图片中识别目标!", 2, 1) # | |||
TARGET_DETECT_ERROR = (282102, "target detect error", "未检测到图片中识别目标!", 2, 1) | |||
TARGET_RECOGNIZE_ERROR = (282103, "target recognize error", "图片目标识别错误!", 2, 1) | |||
@@ -21,7 +21,7 @@ class ExceptionType(Enum): | |||
PUSH_STREAM_URL_EXCEPTION = ("SP007", "推流地址不能为空!") | |||
PUSH_STREAM_TIME_EXCEPTION = ("SP008", "推流时间或原视频时间太短, 未生成分析结果, 建议延长推流时间或原视频时间!") | |||
PUSH_STREAM_TIME_EXCEPTION = ("SP008", "未生成本地视频地址!") | |||
AI_MODEL_MATCH_EXCEPTION = ("SP009", "未匹配到对应的AI模型!") | |||
@@ -4,9 +4,9 @@ import traceback | |||
from aliyunsdkcore.client import AcsClient | |||
from aliyunsdkvod.request.v20170321 import CreateUploadVideoRequest | |||
from aliyunsdkvod.request.v20170321 import GetPlayInfoRequest | |||
from voduploadsdk.AliyunVodUtils import * | |||
from voduploadsdk.AliyunVodUploader import AliyunVodUploader | |||
from voduploadsdk.UploadVideoRequest import UploadVideoRequest | |||
from vodsdk.AliyunVodUtils import * | |||
from vodsdk.AliyunVodUploader import AliyunVodUploader | |||
from vodsdk.UploadVideoRequest import UploadVideoRequest | |||
# # # 填入AccessKey信息 | |||
def init_vod_client(accessKeyId, accessKeySecret): |
@@ -10,9 +10,9 @@ from alibabacloud_darabonba_env.client import Client as EnvClient | |||
from alibabacloud_vod20170321 import models as vod_20170321_models | |||
from alibabacloud_tea_console.client import Client as ConsoleClient | |||
from alibabacloud_tea_util.client import Client as UtilClient | |||
from voduploadsdk.AliyunVodUtils import * | |||
from voduploadsdk.AliyunVodUploader import AliyunVodUploader | |||
from voduploadsdk.UploadVideoRequest import UploadVideoRequest | |||
from vodsdk.AliyunVodUtils import * | |||
from vodsdk.AliyunVodUploader import AliyunVodUploader | |||
from vodsdk.UploadVideoRequest import UploadVideoRequest | |||
class Sample: | |||
def __init__(self): | |||
@@ -133,9 +133,9 @@ import traceback | |||
from aliyunsdkcore.client import AcsClient | |||
from aliyunsdkvod.request.v20170321 import CreateUploadVideoRequest | |||
from aliyunsdkvod.request.v20170321 import GetPlayInfoRequest | |||
from voduploadsdk.AliyunVodUtils import * | |||
from voduploadsdk.AliyunVodUploader import AliyunVodUploader | |||
from voduploadsdk.UploadVideoRequest import UploadVideoRequest | |||
from vodsdk.AliyunVodUtils import * | |||
from vodsdk.AliyunVodUploader import AliyunVodUploader | |||
from vodsdk.UploadVideoRequest import UploadVideoRequest | |||
# 获取播放地址 | |||
def init_vod_client(accessKeyId, accessKeySecret): | |||
regionId = 'cn-shanghai' # 点播服务接入地域 |
@@ -6,9 +6,9 @@ import json | |||
from aliyunsdkcore.client import AcsClient | |||
from aliyunsdkvod.request.v20170321 import GetPlayInfoRequest | |||
from voduploadsdk.AliyunVodUtils import * | |||
from voduploadsdk.AliyunVodUploader import AliyunVodUploader | |||
from voduploadsdk.UploadVideoRequest import UploadVideoRequest | |||
from vodsdk.AliyunVodUtils import * | |||
from vodsdk.AliyunVodUploader import AliyunVodUploader | |||
from vodsdk.UploadVideoRequest import UploadVideoRequest | |||
''' | |||
视频上传使用vod |
@@ -3,9 +3,9 @@ import traceback | |||
from aliyunsdkcore.client import AcsClient | |||
from aliyunsdkvod.request.v20170321 import CreateUploadVideoRequest | |||
from aliyunsdkvod.request.v20170321 import GetPlayInfoRequest | |||
from voduploadsdk.AliyunVodUtils import * | |||
from voduploadsdk.AliyunVodUploader import AliyunVodUploader | |||
from voduploadsdk.UploadVideoRequest import UploadVideoRequest | |||
from vodsdk.AliyunVodUtils import * | |||
from vodsdk.AliyunVodUploader import AliyunVodUploader | |||
from vodsdk.UploadVideoRequest import UploadVideoRequest | |||
# 获取播放地址 | |||
def init_vod_client(accessKeyId, accessKeySecret): | |||
regionId = 'cn-shanghai' # 点播服务接入地域 |
@@ -1,21 +1,23 @@ | |||
from loguru import logger | |||
import pickle | |||
from loguru import logger | |||
# 定义一个类 | |||
class Person: | |||
def __init__(self, name, age): | |||
self.name = name | |||
self.age = age | |||
# 创建一个Person实例 | |||
person = Person("Alice", 25) | |||
# 使用Loguru的serialize方法将Person实例序列化为字节字符串 | |||
serialized_person = logger.serialize(person) | |||
print(serialized_person) | |||
# 使用pickle库将字节字符串反序列化为Python对象 | |||
deserialized_person = pickle.loads(serialized_person) | |||
# 输出反序列化后的对象属性 | |||
print(deserialized_person.name) # Alice | |||
print(deserialized_person.age) # 25 | |||
# class Person: | |||
# def __init__(self, name, age): | |||
# self.name = name | |||
# self.age = age | |||
# | |||
# # 创建一个Person实例 | |||
# person = Person("Alice", 25) | |||
# | |||
# # 使用Loguru的serialize方法将Person实例序列化为字节字符串 | |||
# serialized_person = logger.serialize(person) | |||
# print(serialized_person) | |||
# # 使用pickle库将字节字符串反序列化为Python对象 | |||
# deserialized_person = pickle.loads(serialized_person) | |||
# | |||
# # 输出反序列化后的对象属性 | |||
# print(deserialized_person.name) # Alice | |||
# print(deserialized_person.age) # 25 | |||
aa = {"name": "11111"} | |||
logger.info(aa) |
@@ -1,7 +1,7 @@ | |||
# -*- coding: utf-8 -*- | |||
import threading | |||
import time | |||
from concurrent.futures import ThreadPoolExecutor | |||
from concurrent.futures import ThreadPoolExecutor, ALL_COMPLETED, wait | |||
class Test(object): | |||
@@ -24,21 +24,21 @@ class Test(object): | |||
def bb(): | |||
print("!1111111111") | |||
def aa(t): | |||
while True: | |||
t.submit(bb) | |||
def aa(aa): | |||
print(aa) | |||
time.sleep(5) | |||
return "1111" | |||
# test = Test() | |||
# test.process() | |||
# print(3//2) | |||
# with ThreadPoolExecutor(max_workers=10) as t: | |||
# t.submit(aa, t) | |||
# time.sleep(1000) | |||
# codeArray=[''] | |||
# codeStr = ','.join(codeArray) | |||
# print(codeStr) | |||
aa={'aaaa': []} | |||
aa["aaaa"].append("1111111") | |||
aa["aaaa"].append("1111111") | |||
aa["aaaa"].append("1111111") | |||
print(aa) | |||
with ThreadPoolExecutor(max_workers=10) as t: | |||
aa = t.submit(aa, "aaa") | |||
results = wait([aa], timeout=60, return_when=ALL_COMPLETED) | |||
completed_futures = results.done | |||
for f in completed_futures: | |||
if f.exception(): | |||
raise f.exception() | |||
else: | |||
print(f"Task {f.result()} succeeded") |
@@ -1,31 +1,32 @@ | |||
# -*- coding: utf-8 -*- | |||
import oss2 | |||
import time | |||
from aliyunsdkvod.request.v20170321.GetPlayInfoRequest import GetPlayInfoRequest | |||
from loguru import logger | |||
from common import YmlConstant | |||
from exception.CustomerException import ServiceException | |||
from enums.ExceptionEnum import ExceptionType | |||
import json | |||
from aliyunsdkcore.client import AcsClient | |||
from aliyunsdkvod.request.v20170321 import GetPlayInfoRequest | |||
from voduploadsdk.AliyunVodUtils import * | |||
from voduploadsdk.AliyunVodUploader import AliyunVodUploader | |||
from voduploadsdk.UploadVideoRequest import UploadVideoRequest | |||
from util import LogUtils | |||
from vodsdk.AliyunVodUploader import AliyunVodUploader | |||
from vodsdk.UploadVideoRequest import UploadVideoRequest | |||
class AliyunOssSdk: | |||
def __init__(self, context, log, requestId): | |||
def __init__(self, context, requestId): | |||
LogUtils.init_log(context) | |||
self.__context = context | |||
self.bucket = None | |||
self.__logger = log | |||
self.__requestId = requestId | |||
def get_oss_bucket(self): | |||
if self.bucket is None: | |||
self.__logger.info("初始化oss桶, requestId:{}", self.__requestId) | |||
logger.info("初始化oss桶, requestId:{}", self.__requestId) | |||
auth = oss2.Auth(YmlConstant.get_aliyun_access_key(self.__context), | |||
YmlConstant.get_aliyun_access_secret(self.__context)) | |||
self.bucket = oss2.Bucket(auth, YmlConstant.get_aliyun_oss_endpoint(self.__context), | |||
@@ -33,29 +34,29 @@ class AliyunOssSdk: | |||
connect_timeout=YmlConstant.get_aliyun_oss_connect_timeout(self.__context)) | |||
def sync_upload_file(self, updatePath, fileByte): | |||
self.__logger.info("开始上传文件到oss, requestId:{}", self.__requestId) | |||
logger.info("开始上传文件到oss, requestId:{}", self.__requestId) | |||
self.get_oss_bucket() | |||
MAX_RETRIES = 3 | |||
retry_count = 0 | |||
while True: | |||
try: | |||
self.bucket.put_object(updatePath, fileByte) | |||
self.__logger.info("上传文件到oss成功! requestId:{}", self.__requestId) | |||
logger.info("上传文件到oss成功! requestId:{}", self.__requestId) | |||
break | |||
except Exception as e: | |||
retry_count += 1 | |||
time.sleep(1) | |||
self.__logger.info("上传文件到oss失败, 重试次数:{}, requestId:{}", retry_count, self.__requestId) | |||
logger.info("上传文件到oss失败, 重试次数:{}, requestId:{}", retry_count, self.__requestId) | |||
if retry_count > MAX_RETRIES: | |||
self.__logger.exception("上传文件到oss重试失败:{}, requestId:{}", e, self.__requestId) | |||
logger.exception("上传文件到oss重试失败:{}, requestId:{}", e, self.__requestId) | |||
raise e | |||
class ThAliyunVodSdk: | |||
def __init__(self, context, log, requestId): | |||
def __init__(self, context, requestId): | |||
LogUtils.init_log(context) | |||
self.__context = context | |||
self.__logger = log | |||
self.__requestId = requestId | |||
def init_vod_client(self, accessKeyId, accessKeySecret): | |||
@@ -63,7 +64,7 @@ class ThAliyunVodSdk: | |||
return AcsClient(accessKeyId, accessKeySecret, regionId, auto_retry=True, max_retry_time=3, timeout=30) | |||
def get_play_info(self, videoId): | |||
self.__logger.info("开始获取视频地址,videoId:{}, requestId:{}", videoId, self.__requestId) | |||
logger.info("开始获取视频地址,videoId:{}, requestId:{}", videoId, self.__requestId) | |||
start = time.time() | |||
while True: | |||
try: | |||
@@ -75,33 +76,33 @@ class ThAliyunVodSdk: | |||
request.set_AuthTimeout(3600 * 5) | |||
response = json.loads(clt.do_action_with_exception(request)) | |||
play_url = response["PlayInfoList"]["PlayInfo"][0]["PlayURL"] | |||
self.__logger.info("获取视频地址成功,视频地址: {}, requestId: {}", play_url, self.__requestId) | |||
logger.info("获取视频地址成功,视频地址: {}, requestId: {}", play_url, self.__requestId) | |||
return play_url | |||
except Exception as e: | |||
self.__logger.error("获取视频地址失败,5秒后重试, requestId: {}", self.__requestId) | |||
logger.info("获取视频地址失败,5秒后重试, requestId: {}", self.__requestId) | |||
time.sleep(5) | |||
current_time = time.time() | |||
if "HTTP Status: 403" not in str(e): | |||
self.__logger.exception("获取视频地址失败: {}, requestId: {}", e, self.__requestId) | |||
logger.error("获取视频地址失败: {}, requestId: {}", str(e), self.__requestId) | |||
raise ServiceException(ExceptionType.GET_VIDEO_URL_EXCEPTION.value[0], | |||
ExceptionType.GET_VIDEO_URL_EXCEPTION.value[1]) | |||
if "HTTP Status: 403" in str(e) and ("UploadFail" in str(e) or "TranscodeFail" in str(e)): | |||
self.__logger.exception("获取视频地址失败: {}, requestId: {}", e, self.__requestId) | |||
logger.error("获取视频地址失败: {}, requestId: {}", str(e), self.__requestId) | |||
raise ServiceException(ExceptionType.GET_VIDEO_URL_EXCEPTION.value[0], | |||
ExceptionType.GET_VIDEO_URL_EXCEPTION.value[1]) | |||
diff_time = current_time - start | |||
if diff_time > 60 * 60 * 2: | |||
self.__logger.exception("获取视频地址失败超时异常: {},超时时间:{}, requestId: {}", e, diff_time, | |||
if diff_time > 60 * 60 * 5: | |||
logger.error("获取视频地址失败超时异常: {},超时时间:{}, requestId: {}", str(e), diff_time, | |||
self.__requestId) | |||
raise ServiceException(ExceptionType.GET_VIDEO_URL_TIMEOUT_EXCEPTION.value[0], | |||
ExceptionType.GET_VIDEO_URL_TIMEOUT_EXCEPTION.value[1]) | |||
def upload_local_video(self, filePath, file_title): | |||
self.__logger.info("开始执行vod视频上传, filePath: {}, requestId: {}", filePath, self.__requestId) | |||
logger.info("开始执行vod视频上传, filePath: {}, requestId: {}", filePath, self.__requestId) | |||
uploader = AliyunVodUploader(YmlConstant.get_aliyun_access_key(self.__context), | |||
YmlConstant.get_aliyun_access_secret(self.__context)) | |||
YmlConstant.get_aliyun_access_secret(self.__context), self.__requestId) | |||
uploadVideoRequest: UploadVideoRequest = UploadVideoRequest(filePath, file_title) | |||
self.__logger.info("视频分类:{}", YmlConstant.get_aliyun_vod_cateId(self.__context)) | |||
logger.info("视频分类:{}", YmlConstant.get_aliyun_vod_cateId(self.__context)) | |||
uploadVideoRequest.setCateId(YmlConstant.get_aliyun_vod_cateId(self.__context)) | |||
# 可以设置视频封面,如果是本地或网络图片可使用UploadImageRequest上传图片到视频点播,获取到ImageURL | |||
# ImageURL示例:https://example.com/sample-****.jpg | |||
@@ -113,14 +114,13 @@ class ThAliyunVodSdk: | |||
while True: | |||
try: | |||
result = uploader.uploadLocalVideo(uploadVideoRequest) | |||
self.__logger.info("vod视频上传成功, videoId:{}, requestId:{}", result.get("VideoId"), self.__requestId) | |||
logger.info("vod视频上传成功, videoId:{}, requestId:{}", result.get("VideoId"), self.__requestId) | |||
return result.get("VideoId") | |||
except AliyunVodException as e: | |||
except Exception as e: | |||
retry_count += 1 | |||
time.sleep(3) | |||
self.__logger.error("vod视频上传失败,重试次数:{}, requestId:{}", retry_count, self.__requestId) | |||
time.sleep(1) | |||
logger.error("vod视频上传失败:{},重试次数:{}, requestId:{}", str(e), retry_count, self.__requestId) | |||
if retry_count >= MAX_RETRIES: | |||
self.__logger.exception("vod视频上传重试失败: {}, requestId:{}", e.message, self.__requestId) | |||
raise ServiceException(ExceptionType.SERVICE_INNER_EXCEPTION.value[0], | |||
ExceptionType.SERVICE_INNER_EXCEPTION.value[1]) | |||
@@ -129,3 +129,21 @@ class ThAliyunVodSdk: | |||
if videoId is None or len(videoId) == 0: | |||
return None | |||
return self.get_play_info(videoId) | |||
# if __name__ == "__main__": | |||
# with open('/home/th/tuo_heng/prod/tuoheng_alg/dsp_application.yml', 'r', encoding='"utf-8"') as f: | |||
# file_content = f.read() | |||
# context = yaml.load(file_content, yaml.FullLoader) | |||
# aliyunVodSdk = ThAliyunVodSdk(context, logger, "11111111111") | |||
# aliyunVodSdk = ThAliyunVodSdk(context, logger, "11111111111") | |||
# | |||
# upload_video_thread_or = Common(context, aliyunVodSdk.get_play_url, '/home/th/tuo_heng/prod/dsp/video1/20230510185733460569_on_ai_592c3dd7eb404af9a744c5543e0e006a.mp4', "orOnLineVideo") | |||
# upload_video_thread_ai = Common(context, aliyunVodSdk.get_play_url, '/home/th/tuo_heng/prod/dsp/video1/20230510185733460569_on_or_592c3dd7eb404af9a744c5543e0e006a.mp4', "aiOnLineVideo") | |||
# upload_video_thread_or.setDaemon(True) | |||
# upload_video_thread_ai.setDaemon(True) | |||
# upload_video_thread_or.start() | |||
# upload_video_thread_ai.start() | |||
# or_url = upload_video_thread_or.get_result() | |||
# ai_url = upload_video_thread_ai.get_result() | |||
# print(or_url) | |||
# print(ai_url) |
@@ -402,7 +402,7 @@ class Cv2Util(): | |||
'-r', str(self.fps), | |||
'-i', '-', # 指定输入文件 | |||
'-g', str(self.fps), | |||
'-maxrate', '8000k', | |||
'-maxrate', '6000k', | |||
# '-profile:v', 'high', | |||
'-b:v', '5000k', | |||
# '-crf', '18', | |||
@@ -474,80 +474,93 @@ class Cv2Util(): | |||
if self.orFilePath is not None and self.or_video_file is None: | |||
self.or_video_file = cv2.VideoWriter(self.orFilePath, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, | |||
(self.w, self.h)) | |||
# self.or_video_file.set(cv2.CAP_PROP_BITRATE, 5000) | |||
if self.or_video_file is None: | |||
self.__logger.error("or_video_file为空, requestId:{}", self.requestId) | |||
raise ServiceException(ExceptionType.SERVICE_INNER_EXCEPTION.value[0], | |||
ExceptionType.SERVICE_INNER_EXCEPTION.value[1]) | |||
except ServiceException as s: | |||
if self.or_video_file: | |||
self.or_video_file.release() | |||
self.or_video_file = None | |||
self.__logger.exception("构建OR文件写对象异常: {}, requestId:{}", s.msg, self.requestId) | |||
raise s | |||
except Exception as e: | |||
if self.or_video_file: | |||
self.or_video_file.release() | |||
self.or_video_file = None | |||
self.__logger.exception("构建OR文件写对象异常: {}, requestId:{}", e, self.requestId) | |||
raise e | |||
except: | |||
if self.or_video_file: | |||
self.or_video_file.release() | |||
self.or_video_file = None | |||
self.__logger.exception("构建OR文件写对象异常, requestId:{}", self.requestId) | |||
raise Exception("构建OR文件写对象异常") | |||
def build_ai_write(self): | |||
try: | |||
if self.aiFilePath is not None and self.ai_video_file is None: | |||
self.ai_video_file = cv2.VideoWriter(self.aiFilePath, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, | |||
(self.w * 2, self.h)) | |||
# self.ai_video_file.set(cv2.CAP_PROP_BITRATE, 5000) | |||
if self.ai_video_file is None: | |||
self.__logger.error("ai_video_file为空, requestId:{}", self.requestId) | |||
raise ServiceException(ExceptionType.SERVICE_INNER_EXCEPTION.value[0], | |||
ExceptionType.SERVICE_INNER_EXCEPTION.value[1]) | |||
except ServiceException as s: | |||
if self.ai_video_file: | |||
self.ai_video_file.release() | |||
self.ai_video_file = None | |||
self.__logger.exception("构建AI文件写对象异常: {}, requestId:{}", s.msg, self.requestId) | |||
raise s | |||
except Exception as e: | |||
if self.ai_video_file: | |||
self.ai_video_file.release() | |||
self.ai_video_file = None | |||
self.__logger.exception("构建AI文件写对象异常: {}, requestId:{}", e, self.requestId) | |||
raise e | |||
except: | |||
if self.ai_video_file: | |||
self.ai_video_file.release() | |||
self.ai_video_file = None | |||
self.__logger.exception("构建AI文件写对象异常, requestId:{}", self.requestId) | |||
raise Exception("构建AI文件写对象异常") | |||
def video_or_write(self, frame): | |||
try: | |||
if self.or_video_file is None: | |||
self.build_or_write() | |||
self.or_video_file.write(frame) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ex: | |||
ai_retry_num = 0 | |||
while True: | |||
try: | |||
ai_retry_num += 1 | |||
if ai_retry_num > 3: | |||
self.__logger.exception("重新写入原视频视频到本地,重试失败, requestId: {}", self.requestId) | |||
raise ServiceException(ExceptionType.SERVICE_INNER_EXCEPTION.value[0], | |||
ExceptionType.SERVICE_INNER_EXCEPTION.value[1]) | |||
self.or_video_file.write(frame) | |||
self.__logger.info("重新写入原视频视到本地, 当前重试次数: {}, requestId: {}", ai_retry_num, | |||
self.requestId) | |||
break | |||
except Exception as e: | |||
self.__logger.exception("重新写入原视频视到本地:{}, 开始重试, 当前重试次数:{}, requestId: {}", e, | |||
ai_retry_num, self.requestId) | |||
ai_retry_num = 0 | |||
while True: | |||
try: | |||
if self.or_video_file is None: | |||
self.build_or_write() | |||
self.or_video_file.write(frame) | |||
break | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ex: | |||
if ai_retry_num > 3: | |||
self.__logger.exception("重新写入原视频视频到本地, 重试失败, requestId: {}", self.requestId) | |||
raise ex | |||
finally: | |||
ai_retry_num += 1 | |||
def video_ai_write(self, frame): | |||
try: | |||
if self.ai_video_file is None: | |||
self.build_ai_write() | |||
self.ai_video_file.write(frame) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ex: | |||
ai_retry_num = 0 | |||
while True: | |||
try: | |||
ai_retry_num += 1 | |||
if ai_retry_num > 3: | |||
self.__logger.exception("重新写入分析后的视频到本地,重试失败, requestId: {}", self.requestId) | |||
raise ServiceException(ExceptionType.SERVICE_INNER_EXCEPTION.value[0], | |||
ExceptionType.SERVICE_INNER_EXCEPTION.value[1]) | |||
self.ai_video_file.write(frame) | |||
self.__logger.info("重新写入分析后的视频到本地, 当前重试次数: {}, requestId: {}", ai_retry_num, | |||
self.requestId) | |||
break | |||
except Exception as e: | |||
self.__logger.exception("重新写入分析后的视频到本地:{}, 开始重试, 当前重试次数:{}, requestId: {}", e, | |||
ai_retry_num, self.requestId) | |||
ai_retry_num = 0 | |||
while True: | |||
try: | |||
if self.ai_video_file is None: | |||
self.build_ai_write() | |||
self.ai_video_file.write(frame) | |||
break | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ex: | |||
if ai_retry_num > 3: | |||
self.__logger.exception("重新写入分析后的视频到本地,重试失败, requestId: {}", self.requestId) | |||
raise ex | |||
finally: | |||
ai_retry_num += 1 | |||
def video_merge(self, frame1, frame2): | |||
# frameLeft = cv2.resize(frame1, (int(self.width / 2), int(self.height / 2)), interpolation=cv2.INTER_LINEAR) |
@@ -320,6 +320,8 @@ class RiverModel: | |||
return AI_process([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, objectPar=self.objectPar, font=self.digitFont, segPar=self.segPar, | |||
mode=self.mode, postPar=copy.deepcopy(self.postPar)) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -466,6 +468,8 @@ class HighWayModel: | |||
self.rainbows, objectPar=copy.deepcopy(self.objectPar), font=self.digitFont, | |||
segPar=copy.deepcopy(self.segPar), | |||
mode=self.mode, postPar=copy.deepcopy(self.postPar)) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
@@ -570,6 +574,8 @@ class ForestModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -671,6 +677,8 @@ class VehicleModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -774,6 +782,8 @@ class PedestrianModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -876,6 +886,8 @@ class SmogfireModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -979,6 +991,8 @@ class AnglerSwimmerModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -1083,6 +1097,8 @@ class CountryRoadModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -1187,6 +1203,8 @@ class ChannelEmergencyModel: | |||
return AI_process_forest([frame], self.model, self.segmodel, self.names, self.label_arraylist, | |||
self.rainbows, self.half, self.device, self.conf_thres, self.iou_thres, | |||
[], font=self.digitFont, trtFlag_det=self.trtFlag_det) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -1269,6 +1287,8 @@ class ShipModel: | |||
fontpath=self.fontPath) | |||
self.label_arraylist = self.par["label_array"] | |||
return OBB_infer(self.model, frame, self.par) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
# self.num += 1 | |||
# cv2.imwrite('/home/th/tuo_heng/dev/img%s.jpg' % str(self.num), frame) | |||
@@ -1318,6 +1338,8 @@ class IMModel: | |||
iou_thres=self.par['iou_thres'], nc=self.par[self.img_type]['nc']) # 后处理 | |||
dataBack = get_return_data(frame, boxes, modelType=self.img_type, plate_dilate=self.par['plate_dilate']) | |||
return dataBack | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
self.logger.exception("算法模型分析异常:{}, requestId:{}", ee, self.requestId) | |||
raise ServiceException(ExceptionType.MODEL_ANALYSE_EXCEPTION.value[0], | |||
@@ -1380,6 +1402,8 @@ class OCR_Model: | |||
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |||
par = [gray_frame, self.engine, self.context, self.converter, self.AlignCollate_normal, self.device] | |||
return ocr_process(par) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
self.__logger.exception("ocr坐标识别异常:{}, requestId:{}", ee, self.requestId) | |||
raise ServiceException(ExceptionType.COORDINATE_ACQUISITION_FAILED.value[0], | |||
@@ -1416,6 +1440,8 @@ class BaiduAiImageModel: | |||
+ " target: " + target) | |||
return baiduEnum.value[2](self.__aipImageClassifyClient, self.__aipBodyAnalysisClient, url, | |||
self.__requestId) | |||
except ServiceException as s: | |||
raise s | |||
except Exception as ee: | |||
self.logger.exception("算法模型分析异常:{}, requestId:{}", ee, self.requestId) | |||
raise ServiceException(ExceptionType.MODEL_ANALYSE_EXCEPTION.value[0], |
@@ -0,0 +1,730 @@ | |||
# -*- coding: UTF-8 -*- | |||
import json | |||
import oss2 | |||
import base64 | |||
import time | |||
from aliyunsdkcore import client | |||
from aliyunsdkvod.request.v20170321 import CreateUploadVideoRequest | |||
from aliyunsdkvod.request.v20170321 import RefreshUploadVideoRequest | |||
from aliyunsdkvod.request.v20170321 import CreateUploadImageRequest | |||
from aliyunsdkvod.request.v20170321 import CreateUploadAttachedMediaRequest | |||
from vodsdk.AliyunVodUtils import * | |||
from loguru import logger | |||
VOD_MAX_TITLE_LENGTH = 128 | |||
VOD_MAX_DESCRIPTION_LENGTH = 1024 | |||
class AliyunVodUploader: | |||
def __init__(self, accessKeyId, accessKeySecret, requestId, ecsRegionId=None): | |||
""" | |||
constructor for VodUpload | |||
:param accessKeyId: string, access key id | |||
:param accessKeySecret: string, access key secret | |||
:param ecsRegion: string, 部署迁移脚本的ECS所在的Region,详细参考:https://help.aliyun.com/document_detail/40654.html,如:cn-beijing | |||
:return | |||
""" | |||
self.__requestId = requestId | |||
# LogUtils.init_log(context) | |||
self.__accessKeyId = accessKeyId | |||
self.__accessKeySecret = accessKeySecret | |||
self.__ecsRegion = ecsRegionId | |||
self.__vodApiRegion = None | |||
self.__connTimeout = 60 | |||
self.__bucketClient = None | |||
self.__maxRetryTimes = 5 | |||
self.__vodClient = None | |||
self.__EnableCrc = True | |||
# 分片上传参数 | |||
self.__multipartThreshold = 10 * 1024 * 1024 # 分片上传的阈值,超过此值开启分片上传 | |||
self.__multipartPartSize = 10 * 1024 * 1024 # 分片大小,单位byte | |||
self.__multipartThreadsNum = 3 # 分片上传时并行上传的线程数,暂时为串行上传,不支持并行,后续会支持。 | |||
# 设置apiRegion为cn-shanghai, 初始化客户端self.__vodClient | |||
self.setApiRegion('cn-shanghai') | |||
logger.info("初始化阿里云视频上传sdk,连接超时时间:{}, 重试次数:{}, requestId:{}", self.__connTimeout, | |||
self.__maxRetryTimes, requestId) | |||
def setApiRegion(self, apiRegion): | |||
""" | |||
设置VoD的接入地址,中国大陆为cn-shanghai,海外支持ap-southeast-1(新加坡)等区域,详情参考:https://help.aliyun.com/document_detail/98194.html | |||
:param apiRegion: 接入地址的Region英文表示 | |||
:return: | |||
""" | |||
self.__vodApiRegion = apiRegion | |||
self.__vodClient = self.__initVodClient() | |||
def __initVodClient(self): | |||
return client.AcsClient(self.__accessKeyId, self.__accessKeySecret, self.__vodApiRegion, | |||
auto_retry=True, max_retry_time=self.__maxRetryTimes, timeout=self.__connTimeout) | |||
def setMultipartUpload(self, multipartThreshold=10 * 1024 * 1024, multipartPartSize=10 * 1024 * 1024, | |||
multipartThreadsNum=1): | |||
if multipartThreshold > 0: | |||
self.__multipartThreshold = multipartThreshold | |||
if multipartPartSize > 0: | |||
self.__multipartPartSize = multipartPartSize | |||
if multipartThreadsNum > 0: | |||
self.__multipartThreadsNum = multipartThreadsNum | |||
def setEnableCrc(self, isEnable=False): | |||
self.__EnableCrc = True if isEnable else False | |||
@catch_error | |||
def uploadLocalVideo(self, uploadVideoRequest, startUploadCallback=None): | |||
""" | |||
上传本地视频或音频文件到点播,最大支持48.8TB的单个文件,暂不支持断点续传 | |||
:param uploadVideoRequest: UploadVideoRequest类的实例,注意filePath为本地文件的绝对路径 | |||
:param startUploadCallback为获取到上传地址和凭证(uploadInfo)后开始进行文件上传时的回调,可用于记录上传日志等;uploadId为设置的上传ID,可用于关联导入视频。 | |||
:return | |||
""" | |||
uploadInfo = self.__createUploadVideo(uploadVideoRequest) | |||
if startUploadCallback: | |||
startUploadCallback(uploadVideoRequest.uploadId, uploadInfo) | |||
headers = self.__getUploadHeaders(uploadVideoRequest) | |||
self.__uploadOssObjectWithRetry(uploadVideoRequest.filePath, uploadInfo['UploadAddress']['FileName'], | |||
uploadInfo, headers) | |||
return uploadInfo | |||
@catch_error | |||
def uploadWebVideo(self, uploadVideoRequest, startUploadCallback=None): | |||
""" | |||
上传网络视频或音频文件到点播,最大支持48.8TB的单个文件(需本地磁盘空间足够);会先下载到本地临时目录,再上传到点播存储 | |||
:param uploadVideoRequest: UploadVideoRequest类的实例,注意filePath为网络文件的URL地址 | |||
:return | |||
""" | |||
# 下载文件 | |||
uploadVideoRequest = self.__downloadWebMedia(uploadVideoRequest) | |||
# 上传到点播 | |||
uploadInfo = self.__createUploadVideo(uploadVideoRequest) | |||
if startUploadCallback: | |||
startUploadCallback(uploadVideoRequest.uploadId, uploadInfo) | |||
headers = self.__getUploadHeaders(uploadVideoRequest) | |||
self.__uploadOssObjectWithRetry(uploadVideoRequest.filePath, uploadInfo['UploadAddress']['FileName'], | |||
uploadInfo, headers) | |||
# 删除本地临时文件 | |||
os.remove(uploadVideoRequest.filePath) | |||
return uploadInfo['VideoId'] | |||
@catch_error | |||
def uploadLocalM3u8(self, uploadVideoRequest, sliceFilePaths=None): | |||
""" | |||
上传本地m3u8视频或音频文件到点播,m3u8文件和分片文件默认在同一目录 | |||
:param uploadVideoRequest: UploadVideoRequest类的实例,注意filePath为本地m3u8索引文件的绝对路径, | |||
且m3u8文件的分片信息必须是相对地址,不能含有URL或本地绝对路径 | |||
:param sliceFilePaths: list, 分片文件的本地路径列表,例如:['/opt/m3u8_video/sample_001.ts', '/opt/m3u8_video/sample_002.ts'] | |||
sliceFilePaths为None时,会按照同一目录去解析分片地址;如不在同一目录等原因导致解析有误,可自行组装分片地址 | |||
:return | |||
""" | |||
if sliceFilePaths is None: | |||
sliceFilePaths = self.parseLocalM3u8(uploadVideoRequest.filePath) | |||
if (not isinstance(sliceFilePaths, list)) or len(sliceFilePaths) <= 0: | |||
raise AliyunVodException('InvalidM3u8SliceFile', 'M3u8 slice files invalid', | |||
'sliceFilePaths invalid or m3u8 index file error') | |||
# 上传到点播的m3u8索引文件会重写,以此确保分片地址都为相对地址 | |||
downloader = AliyunVodDownloader() | |||
m3u8LocalDir = downloader.getSaveLocalDir() + '/' + AliyunVodUtils.getStringMd5(uploadVideoRequest.fileName) | |||
downloader.setSaveLocalDir(m3u8LocalDir) | |||
m3u8LocalPath = m3u8LocalDir + '/' + os.path.basename(uploadVideoRequest.fileName) | |||
self.__rewriteM3u8File(uploadVideoRequest.filePath, m3u8LocalPath, True) | |||
# 获取上传凭证 | |||
uploadVideoRequest.setFilePath(m3u8LocalPath) | |||
uploadInfo = self.__createUploadVideo(uploadVideoRequest) | |||
uploadAddress = uploadInfo['UploadAddress'] | |||
headers = self.__getUploadHeaders(uploadVideoRequest) | |||
# 依次上传分片文件 | |||
for sliceFilePath in sliceFilePaths: | |||
tempFilePath, sliceFileName = AliyunVodUtils.getFileBriefPath(sliceFilePath) | |||
self.__uploadOssObjectWithRetry(sliceFilePath, uploadAddress['ObjectPrefix'] + sliceFileName, uploadInfo, | |||
headers) | |||
# 上传m3u8文件 | |||
self.__uploadOssObjectWithRetry(m3u8LocalPath, uploadAddress['FileName'], uploadInfo, headers) | |||
# 删除重写到本地的m3u8文件 | |||
if os.path.exists(m3u8LocalPath): | |||
os.remove(m3u8LocalPath) | |||
if not os.listdir(m3u8LocalDir): | |||
os.rmdir(m3u8LocalDir) | |||
return uploadInfo['VideoId'] | |||
@catch_error | |||
def uploadWebM3u8(self, uploadVideoRequest, sliceFileUrls=None): | |||
""" | |||
上传网络m3u8视频或音频文件到点播,需本地磁盘空间足够,会先下载到本地临时目录,再上传到点播存储 | |||
:param uploadVideoRequest: UploadVideoRequest类的实例,注意filePath为m3u8网络文件的URL地址 | |||
:param sliceFileUrls: list, 分片文件的url,例如:['http://host/sample_001.ts', 'http://host/sample_002.ts'] | |||
sliceFileUrls为None时,会按照同一前缀解析分片地址;如分片路径和m3u8索引文件前缀不同等原因导致解析有误,可自行组装分片地址 | |||
:return | |||
""" | |||
if sliceFileUrls is None: | |||
sliceFileUrls = self.parseWebM3u8(uploadVideoRequest.filePath) | |||
if (not isinstance(sliceFileUrls, list)) or len(sliceFileUrls) <= 0: | |||
raise AliyunVodException('InvalidM3u8SliceFile', 'M3u8 slice urls invalid', | |||
'sliceFileUrls invalid or m3u8 index file error') | |||
# 下载m3u8文件和所有ts分片文件到本地;上传到点播的m3u8索引文件会重写,以此确保分片地址都为相对地址 | |||
downloader = AliyunVodDownloader() | |||
m3u8LocalDir = downloader.getSaveLocalDir() + '/' + AliyunVodUtils.getStringMd5(uploadVideoRequest.fileName) | |||
downloader.setSaveLocalDir(m3u8LocalDir) | |||
m3u8LocalPath = m3u8LocalDir + '/' + os.path.basename(uploadVideoRequest.fileName) | |||
self.__rewriteM3u8File(uploadVideoRequest.filePath, m3u8LocalPath, False) | |||
sliceList = [] | |||
for sliceFileUrl in sliceFileUrls: | |||
tempFilePath, sliceFileName = AliyunVodUtils.getFileBriefPath(sliceFileUrl) | |||
err, sliceLocalPath = downloader.downloadFile(sliceFileUrl, sliceFileName) | |||
if sliceLocalPath is None: | |||
raise AliyunVodException('FileDownloadError', 'Download M3u8 File Error', '') | |||
sliceList.append((sliceLocalPath, sliceFileName)) | |||
# 获取上传凭证 | |||
uploadVideoRequest.setFilePath(m3u8LocalPath) | |||
uploadInfo = self.__createUploadVideo(uploadVideoRequest) | |||
uploadAddress = uploadInfo['UploadAddress'] | |||
headers = self.__getUploadHeaders(uploadVideoRequest) | |||
# 依次上传分片文件 | |||
for sliceFile in sliceList: | |||
self.__uploadOssObjectWithRetry(sliceFile[0], uploadAddress['ObjectPrefix'] + sliceFile[1], uploadInfo, | |||
headers) | |||
# 上传m3u8文件 | |||
self.__uploadOssObjectWithRetry(m3u8LocalPath, uploadAddress['FileName'], uploadInfo, headers) | |||
# 删除下载到本地的m3u8文件和分片文件 | |||
if os.path.exists(m3u8LocalPath): | |||
os.remove(m3u8LocalPath) | |||
for sliceFile in sliceList: | |||
if os.path.exists(sliceFile[0]): | |||
os.remove(sliceFile[0]) | |||
if not os.listdir(m3u8LocalDir): | |||
os.rmdir(m3u8LocalDir) | |||
return uploadInfo['VideoId'] | |||
@catch_error | |||
def uploadImage(self, uploadImageRequest, isLocalFile=True): | |||
""" | |||
上传图片文件到点播,不支持断点续传;该接口可支持上传本地图片或网络图片 | |||
:param uploadImageRequest: UploadImageRequest,注意filePath为本地文件的绝对路径或网络文件的URL地址 | |||
:param isLocalFile: bool, 是否为本地文件。True:本地文件,False:网络文件 | |||
:return | |||
""" | |||
# 网络图片需要先下载到本地 | |||
if not isLocalFile: | |||
uploadImageRequest = self.__downloadWebMedia(uploadImageRequest) | |||
# 上传到点播 | |||
uploadInfo = self.__createUploadImage(uploadImageRequest) | |||
self.__uploadOssObject(uploadImageRequest.filePath, uploadInfo['UploadAddress']['FileName'], uploadInfo, None) | |||
# 删除本地临时文件 | |||
if not isLocalFile: | |||
os.remove(uploadImageRequest.filePath) | |||
return uploadInfo['ImageId'], uploadInfo['ImageURL'] | |||
@catch_error | |||
def uploadAttachedMedia(self, uploadAttachedRequest, isLocalFile=True): | |||
""" | |||
上传辅助媒资文件(如水印、字幕文件)到点播,不支持断点续传;该接口可支持上传本地或网络文件 | |||
:param uploadAttachedRequest: UploadAttachedMediaRequest,注意filePath为本地文件的绝对路径或网络文件的URL地址 | |||
:param isLocalFile: bool, 是否为本地文件。True:本地文件,False:网络文件 | |||
:return | |||
""" | |||
# 网络文件需要先下载到本地 | |||
if not isLocalFile: | |||
uploadAttachedRequest = self.__downloadWebMedia(uploadAttachedRequest) | |||
# 上传到点播 | |||
uploadInfo = self.__createUploadAttachedMedia(uploadAttachedRequest) | |||
self.__uploadOssObject(uploadAttachedRequest.filePath, uploadInfo['UploadAddress']['FileName'], uploadInfo, | |||
None) | |||
# 删除本地临时文件 | |||
if not isLocalFile: | |||
os.remove(uploadAttachedRequest.filePath) | |||
result = {'MediaId': uploadInfo['MediaId'], 'MediaURL': uploadInfo['MediaURL'], | |||
'FileURL': uploadInfo['FileURL']} | |||
return result | |||
@catch_error | |||
def parseWebM3u8(self, m3u8FileUrl): | |||
""" | |||
解析网络m3u8文件得到所有分片文件地址,原理是将m3u8地址前缀拼接ts分片名称作为后者的下载url,适用于url不带签名或分片与m3u8文件签名相同的情况 | |||
本函数解析时会默认分片文件和m3u8文件位于同一目录,如不是则请自行拼接分片文件的地址列表 | |||
:param m3u8FileUrl: string, m3u8网络文件url,例如:http://host/sample.m3u8 | |||
:return sliceFileUrls | |||
""" | |||
sliceFileUrls = [] | |||
res = requests.get(m3u8FileUrl) | |||
res.raise_for_status() | |||
for line in res.iter_lines(): | |||
if line.startswith('#'): | |||
continue | |||
sliceFileUrl = AliyunVodUtils.replaceFileName(m3u8FileUrl, line.strip()) | |||
sliceFileUrls.append(sliceFileUrl) | |||
return sliceFileUrls | |||
@catch_error | |||
def parseLocalM3u8(self, m3u8FilePath): | |||
""" | |||
解析本地m3u8文件得到所有分片文件地址,原理是将m3u8地址前缀拼接ts分片名称作为后者的本地路径 | |||
本函数解析时会默认分片文件和m3u8文件位于同一目录,如不是则请自行拼接分片文件的地址列表 | |||
:param m3u8FilePath: string, m3u8本地文件路径,例如:/opt/videos/sample.m3u8 | |||
:return sliceFilePaths | |||
""" | |||
sliceFilePaths = [] | |||
m3u8FilePath = AliyunVodUtils.toUnicode(m3u8FilePath) | |||
for line in open(m3u8FilePath): | |||
if line.startswith('#'): | |||
continue | |||
sliceFileName = line.strip() | |||
sliceFilePath = AliyunVodUtils.replaceFileName(m3u8FilePath, sliceFileName) | |||
sliceFilePaths.append(sliceFilePath) | |||
return sliceFilePaths | |||
# 定义进度条回调函数;consumedBytes: 已经上传的数据量,totalBytes:总数据量 | |||
def uploadProgressCallback(self, consumedBytes, totalBytes): | |||
try: | |||
if totalBytes: | |||
rate = int(100 * (float(consumedBytes) / float(totalBytes))) | |||
else: | |||
rate = 0 | |||
logger.info('视频上传中: {} bytes, percent:{}{}, requestId:{}', consumedBytes, format(rate), '%', | |||
self.__requestId) | |||
except Exception as e: | |||
logger.exception("打印视频上传进度回调方法异常: {}", e) | |||
# print("[%s]uploaded %s bytes, percent %s%s" % ( | |||
# AliyunVodUtils.getCurrentTimeStr(), consumedBytes, format(rate), '%')) | |||
# sys.stdout.flush() | |||
def __downloadWebMedia(self, request): | |||
# 下载媒体文件到本地临时目录 | |||
downloader = AliyunVodDownloader() | |||
localFileName = "%s.%s" % (AliyunVodUtils.getStringMd5(request.fileName), request.mediaExt) | |||
fileUrl = request.filePath | |||
err, localFilePath = downloader.downloadFile(fileUrl, localFileName) | |||
if err < 0: | |||
raise AliyunVodException('FileDownloadError', 'Download File Error', '') | |||
# 重新设置上传请求对象 | |||
request.setFilePath(localFilePath) | |||
return request | |||
def __rewriteM3u8File(self, srcM3u8File, dstM3u8File, isSrcLocal=True): | |||
newM3u8Text = '' | |||
if isSrcLocal: | |||
for line in open(AliyunVodUtils.toUnicode(srcM3u8File)): | |||
item = self.__processM3u8Line(line) | |||
if item is not None: | |||
newM3u8Text += item + "\n" | |||
else: | |||
res = requests.get(srcM3u8File) | |||
res.raise_for_status() | |||
for line in res.iter_lines(): | |||
item = self.__processM3u8Line(line) | |||
if item is not None: | |||
newM3u8Text += item + "\n" | |||
AliyunVodUtils.mkDir(dstM3u8File) | |||
with open(dstM3u8File, 'w') as f: | |||
f.write(newM3u8Text) | |||
def __processM3u8Line(self, line): | |||
item = line.strip() | |||
if len(item) <= 0: | |||
return None | |||
if item.startswith('#'): | |||
return item | |||
tempFilePath, fileName = AliyunVodUtils.getFileBriefPath(item) | |||
return fileName | |||
def __requestUploadInfo(self, request, mediaType): | |||
request.set_accept_format('JSON') | |||
result = json.loads(self.__vodClient.do_action_with_exception(request).decode('utf-8')) | |||
result['OriUploadAddress'] = result['UploadAddress'] | |||
result['OriUploadAuth'] = result['UploadAuth'] | |||
result['UploadAddress'] = json.loads(base64.b64decode(result['OriUploadAddress']).decode('utf-8')) | |||
result['UploadAuth'] = json.loads(base64.b64decode(result['OriUploadAuth']).decode('utf-8')) | |||
result['MediaType'] = mediaType | |||
if mediaType == 'video': | |||
result['MediaId'] = result['VideoId'] | |||
elif mediaType == 'image': | |||
result['MediaId'] = result['ImageId'] | |||
result['MediaURL'] = result['ImageURL'] | |||
return result | |||
# 获取视频上传地址和凭证 | |||
def __createUploadVideo(self, uploadVideoRequest): | |||
request = CreateUploadVideoRequest.CreateUploadVideoRequest() | |||
title = AliyunVodUtils.subString(uploadVideoRequest.title, VOD_MAX_TITLE_LENGTH) | |||
request.set_Title(title) | |||
request.set_FileName(uploadVideoRequest.fileName) | |||
if uploadVideoRequest.description: | |||
description = AliyunVodUtils.subString(uploadVideoRequest.description, VOD_MAX_DESCRIPTION_LENGTH) | |||
request.set_Description(description) | |||
if uploadVideoRequest.coverURL: | |||
request.set_CoverURL(uploadVideoRequest.coverURL) | |||
if uploadVideoRequest.tags: | |||
request.set_Tags(uploadVideoRequest.tags) | |||
if uploadVideoRequest.cateId: | |||
request.set_CateId(uploadVideoRequest.cateId) | |||
if uploadVideoRequest.templateGroupId: | |||
request.set_TemplateGroupId(uploadVideoRequest.templateGroupId) | |||
if uploadVideoRequest.storageLocation: | |||
request.set_StorageLocation(uploadVideoRequest.storageLocation) | |||
if uploadVideoRequest.userData: | |||
request.set_UserData(uploadVideoRequest.userData) | |||
if uploadVideoRequest.appId: | |||
request.set_AppId(uploadVideoRequest.appId) | |||
if uploadVideoRequest.workflowId: | |||
request.set_WorkflowId(uploadVideoRequest.workflowId) | |||
# 根据request发送请求阿里云 | |||
result = self.__requestUploadInfo(request, 'video') | |||
# logger.info("CreateUploadVideo, 获取响应体: {}, requestId:{}", result, self.__requestId) | |||
logger.info("CreateUploadVideo, FilePath: {}, VideoId: {}, requestId:{}", uploadVideoRequest.filePath, | |||
result['VideoId'], self.__requestId) | |||
return result | |||
# 刷新上传凭证 | |||
def __refresh_upload_video(self, videoId): | |||
request = RefreshUploadVideoRequest.RefreshUploadVideoRequest(); | |||
request.set_VideoId(videoId) | |||
result = self.__requestUploadInfo(request, 'video') | |||
logger.info("RefreshUploadVideo, VideoId:{}, requestId:{}", result['VideoId'], self.__requestId) | |||
return result | |||
# 获取图片上传地址和凭证 | |||
def __createUploadImage(self, uploadImageRequest): | |||
request = CreateUploadImageRequest.CreateUploadImageRequest() | |||
request.set_ImageType(uploadImageRequest.imageType) | |||
request.set_ImageExt(uploadImageRequest.imageExt) | |||
if uploadImageRequest.title: | |||
title = AliyunVodUtils.subString(uploadImageRequest.title, VOD_MAX_TITLE_LENGTH) | |||
request.set_Title(title) | |||
if uploadImageRequest.description: | |||
description = AliyunVodUtils.subString(uploadImageRequest.description, VOD_MAX_DESCRIPTION_LENGTH) | |||
request.set_Description(description) | |||
if uploadImageRequest.tags: | |||
request.set_Tags(uploadImageRequest.tags) | |||
if uploadImageRequest.cateId: | |||
request.set_CateId(uploadImageRequest.cateId) | |||
if uploadImageRequest.storageLocation: | |||
request.set_StorageLocation(uploadImageRequest.storageLocation) | |||
if uploadImageRequest.userData: | |||
request.set_UserData(uploadImageRequest.userData) | |||
if uploadImageRequest.appId: | |||
request.set_AppId(uploadImageRequest.appId) | |||
if uploadImageRequest.workflowId: | |||
request.set_WorkflowId(uploadImageRequest.workflowId) | |||
result = self.__requestUploadInfo(request, 'image') | |||
logger.info("CreateUploadImage, FilePath: %s, ImageId: %s, ImageUrl: %s" % ( | |||
uploadImageRequest.filePath, result['ImageId'], result['ImageURL'])) | |||
return result | |||
def __createUploadAttachedMedia(self, uploadAttachedRequest): | |||
request = CreateUploadAttachedMediaRequest.CreateUploadAttachedMediaRequest() | |||
request.set_BusinessType(uploadAttachedRequest.businessType) | |||
request.set_MediaExt(uploadAttachedRequest.mediaExt) | |||
if uploadAttachedRequest.title: | |||
title = AliyunVodUtils.subString(uploadAttachedRequest.title, VOD_MAX_TITLE_LENGTH) | |||
request.set_Title(title) | |||
if uploadAttachedRequest.description: | |||
description = AliyunVodUtils.subString(uploadAttachedRequest.description, VOD_MAX_DESCRIPTION_LENGTH) | |||
request.set_Description(description) | |||
if uploadAttachedRequest.tags: | |||
request.set_Tags(uploadAttachedRequest.tags) | |||
if uploadAttachedRequest.cateId: | |||
request.set_CateId(uploadAttachedRequest.cateId) | |||
if uploadAttachedRequest.storageLocation: | |||
request.set_StorageLocation(uploadAttachedRequest.storageLocation) | |||
if uploadAttachedRequest.userData: | |||
request.set_UserData(uploadAttachedRequest.userData) | |||
if uploadAttachedRequest.appId: | |||
request.set_AppId(uploadAttachedRequest.appId) | |||
if uploadAttachedRequest.workflowId: | |||
request.set_WorkflowId(uploadAttachedRequest.workflowId) | |||
result = self.__requestUploadInfo(request, 'attached') | |||
logger.info("CreateUploadImage, FilePath: %s, MediaId: %s, MediaURL: %s" % ( | |||
uploadAttachedRequest.filePath, result['MediaId'], result['MediaURL'])) | |||
return result | |||
def __getUploadHeaders(self, uploadVideoRequest): | |||
if uploadVideoRequest.isShowWatermark is None: | |||
return None | |||
else: | |||
userData = "{\"Vod\":{\"UserData\":{\"IsShowWaterMark\": \"%s\"}}}" % (uploadVideoRequest.isShowWatermark) | |||
return {'x-oss-notification': base64.b64encode(userData, 'utf-8')} | |||
# uploadType,可选:multipart, put, web | |||
def __uploadOssObjectWithRetry(self, filePath, object, uploadInfo, headers=None): | |||
retryTimes = 0 | |||
while retryTimes < self.__maxRetryTimes: | |||
try: | |||
return self.__uploadOssObject(filePath, object, uploadInfo, headers) | |||
except OssError as e: | |||
# 上传凭证过期需要重新获取凭证 | |||
if e.code == 'SecurityTokenExpired' or e.code == 'InvalidAccessKeyId': | |||
uploadInfo = self.__refresh_upload_video(uploadInfo['MediaId']) | |||
except Exception as e: | |||
raise e | |||
except: | |||
raise AliyunVodException('UnkownError', repr(e), traceback.format_exc()) | |||
finally: | |||
retryTimes += 1 | |||
else: | |||
raise Exception("重试超过限制") | |||
def __uploadOssObject(self, filePath, object, uploadInfo, headers=None): | |||
self.__createOssClient(uploadInfo['UploadAuth'], uploadInfo['UploadAddress']) | |||
""" | |||
p = os.path.dirname(os.path.realpath(__file__)) | |||
store = os.path.dirname(p) + '/osstmp' | |||
return oss2.resumable_upload(self.__bucketClient, object, filePath, | |||
store=oss2.ResumableStore(root=store), headers=headers, | |||
multipart_threshold=self.__multipartThreshold, part_size=self.__multipartPartSize, | |||
num_threads=self.__multipartThreadsNum, progress_callback=self.uploadProgressCallback) | |||
""" | |||
uploader = _VodResumableUploader(self.__bucketClient, filePath, object, uploadInfo, headers, | |||
self.uploadProgressCallback, self.__refreshUploadAuth, | |||
requestId=self.__requestId) | |||
uploader.setMultipartInfo(self.__multipartThreshold, self.__multipartPartSize, self.__multipartThreadsNum) | |||
uploader.setClientId(self.__accessKeyId) | |||
res = uploader.upload() | |||
uploadAddress = uploadInfo['UploadAddress'] | |||
bucketHost = uploadAddress['Endpoint'].replace('://', '://' + uploadAddress['Bucket'] + ".") | |||
logger.info("UploadFile {} Finish, MediaId: {}, FilePath: {}, Destination: {}/{}, requestId:{}", | |||
uploadInfo['MediaType'], uploadInfo['MediaId'], filePath, bucketHost, object, self.__requestId) | |||
return res | |||
# 使用上传凭证和地址信息初始化OSS客户端(注意需要先Base64解码并Json Decode再传入) | |||
# 如果上传的ECS位于点播相同的存储区域(如上海),则可以指定internal为True,通过内网上传更快且免费 | |||
def __createOssClient(self, uploadAuth, uploadAddress): | |||
auth = oss2.StsAuth(uploadAuth['AccessKeyId'], uploadAuth['AccessKeySecret'], uploadAuth['SecurityToken']) | |||
endpoint = AliyunVodUtils.convertOssInternal(uploadAddress['Endpoint'], self.__ecsRegion) | |||
self.__bucketClient = oss2.Bucket(auth, endpoint, uploadAddress['Bucket'], | |||
connect_timeout=self.__connTimeout, enable_crc=self.__EnableCrc) | |||
return self.__bucketClient | |||
def __refreshUploadAuth(self, videoId): | |||
uploadInfo = self.__refresh_upload_video(videoId) | |||
uploadAuth = uploadInfo['UploadAuth'] | |||
uploadAddress = uploadInfo['UploadAddress'] | |||
return self.__createOssClient(uploadAuth, uploadAddress) | |||
from oss2 import SizedFileAdapter, determine_part_size | |||
from oss2.models import PartInfo | |||
from aliyunsdkcore.utils import parameter_helper as helper | |||
class _VodResumableUploader: | |||
def __init__(self, bucket, filePath, object, uploadInfo, headers, progressCallback, refreshAuthCallback, | |||
requestId=None): | |||
self.__bucket = bucket | |||
self.__filePath = filePath | |||
self.__object = object | |||
self.__uploadInfo = uploadInfo | |||
self.__totalSize = None | |||
self.__headers = headers | |||
self.__mtime = os.path.getmtime(filePath) | |||
self.__progressCallback = progressCallback | |||
self.__refreshAuthCallback = refreshAuthCallback | |||
self.__threshold = None | |||
self.__partSize = None | |||
self.__threadsNum = None | |||
self.__uploadId = 0 | |||
self.__record = {} | |||
self.__finishedSize = 0 | |||
self.__finishedParts = [] | |||
self.__filePartHash = None | |||
self.__clientId = None | |||
self.__requestId = requestId | |||
def setMultipartInfo(self, threshold, partSize, threadsNum): | |||
self.__threshold = threshold | |||
self.__partSize = partSize | |||
self.__threadsNum = threadsNum | |||
def setClientId(self, clientId): | |||
self.__clientId = clientId | |||
def upload(self): | |||
self.__totalSize = os.path.getsize(self.__filePath) | |||
logger.info("上传视频路径: {}, 视频大小: {}, requestId:{}", self.__filePath, self.__totalSize, self.__requestId) | |||
if self.__threshold and self.__totalSize <= self.__threshold: | |||
return self.simpleUpload() | |||
else: | |||
return self.multipartUpload() | |||
def simpleUpload(self): | |||
with open(AliyunVodUtils.toUnicode(self.__filePath), 'rb') as f: | |||
result = self.__bucket.put_object(self.__object, f, headers=self.__headers, progress_callback=None) | |||
if self.__uploadInfo['MediaType'] == 'video': | |||
self.__reportUploadProgress('put', 1, self.__totalSize) | |||
return result | |||
def multipartUpload(self): | |||
psize = oss2.determine_part_size(self.__totalSize, preferred_size=self.__partSize) | |||
# 初始化分片 | |||
self.__uploadId = self.__bucket.init_multipart_upload(self.__object).upload_id | |||
startTime = time.time() | |||
expireSeconds = 2500 # 上传凭证有效期3000秒,提前刷新 | |||
# 逐个上传分片 | |||
with open(AliyunVodUtils.toUnicode(self.__filePath), 'rb') as fileObj: | |||
partNumber = 1 | |||
offset = 0 | |||
while offset < self.__totalSize: | |||
uploadSize = min(psize, self.__totalSize - offset) | |||
# logger.info("UploadPart, FilePath: %s, VideoId: %s, UploadId: %s, PartNumber: %s, PartSize: %s" % (self.__fileName, self.__videoId, self.__uploadId, partNumber, uploadSize)) | |||
result = self.__upload_part(partNumber, fileObj, uploadSize) | |||
# print(result.request_id) | |||
self.__finishedParts.append(PartInfo(partNumber, result.etag)) | |||
offset += uploadSize | |||
partNumber += 1 | |||
# 上传进度回调 | |||
self.__progressCallback(offset, self.__totalSize) | |||
if self.__uploadInfo['MediaType'] == 'video': | |||
# 上报上传进度 | |||
self.__reportUploadProgress('multipart', partNumber - 1, offset) | |||
# 检测上传凭证是否过期 | |||
nowTime = time.time() | |||
if nowTime - startTime >= expireSeconds: | |||
self.__bucket = self.__refreshAuthCallback(self.__uploadInfo['MediaId']) | |||
startTime = nowTime | |||
# 完成分片上传 | |||
self.__complete_multipart_upload() | |||
return result | |||
def __upload_part(self, partNumber, fileObj, uploadSize): | |||
retry_num = 0 | |||
while True: | |||
try: | |||
return self.__bucket.upload_part(self.__object, self.__uploadId, partNumber, | |||
SizedFileAdapter(fileObj, uploadSize)) | |||
except Exception as e: | |||
logger.error("阿里云分片上传异常报错: {}, 当前重试次数:{} requestId:{}", str(e), retry_num + 1, self.__requestId) | |||
if retry_num > 3: | |||
raise Exception("阿里云分片上传异常") | |||
except: | |||
logger.error("阿里云完成分片上传异常报错, 当前重试次数:{}, requestId:{}", retry_num + 1, self.__requestId) | |||
if retry_num > 3: | |||
raise Exception("阿里云分片上传异常") | |||
finally: | |||
retry_num += 1 | |||
time.sleep(1) | |||
def __complete_multipart_upload(self): | |||
retry_num = 0 | |||
while True: | |||
try: | |||
self.__bucket.complete_multipart_upload(self.__object, self.__uploadId, self.__finishedParts, | |||
headers=self.__headers) | |||
break | |||
except Exception as e: | |||
logger.error("阿里云完成分片上传异常报错: {}, 当前重试次数:{}, requestId:{}", str(e), retry_num + 1, self.__requestId) | |||
if retry_num > 5: | |||
raise Exception("阿里云完成分片上传异常") | |||
except: | |||
logger.error("阿里云完成分片上传异常报错, 当前重试次数:{}, requestId:{}", retry_num + 1, self.__requestId) | |||
if retry_num > 5: | |||
raise Exception("阿里云完成分片上传异常") | |||
finally: | |||
time.sleep(1) | |||
retry_num += 1 | |||
def __reportUploadProgress(self, uploadMethod, donePartsCount, doneBytes): | |||
retry_num = 5 | |||
current_num = 0 | |||
while True: | |||
try: | |||
reportHost = 'vod.cn-shanghai.aliyuncs.com' | |||
sdkVersion = '1.3.1' | |||
reportKey = 'HBL9nnSwhtU2$STX' | |||
uploadPoint = {'upMethod': uploadMethod, 'partSize': self.__partSize, 'doneBytes': doneBytes} | |||
timestamp = int(time.time()) | |||
authInfo = AliyunVodUtils.getStringMd5("%s|%s|%s" % (self.__clientId, reportKey, timestamp)) | |||
fields = {'Action': 'ReportUploadProgress', 'Format': 'JSON', 'Version': '2017-03-21', | |||
'Timestamp': helper.get_iso_8061_date(), 'SignatureNonce': helper.get_uuid(), | |||
'VideoId': self.__uploadInfo['MediaId'], 'Source': 'PythonSDK', 'ClientId': self.__clientId, | |||
'BusinessType': 'UploadVideo', 'TerminalType': 'PC', 'DeviceModel': 'Server', | |||
'AppVersion': sdkVersion, 'AuthTimestamp': timestamp, 'AuthInfo': authInfo, | |||
'FileName': self.__filePath, | |||
'FileHash': self.__getFilePartHash(self.__clientId, self.__filePath, self.__totalSize), | |||
'FileSize': self.__totalSize, 'FileCreateTime': timestamp, 'UploadRatio': 0, | |||
'UploadId': self.__uploadId, | |||
'DonePartsCount': donePartsCount, 'PartSize': self.__partSize, | |||
'UploadPoint': json.dumps(uploadPoint), | |||
'UploadAddress': self.__uploadInfo['OriUploadAddress'] | |||
} | |||
requests.post('http://' + reportHost, fields, timeout=30) | |||
break | |||
except Exception as e: | |||
current_num += 1 | |||
time.sleep(1) | |||
logger.error("vod上报视频进度异常: {}, 当前重试次数:{}, requestId:{}", repr(e), current_num, | |||
self.__requestId) | |||
if current_num > retry_num: | |||
logger.error("vod上报视频重试失败 {}, requestId:{}", repr(e), self.__requestId) | |||
raise e | |||
def __getFilePartHash(self, clientId, filePath, fileSize): | |||
if self.__filePartHash: | |||
return self.__filePartHash | |||
length = 1 * 1024 * 1024 | |||
if fileSize < length: | |||
length = fileSize | |||
try: | |||
fp = open(AliyunVodUtils.toUnicode(filePath), 'rb') | |||
strVal = fp.read(length) | |||
self.__filePartHash = AliyunVodUtils.getStringMd5(strVal, False) | |||
fp.close() | |||
except: | |||
self.__filePartHash = "%s|%s|%s" % (clientId, filePath, self.__mtime) | |||
return self.__filePartHash |
@@ -0,0 +1,327 @@ | |||
# -*- coding: UTF-8 -*- | |||
import os, sys | |||
import hashlib | |||
import datetime | |||
import functools | |||
import logging | |||
from loguru import logger | |||
from oss2.exceptions import OssError | |||
from aliyunsdkcore.acs_exception.exceptions import ServerException | |||
from aliyunsdkcore.acs_exception.exceptions import ClientException | |||
import traceback | |||
import requests | |||
if sys.version_info[0] == 3: | |||
import urllib.parse | |||
else: | |||
from urllib import unquote | |||
VOD_PRINT_INFO_LOG_SWITCH = 1 | |||
class AliyunVodLog: | |||
""" | |||
VOD日志类,基于logging实现 | |||
""" | |||
@staticmethod | |||
def printLogStr(msg, *args, **kwargs): | |||
if VOD_PRINT_INFO_LOG_SWITCH: | |||
print("[%s]%s" % (AliyunVodUtils.getCurrentTimeStr(), msg)) | |||
@staticmethod | |||
def info(msg, *args, **kwargs): | |||
logging.info(msg, *args, **kwargs) | |||
AliyunVodLog.printLogStr(msg, *args, **kwargs) | |||
@staticmethod | |||
def error(msg, *args, **kwargs): | |||
logging.error(msg, *args, **kwargs) | |||
AliyunVodLog.printLogStr(msg, *args, **kwargs) | |||
@staticmethod | |||
def warning(msg, *args, **kwargs): | |||
logging.warning(msg, *args, **kwargs) | |||
AliyunVodLog.printLogStr(msg, *args, **kwargs) | |||
class AliyunVodUtils: | |||
""" | |||
VOD上传SDK的工具类,提供截取字符串、获取扩展名、获取文件名等静态函数 | |||
""" | |||
# 截取字符串,在不超过最大字节数前提下确保中文字符不被截断出现乱码(先转换成unicode,再取子串,然后转换成utf-8) | |||
@staticmethod | |||
def subString(strVal, maxBytes, charSet='utf-8'): | |||
i = maxBytes | |||
if sys.version_info[0] == 3: | |||
while len(strVal.encode(charSet)) > maxBytes: | |||
if i < 0: | |||
return '' | |||
strVal = strVal[:i] | |||
i -= 1 | |||
else: | |||
while len(strVal) > maxBytes: | |||
if i < 0: | |||
return '' | |||
strVal = strVal.decode(charSet)[:i].encode(charSet) | |||
i -= 1 | |||
return strVal | |||
@staticmethod | |||
def getFileExtension(fileName): | |||
end = fileName.rfind('?') | |||
if end <= 0: | |||
end = len(fileName) | |||
i = fileName.rfind('.') | |||
if i >= 0: | |||
return fileName[i + 1:end].lower() | |||
else: | |||
return None | |||
# urldecode | |||
@staticmethod | |||
def urlDecode(fileUrl): | |||
if sys.version_info[0] == 3: | |||
return urllib.parse.unquote(fileUrl) | |||
else: | |||
return unquote(fileUrl) | |||
# urlencode | |||
@staticmethod | |||
def urlEncode(fileUrl): | |||
if sys.version_info[0] == 3: | |||
return urllib.parse.urlencode(fileUrl) | |||
else: | |||
return urllib.urlencode(fileUrl) | |||
# 获取Url的摘要地址(去除?后的参数,如果有)以及文件名 | |||
@staticmethod | |||
def getFileBriefPath(fileUrl): | |||
# fileUrl = AliyunVodUtils.urlDecode(fileUrl) | |||
i = fileUrl.rfind('?') | |||
if i > 0: | |||
briefPath = fileUrl[:i] | |||
else: | |||
briefPath = fileUrl | |||
briefName = os.path.basename(briefPath) | |||
return briefPath, AliyunVodUtils.urlDecode(briefName) | |||
@staticmethod | |||
def getStringMd5(strVal, isEncode=True): | |||
m = hashlib.md5() | |||
m.update(strVal.encode('utf-8') if isEncode else strVal) | |||
return m.hexdigest() | |||
@staticmethod | |||
def getCurrentTimeStr(): | |||
now = datetime.datetime.now() | |||
return now.strftime("%Y-%m-%d %H:%M:%S") | |||
# 将oss地址转换为内网地址(如果脚本部署的ecs与oss bucket在同一区域) | |||
@staticmethod | |||
def convertOssInternal(ossUrl, ecsRegion=None, isVpc=False): | |||
if (not ossUrl) or (not ecsRegion): | |||
return ossUrl | |||
availableRegions = ['cn-qingdao', 'cn-beijing', 'cn-zhangjiakou', 'cn-huhehaote', 'cn-hangzhou', 'cn-shanghai', | |||
'cn-shenzhen', | |||
'cn-hongkong', 'ap-southeast-1', 'ap-southeast-2', 'ap-southeast-3', | |||
'ap-northeast-1', 'us-west-1', 'us-east-1', 'eu-central-1', 'me-east-1'] | |||
if ecsRegion not in availableRegions: | |||
return ossUrl | |||
ossUrl = ossUrl.replace("https:", "http:") | |||
if isVpc: | |||
return ossUrl.replace("oss-%s.aliyuncs.com" % (ecsRegion), "vpc100-oss-%s.aliyuncs.com" % (ecsRegion)) | |||
else: | |||
return ossUrl.replace("oss-%s.aliyuncs.com" % (ecsRegion), "oss-%s-internal.aliyuncs.com" % (ecsRegion)) | |||
# 把输入转换为unicode | |||
@staticmethod | |||
def toUnicode(data): | |||
if isinstance(data, bytes): | |||
return data.decode('utf-8') | |||
else: | |||
return data | |||
# 替换路径中的文件名;考虑分隔符为"/" 或 "\"(windows) | |||
@staticmethod | |||
def replaceFileName(filePath, replace): | |||
if len(filePath) <= 0 or len(replace) <= 0: | |||
return filePath | |||
filePath = AliyunVodUtils.urlDecode(filePath) | |||
separator = '/' | |||
start = filePath.rfind(separator) | |||
if start < 0: | |||
separator = '\\' | |||
start = filePath.rfind(separator) | |||
if start < 0: | |||
return None | |||
result = "%s%s%s" % (filePath[0:start], separator, replace) | |||
return result | |||
# 创建文件中的目录 | |||
@staticmethod | |||
def mkDir(filePath): | |||
if len(filePath) <= 0: | |||
return -1 | |||
separator = '/' | |||
i = filePath.rfind(separator) | |||
if i < 0: | |||
separator = '\\' | |||
i = filePath.rfind(separator) | |||
if i < 0: | |||
return -2 | |||
dirs = filePath[:i] | |||
if os.path.exists(dirs) and os.path.isdir(dirs): | |||
return 0 | |||
os.makedirs(dirs) | |||
return 1 | |||
class AliyunVodException(Exception): | |||
""" | |||
VOD上传SDK的异常类,做统一的异常处理,外部捕获此异常即可 | |||
""" | |||
def __init__(self, type, code, msg, http_status=None, request_id=None): | |||
Exception.__init__(self) | |||
self.type = type or 'UnkownError' | |||
self.code = code | |||
self.message = msg | |||
self.http_status = http_status or 'NULL' | |||
self.request_id = request_id or 'NULL' | |||
def __str__(self): | |||
return "Type: %s, Code: %s, Message: %s, HTTPStatus: %s, RequestId: %s" % ( | |||
self.type, self.code, self.message, str(self.http_status), self.request_id) | |||
def catch_error(method): | |||
""" | |||
装饰器,将内部异常转换成统一的异常类AliyunVodException | |||
""" | |||
@functools.wraps(method) | |||
def wrapper(self, *args, **kwargs): | |||
try: | |||
return method(self, *args, **kwargs) | |||
except ServerException as e: | |||
logger.error("阿里云ServerException异常, error_code: {}, error_msg:{}, status:{}, requestId:{}", | |||
e.get_error_code(), e.get_error_msg(), e.get_http_status(), self.__requestId) | |||
# 可能原因:AK错误、账号无权限、参数错误等 | |||
raise AliyunVodException('ServerException', e.get_error_code(), e.get_error_msg(), e.get_http_status(), | |||
e.get_request_id()) | |||
except ClientException as e: | |||
logger.error("阿里云ClientException异常, error_code: {}, error_msg:{}, requestId:{}", e.get_error_code(), | |||
e.get_error_msg(), self.__requestId) | |||
# 可能原因:本地网络故障(如不能连接外网)等 | |||
raise AliyunVodException('ClientException', e.get_error_code(), e.get_error_msg()) | |||
except OssError as e: | |||
logger.error("阿里云OssError异常, error_code: {}, error_msg:{}, status:{}, requestId:{}", e.code, e.message, | |||
e.status, self.__requestId) | |||
# 可能原因:上传凭证过期等 | |||
raise AliyunVodException('OssError', e.code, e.message, e.status, e.request_id) | |||
except IOError as e: | |||
logger.error("阿里云IOError异常: {}, requestId:{}", traceback.format_exc(), self.__requestId) | |||
# 可能原因:文件URL不能访问、本地文件无法读取等 | |||
raise AliyunVodException('IOError', repr(e), traceback.format_exc()) | |||
except OSError as e: | |||
logger.error("阿里云OSError异常: {}, requestId:{}", traceback.format_exc(), self.__requestId) | |||
# 可能原因:本地文件不存在等 | |||
raise AliyunVodException('OSError', repr(e), traceback.format_exc()) | |||
except AliyunVodException as e: | |||
logger.error("阿里云VodException异常: {}, requestId:{}", e, self.__requestId) | |||
# 可能原因:参数错误 | |||
raise e | |||
except Exception as e: | |||
logger.error("阿里云UnkownException异常: {}, requestId:{}", traceback.format_exc(), self.__requestId) | |||
raise AliyunVodException('UnkownException', repr(e), traceback.format_exc()) | |||
except: | |||
logger.error("阿里云UnkownError异常: {}, requestId:{}", traceback.format_exc(), self.__requestId) | |||
raise AliyunVodException('UnkownError', 'UnkownError', traceback.format_exc()) | |||
return wrapper | |||
class AliyunVodDownloader: | |||
""" | |||
VOD网络文件的下载类,上传网络文件时会先下载到本地临时目录,再上传到点播 | |||
""" | |||
def __init__(self, localDir=None): | |||
if localDir: | |||
self.__localDir = localDir | |||
else: | |||
p = os.path.dirname(os.path.realpath(__file__)) | |||
self.__localDir = os.path.dirname(p) + '/dlfiles' | |||
def setSaveLocalDir(self, localDir): | |||
self.__localDir = localDir | |||
def getSaveLocalDir(self): | |||
return self.__localDir | |||
def downloadFile(self, fileUrl, localFileName, fileSize=None): | |||
localPath = self.__localDir + '/' + localFileName | |||
logger.info("Download %s To %s" % (fileUrl, localPath)) | |||
try: | |||
lsize = self.getFileSize(localPath) | |||
if fileSize and lsize == fileSize: | |||
logger.info('Download OK, File Exists') | |||
return 0, localPath | |||
AliyunVodUtils.mkDir(self.__localDir) | |||
err, webPage = self.__openWebFile(fileUrl, lsize) | |||
if err == 0: | |||
logger.info('Download OK, File Exists') | |||
webPage.close() | |||
return 0, localPath | |||
fileObj = open(localPath, 'ab+') | |||
for chunk in webPage.iter_content(chunk_size=8 * 1024): | |||
if chunk: | |||
fileObj.write(chunk) | |||
except Exception as e: | |||
logger.error("Download fail: %s" % (e)) | |||
return -1, None | |||
fileObj.close() | |||
webPage.close() | |||
logger.info('Download OK') | |||
return 1, localPath | |||
def getFileSize(self, filePath): | |||
try: | |||
lsize = os.stat(filePath).st_size | |||
except: | |||
lsize = 0 | |||
return lsize | |||
def __openWebFile(self, fileUrl, offset): | |||
webPage = None | |||
try: | |||
headers = {'Range': 'bytes=%d-' % offset} | |||
webPage = requests.get(fileUrl, stream=True, headers=headers, timeout=120, verify=False) | |||
status_code = webPage.status_code | |||
err = -1 | |||
if status_code in [200, 206]: | |||
err = 1 | |||
elif status_code == 416: | |||
err = 0 | |||
else: | |||
logger.error("Download offset %s fail, invalid url, status: %s" % (offset, status_code)) | |||
except Exception as e: | |||
logger.error("Download offset %s fail: %s" % (offset, e)) | |||
err = -2 | |||
finally: | |||
return err, webPage |
@@ -0,0 +1,87 @@ | |||
# -*- coding: UTF-8 -*- | |||
""" | |||
# Class UploadAttachedMediaRequest | |||
# | |||
# Aliyun VoD's Upload Attached Media(such as watermark,subtitle files) Request class, which wraps parameters to upload an media file into VoD. | |||
# Users could pass parameters to AliyunVodUploader, including File Path,Title,etc. via an UploadAttachedMediaRequest instance. | |||
# For more details, please check out the VoD API document: https://help.aliyun.com/document_detail/98467.html | |||
""" | |||
from vodsdk.AliyunVodUtils import * | |||
class UploadAttachedMediaRequest: | |||
def __init__(self, filePath, businessType, title=None, fileExt=None): | |||
""" | |||
constructor for UploadAttachedMediaRequest | |||
:param filePath: string, 文件的绝对路径,或者网络文件的URL,必须含有扩展名 | |||
:return | |||
""" | |||
self.businessType = businessType | |||
self.filePath = None | |||
self.fileName = None | |||
self.mediaExt = None | |||
self.title = None | |||
self.setFilePath(filePath, title, fileExt) | |||
self.fileSize = None | |||
self.cateId = None | |||
self.tags = None | |||
self.description = None | |||
self.userData = None | |||
self.storageLocation = None | |||
self.appId = None | |||
self.workflowId = None | |||
def setFilePath(self, filePath, title=None, fileExt=None): | |||
if fileExt is None: | |||
fileExt = AliyunVodUtils.getFileExtension(filePath) | |||
if not fileExt: | |||
raise AliyunVodException('ParameterError', 'InvalidParameter', 'filePath has no Extension') | |||
fileExt = fileExt.lstrip('.') | |||
self.mediaExt = fileExt | |||
self.filePath = AliyunVodUtils.toUnicode(filePath) | |||
briefPath, briefName = AliyunVodUtils.getFileBriefPath(self.filePath) | |||
self.fileName = briefPath | |||
if fileExt and (not self.fileName.endswith('.' + fileExt)): | |||
self.fileName = self.fileName + '.' + fileExt | |||
if title: | |||
self.title = title | |||
else: | |||
if self.title is None: | |||
self.title = briefName | |||
def setBusinessType(self, businessType): | |||
self.businessType = businessType | |||
def setTitle(self, title): | |||
self.title = title | |||
def setFileSize(self, fileSize): | |||
self.fileSize = fileSize | |||
def setCateId(self, cateId): | |||
self.cateId = cateId | |||
def setTags(self, tags): | |||
self.tags = tags | |||
def setDescription(self, description): | |||
self.description = description | |||
def setStorageLocation(self, storageLocation): | |||
self.storageLocation = storageLocation | |||
def setUserData(self, userData): | |||
self.userData = userData | |||
def setAppId(self, appId): | |||
self.appId = appId | |||
def setWorkflowId(self, workflowId): | |||
self.workflowId = workflowId | |||
@@ -0,0 +1,84 @@ | |||
# -*- coding: UTF-8 -*- | |||
""" | |||
# Class UploadImageRequest | |||
# | |||
# Aliyun VoD's Upload Image Request class, which wraps parameters to upload an image into VoD. | |||
# Users could pass parameters to AliyunVodUploader, including File Path,Title,etc. via an UploadImageRequest instance. | |||
# For more details, please check out the VoD API document: https://help.aliyun.com/document_detail/55619.html | |||
""" | |||
from vodsdk.AliyunVodUtils import * | |||
class UploadImageRequest: | |||
def __init__(self, filePath, title=None, fileExt=None): | |||
""" | |||
constructor for UploadVideoRequest | |||
:param filePath: string, 文件的绝对路径,或者网络文件的URL,必须含有扩展名 | |||
:param title: string, 图片标题 | |||
:return | |||
""" | |||
self.filePath = None | |||
self.fileName = None | |||
self.imageExt = None | |||
self.mediaExt = None | |||
self.title = None | |||
self.setFilePath(filePath, title, fileExt) | |||
self.imageType = 'default' | |||
self.cateId = None | |||
self.tags = None | |||
self.description = None | |||
self.userData = None | |||
self.storageLocation = None | |||
self.appId = None | |||
self.workflowId = None | |||
def setFilePath(self, filePath, title=None, fileExt=None): | |||
if fileExt is None: | |||
fileExt = AliyunVodUtils.getFileExtension(filePath) | |||
if not fileExt: | |||
raise AliyunVodException('ParameterError', 'InvalidParameter', 'filePath has no Extension') | |||
fileExt = fileExt.lstrip('.') | |||
self.imageExt = fileExt | |||
self.mediaExt = fileExt | |||
self.filePath = AliyunVodUtils.toUnicode(filePath) | |||
briefPath, briefName = AliyunVodUtils.getFileBriefPath(self.filePath) | |||
self.fileName = briefPath | |||
if fileExt and (not self.fileName.endswith('.' + fileExt)): | |||
self.fileName = self.fileName + '.' + fileExt | |||
if title: | |||
self.title = title | |||
else: | |||
if self.title is None: | |||
self.title = briefName | |||
def setImageType(self, imageType): | |||
self.imageType = imageType | |||
def setTitle(self, title): | |||
self.title = title | |||
def setCateId(self, cateId): | |||
self.cateId = cateId | |||
def setTags(self, tags): | |||
self.tags = tags | |||
def setDescription(self, description): | |||
self.description = description | |||
def setStorageLocation(self, storageLocation): | |||
self.storageLocation = storageLocation | |||
def setUserData(self, userData): | |||
self.userData = userData | |||
def setAppId(self, appId): | |||
self.appId = appId | |||
def setWorkflowId(self, workflowId): | |||
self.workflowId = workflowId |
@@ -0,0 +1,86 @@ | |||
# -*- coding: UTF-8 -*- | |||
""" | |||
# Class UploadVideoRequest | |||
# | |||
# Aliyun VoD's Upload Video Request class, which wraps parameters to upload a video into VoD. | |||
# Users could pass parameters to AliyunVodUploader, including File Path,Title,etc. via an UploadVideoRequest instance. | |||
# For more details, please check out the VoD API document: https://help.aliyun.com/document_detail/55407.html | |||
""" | |||
from vodsdk.AliyunVodUtils import * | |||
class UploadVideoRequest: | |||
def __init__(self, filePath, title=None, fileExt=None): | |||
""" | |||
constructor for UploadVideoRequest | |||
:param filePath: string, 文件的绝对路径,或者网络文件的URL,必须含有扩展名 | |||
:param title: string, 视频标题,最长128字节,不传则使用文件名为标题 | |||
:return | |||
""" | |||
self.filePath = None | |||
self.fileName = None | |||
self.mediaExt = None | |||
self.title = None | |||
self.setFilePath(filePath, title, fileExt) | |||
self.cateId = None | |||
self.tags = None | |||
self.description = None | |||
self.coverURL = None | |||
self.templateGroupId = None | |||
self.isShowWatermark = None | |||
self.userData = None | |||
self.storageLocation = None | |||
self.uploadId = None | |||
self.appId = None | |||
self.workflowId = None | |||
def setFilePath(self, filePath, title=None, fileExt=None): | |||
if fileExt is None: | |||
fileExt = AliyunVodUtils.getFileExtension(filePath) | |||
if not fileExt: | |||
raise AliyunVodException('ParameterError', 'InvalidParameter', 'filePath has no Extension') | |||
fileExt = fileExt.lstrip('.') | |||
self.mediaExt = fileExt | |||
self.filePath = AliyunVodUtils.toUnicode(filePath) | |||
briefPath, briefName = AliyunVodUtils.getFileBriefPath(self.filePath) | |||
self.fileName = briefPath | |||
if fileExt and (not self.fileName.endswith('.' + fileExt)): | |||
self.fileName = self.fileName + '.' + fileExt | |||
if title: | |||
self.title = title | |||
else: | |||
if self.title is None: | |||
self.title = briefName | |||
def setCateId(self, cateId): | |||
self.cateId = cateId | |||
def setTags(self, tags): | |||
self.tags = tags | |||
def setDescription(self, description): | |||
self.description = description | |||
def setCoverURL(self, coverURL): | |||
self.coverURL = coverURL | |||
def setTemplateGroupId(self, templateGroupId): | |||
self.templateGroupId = templateGroupId | |||
# 关闭水印,仅用于配置全局水印且转码模板开启水印后,单次上传时关闭水印 | |||
def shutdownWatermark(self): | |||
self.isShowWatermark = False | |||
# 设置上传ID,可用于关联导入视频 | |||
def setUploadId(self, uploadId): | |||
self.uploadId = uploadId | |||
def setAppId(self, appId): | |||
self.appId = appId | |||
def setWorkflowId(self, workflowId): | |||
self.workflowId = workflowId |
@@ -0,0 +1,3 @@ | |||
__version__ = '1.3.1' | |||