Browse Source

Merge branch 'release' of gitadmin/tuoheng_alg into master

tags/V2.7.4
gitadmin 1 year ago
parent
commit
a649b50e7a
59 changed files with 2695 additions and 339 deletions
  1. +105
    -49
      .idea/workspace.xml
  2. BIN
      common/__pycache__/Constant.cpython-38.pyc
  3. BIN
      common/__pycache__/YmlConstant.cpython-38.pyc
  4. +5
    -4
      concurrency/CommonThread.py
  5. +5
    -2
      concurrency/FileUploadThread.py
  6. +83
    -46
      concurrency/IntelligentRecognitionProcess.py
  7. +36
    -7
      concurrency/PullVideoStreamProcess.py
  8. BIN
      concurrency/__pycache__/CommonThread.cpython-38.pyc
  9. +1
    -1
      dsp_master.py
  10. +1
    -1
      enums/BaiduSdkEnum.py
  11. +3
    -1
      enums/ExceptionEnum.py
  12. +57
    -24
      service/Dispatcher.py
  13. +3
    -3
      test/aliyun/vod.py
  14. +6
    -6
      test/aliyun/vodTest.py
  15. +3
    -3
      test/aliyun/voddemo.py
  16. +3
    -3
      test/aliyun/vodtest2.py
  17. +39
    -49
      test/ffmpeg11/aa.py
  18. +0
    -0
      test/numpy/__init__.py
  19. +108
    -0
      test/numpy/numpy_test.py
  20. +0
    -0
      test/opencv/__init__.py
  21. +198
    -0
      test/opencv/test.py
  22. +0
    -0
      test/pachong/__init__.py
  23. +24
    -0
      test/pachong/pa.py
  24. +0
    -0
      test/元类/__init__.py
  25. +121
    -0
      test/元类/demo1.py
  26. +20
    -18
      test/序列化/Test.py
  27. +5
    -0
      test/日志/test.py
  28. +0
    -0
      test/正则/__init__.py
  29. BIN
      test/正则/__pycache__/re.cpython-38.pyc
  30. +145
    -0
      test/正则/re.py
  31. +0
    -0
      test/类型标注/__init__.py
  32. +18
    -0
      test/类型标注/test.py
  33. +19
    -15
      test/线程/Test.py
  34. +0
    -0
      test/设计模式/__init__.py
  35. +0
    -0
      test/设计模式/单例/__init__.py
  36. BIN
      test/设计模式/单例/__pycache__/single.cpython-38.pyc
  37. +24
    -0
      test/设计模式/单例/demo2.py
  38. +28
    -0
      test/设计模式/单例/demo3.py
  39. +18
    -0
      test/设计模式/单例/demo4.py
  40. +22
    -0
      test/设计模式/单例/demo5.py
  41. +15
    -0
      test/设计模式/单例/single.py
  42. +7
    -0
      test/设计模式/单例/test.py
  43. +0
    -0
      test/设计模式/简单工厂模式/__init__.py
  44. +36
    -0
      test/设计模式/简单工厂模式/demo.py
  45. +44
    -0
      test/设计模式/简单工厂模式/demo1.py
  46. +48
    -29
      util/AliyunSdk.py
  47. +102
    -78
      util/Cv2Utils.py
  48. +26
    -0
      util/ModelUtils.py
  49. BIN
      util/__pycache__/LogUtils.cpython-38.pyc
  50. +730
    -0
      vodsdk/AliyunVodUploader.py
  51. +327
    -0
      vodsdk/AliyunVodUtils.py
  52. +87
    -0
      vodsdk/UploadAttachedMediaRequest.py
  53. +84
    -0
      vodsdk/UploadImageRequest.py
  54. +86
    -0
      vodsdk/UploadVideoRequest.py
  55. +3
    -0
      vodsdk/__init__.py
  56. BIN
      vodsdk/__pycache__/AliyunVodUploader.cpython-38.pyc
  57. BIN
      vodsdk/__pycache__/AliyunVodUtils.cpython-38.pyc
  58. BIN
      vodsdk/__pycache__/UploadVideoRequest.cpython-38.pyc
  59. BIN
      vodsdk/__pycache__/__init__.cpython-38.pyc

+ 105
- 49
.idea/workspace.xml View File

@@ -6,8 +6,17 @@
<component name="ChangeListManager">
<list default="true" id="4f7dccd9-8f92-4a6e-90cc-33890d102263" name="Changes" comment="Changes">
<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$/util/ModelUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/util/ModelUtils.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$/concurrency/PullVideoStreamProcess.py" beforeDir="false" afterPath="$PROJECT_DIR$/concurrency/PullVideoStreamProcess.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/dsp_master.py" beforeDir="false" afterPath="$PROJECT_DIR$/dsp_master.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/enums/ExceptionEnum.py" beforeDir="false" afterPath="$PROJECT_DIR$/enums/ExceptionEnum.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/service/Dispatcher.py" beforeDir="false" afterPath="$PROJECT_DIR$/service/Dispatcher.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/test/ffmpeg11/aa.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/ffmpeg11/aa.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" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -136,11 +145,12 @@
"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_alg/test/元类",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"project.structure.last.edited": "SDK",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2816092",
@@ -150,21 +160,22 @@
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\tuoheng\codenew\tuoheng_alg\test\元类" />
<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\test\设计模式\单例" />
<recent name="D:\tuoheng\codenew\tuoheng_alg\font" />
<recent name="D:\work\alg_new\tuoheng_alg\test\image" />
<recent name="D:\work\alg\tuoheng_alg\test\水印" />
<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.demo1">
<configuration name="demo" 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 +183,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$/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$/concurrency/IntelligentRecognitionProcess.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/设计模式/简单工厂模式/demo.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@@ -186,7 +197,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="color_test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<configuration name="demo1" 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 +205,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/设计模式/简单工厂模式/demo1.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@@ -208,20 +219,20 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="editImage" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<configuration name="demo3" 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="D:\tuoheng\codenew\tuoheng_alg\test\设计模式\单例\demo3.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@@ -230,7 +241,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="mysqltest" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<configuration name="demo4" 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 +249,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/mysqltest.py" />
<option name="SCRIPT_NAME" value="D:\tuoheng\codenew\tuoheng_alg\test\设计模式\单例\demo4.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@@ -252,7 +263,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test (2)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<configuration name="demo5" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="tuoheng_alg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
@@ -260,12 +271,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="D:\tuoheng\codenew\tuoheng_alg\test\设计模式\单例\demo5.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@@ -274,20 +285,20 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test" 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" />
@@ -296,7 +307,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test1" 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" />
@@ -304,12 +315,35 @@
<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/mysqltest.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<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="B:\software\conda\envs\test\python.exe" />
<option name="SDK_NAME" value="Python 3.8 (test)" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/类型标注" />
<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="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@@ -321,19 +355,19 @@
<list>
<item itemvalue="Python.editImage" />
<item itemvalue="Python.mysqltest" />
<item itemvalue="Python.color_test" />
<item itemvalue="Python.test (2)" />
<item itemvalue="Python.IntelligentRecognitionProcess" />
<item itemvalue="Python.test" />
<item itemvalue="Python.test1" />
<item itemvalue="Python.demo" />
<item itemvalue="Python.demo1" />
<item itemvalue="Python.demo3" />
<item itemvalue="Python.demo4" />
<item itemvalue="Python.demo5" />
</list>
<recent_temporary>
<list>
<item itemvalue="Python.color_test" />
<item itemvalue="Python.test (2)" />
<item itemvalue="Python.IntelligentRecognitionProcess" />
<item itemvalue="Python.test" />
<item itemvalue="Python.test1" />
<item itemvalue="Python.demo1" />
<item itemvalue="Python.demo" />
<item itemvalue="Python.demo5" />
<item itemvalue="Python.demo4" />
<item itemvalue="Python.demo3" />
</list>
</recent_temporary>
</component>
@@ -489,7 +523,19 @@
<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="24789000" />
<workItem from="1684110880642" duration="5895000" />
<workItem from="1684197638479" duration="9103000" />
<workItem from="1684284520362" duration="13345000" />
<workItem from="1684379357818" duration="22600000" />
<workItem from="1684456296559" duration="11147000" />
<workItem from="1684653340859" duration="1199000" />
<workItem from="1684715657250" duration="6747000" />
<workItem from="1684801865053" duration="7552000" />
</task>
<servers />
</component>
@@ -539,53 +585,63 @@
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/tuoheng_alg$color_test.coverage" NAME="color_test 覆盖结果" MODIFIED="1683683775604" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/color" />
<SUITE FILE_PATH="coverage/tuoheng_alg$demo1.coverage" NAME="demo1 覆盖结果" MODIFIED="1680162882599" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/demo" />
<SUITE FILE_PATH="coverage/tuoheng_alg$demo1.coverage" NAME="demo1 覆盖结果" MODIFIED="1684807129961" 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$ffmpeg33.coverage" NAME="ffmpeg33 覆盖结果" MODIFIED="1670489109246" 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$demo4.coverage" NAME="demo4 覆盖结果" MODIFIED="1684809818971" 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$minio.coverage" NAME="minio 覆盖结果" MODIFIED="1667465702864" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/minio1" />
<SUITE FILE_PATH="coverage/tuoheng_alg$3.coverage" NAME="视频添加文字水印3 Coverage Results" MODIFIED="1661906152928" 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$ffmpeg12.coverage" NAME="ffmpeg12 覆盖结果" MODIFIED="1675391366890" 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$Test__2_.coverage" NAME="Test (2) 覆盖结果" MODIFIED="1681796501563" 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$test1.coverage" NAME="test1 覆盖结果" MODIFIED="1681988279624" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/cuda" />
<SUITE FILE_PATH="coverage/tuoheng_alg$ossdemo.coverage" NAME="ossdemo 覆盖结果" MODIFIED="1681715255761" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/aliyun" />
<SUITE FILE_PATH="coverage/tuoheng_alg$test__1_.coverage" NAME="test (1) 覆盖结果" MODIFIED="1681969578447" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/cuda" />
<SUITE FILE_PATH="coverage/tuoheng_alg$test__1_.coverage" NAME="test (1) 覆盖结果" MODIFIED="1684480106837" 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$aa1.coverage" NAME="aa1 覆盖结果" MODIFIED="1667351136888" 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___$test.coverage" NAME="test 覆盖结果" MODIFIED="1668577200259" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/while" />
<SUITE FILE_PATH="coverage/tuoheng_alg$editImage.coverage" NAME="editImage 覆盖结果" MODIFIED="1678348350574" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/editimage" />
<SUITE FILE_PATH="coverage/tuoheng_alg$2.coverage" NAME="协程2 覆盖结果" MODIFIED="1668066168428" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="/opt/tuo_heng/algSch/test/协程/" />
<SUITE FILE_PATH="coverage/tuoheng_alg$ImgBaiduSdk.coverage" NAME="ImgBaiduSdk 覆盖结果" MODIFIED="1678355024003" 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$ImageUtils.coverage" NAME="ImageUtils Coverage Results" MODIFIED="1663499421253" 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$demo2.coverage" NAME="demo2 覆盖结果" MODIFIED="1684808407865" 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$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="1684742307978" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/opencv" />
<SUITE FILE_PATH="coverage/tuoheng_alg$demo3.coverage" NAME="demo3 覆盖结果" MODIFIED="1684809071819" 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/" />
<SUITE FILE_PATH="coverage/tuoheng_alg$test2.coverage" NAME="test2 覆盖结果" MODIFIED="1669178077956" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/str" />
<SUITE FILE_PATH="coverage/tuoheng_alg$aa.coverage" NAME="aa 覆盖结果" MODIFIED="1670490313339" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="/home/chenyukun/algSch/test/ffmpeg11" />
<SUITE FILE_PATH="coverage/tuoheng_alg$ffmpeg22.coverage" NAME="aa 覆盖结果" MODIFIED="1667350492259" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="/opt/tuo_heng" />
<SUITE FILE_PATH="coverage/tuoheng_alg$aa.coverage" NAME="aa 覆盖结果" MODIFIED="1684461916527" 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__1_.coverage" NAME="KafkaUtils (1) Coverage Results" MODIFIED="1663464961001" 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$voddemo.coverage" NAME="voddemo 覆盖结果" MODIFIED="1681722102430" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/aliyun" />
<SUITE FILE_PATH="coverage/tuoheng_alg___$producer_start.coverage" NAME="producer_start 覆盖结果" MODIFIED="1668522825199" 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___$producer_start1.coverage" NAME="producer_start1 覆盖结果" MODIFIED="1668437822632" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="/home/thsw/chenyukun/algSch/test/kafka" />
<SUITE FILE_PATH="coverage/tuoheng_alg$re.coverage" NAME="re 覆盖结果" MODIFIED="1684221962919" 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$producer_start__1_.coverage" NAME="producer_start (1) 覆盖结果" MODIFIED="1665832569996" 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___$ffmpeg11.coverage" NAME="ffmpeg11 覆盖结果" MODIFIED="1668410004435" 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$demo.coverage" NAME="demo 覆盖结果" MODIFIED="1684810722320" 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$producer_start.coverage" NAME="producer_start1 覆盖结果" MODIFIED="1670999187123" 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$test__3_.coverage" NAME="test (3) 覆盖结果" MODIFIED="1684802056733" 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$numpy_test.coverage" NAME="numpy_test 覆盖结果" MODIFIED="1684205019028" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/numpy" />
<SUITE FILE_PATH="coverage/tuoheng_alg$.coverage" NAME="协程笔记 覆盖结果" MODIFIED="1680926972744" 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$4.coverage" NAME="视频添加图片水印4 Coverage Results" MODIFIED="1661874731395" 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$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" />
<SUITE FILE_PATH="coverage/tuoheng_alg$cv2test1__1_.coverage" NAME="cv2test1 覆盖结果" MODIFIED="1665820653649" 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$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$demo5.coverage" NAME="demo5 覆盖结果" MODIFIED="1684810002359" 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___$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$pa.coverage" NAME="pa 覆盖结果" MODIFIED="1684217734590" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/pachong" />
<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/进程" />
<SUITE FILE_PATH="coverage/tuoheng_alg$test__2_.coverage" NAME="test (2) 覆盖结果" MODIFIED="1684743889711" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/test/类型标注" />
</component>
</project>

BIN
common/__pycache__/Constant.cpython-38.pyc View File


BIN
common/__pycache__/YmlConstant.cpython-38.pyc View File


+ 5
- 4
concurrency/CommonThread.py View File

@@ -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("线程停止完成!")

+ 5
- 2
concurrency/FileUploadThread.py View File

@@ -1,4 +1,5 @@
import copy
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from threading import Thread
from loguru import logger
@@ -113,7 +114,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:
@@ -165,8 +166,10 @@ class ImageFileUpload(FileUpload):
thread_result.result()
for msg in msg_list:
self.sendResult(msg)
else:
time.sleep(1)
except Exception as e:
logger.exception("图片上传异常:{}, requestId:{}", e, self.msg.get("request_id"))
logger.exception("图片上传异常:{}, requestId:{}", str(e), self.msg.get("request_id"))
finally:
high_score_image.clear()


+ 83
- 46
concurrency/IntelligentRecognitionProcess.py View File

@@ -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):
@@ -351,7 +361,7 @@ class OnlineIntelligentRecognitionProcess(IntelligentRecognitionProcess):
return pullProcess

def checkPullProcess(self, pullProcess):
if not pullProcess.is_alive():
if pullProcess is not None and not pullProcess.is_alive():
logger.info("拉流进程停止异常, requestId: {}", self.msg.get("request_id"))
raise Exception("拉流进程异常停止")

@@ -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,17 +422,17 @@ 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("未知拉流状态异常!")
logger.info("实时进程任务完成,requestId:{}", self.msg.get("request_id"))
except ServiceException as s:
logger.error("服务异常,异常编号:{}, 异常描述:{}, requestId: {}", s.code, s.msg, self.msg.get("request_id"))
logger.exception("服务异常,异常编号:{}, 异常描述:{}, requestId: {}", s.code, s.msg, self.msg.get("request_id"))
feedback = {"feedback": message_feedback(self.msg.get("request_id"), AnalysisStatus.FAILED.value,
self.analyse_type,
s.code,
@@ -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,9 +530,9 @@ 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():
if pullProcess is not None and not pullProcess.is_alive():
logger.info("拉流进程停止异常, requestId: {}", self.msg.get("request_id"))
raise Exception("拉流进程异常停止")
# 检查是否获取到视频信息
@@ -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:

+ 36
- 7
concurrency/PullVideoStreamProcess.py View File

@@ -48,13 +48,41 @@ class PullVideoStreamProcess(Process):
imageFileUpload = ImageFileUpload(self.fbQueue, self.content, self.msg, self.imageQueue, self.analyse_type)
imageFileUpload.setDaemon(True)
imageFileUpload.start()
return imageFileUpload
start_time = time.time()
while True:
if imageFileUpload.is_alive():
return imageFileUpload
if not imageFileUpload.is_alive():
logger.warning("图片上传线程异常等待中, requestId:{}", self.msg.get("request_id"))
if time.time() - start_time <= 3:
continue
elif int(time.time() - start_time) <= 5:
logger.warning("图片上传线程异常重启中, requestId:{}", self.msg.get("request_id"))
imageFileUpload.start()
time.sleep(1)
continue
elif int(time.time() - start_time) > 5:
raise Exception("图片上传线程启动异常")

def start_heartbeat(self):
hb = Heartbeat(self.fbQueue, self.hbQueue, self.msg.get("request_id"), self.analyse_type)
hb.setDaemon(True)
hb.start()
return hb
start_time = time.time()
while True:
if hb.is_alive():
return hb
if not hb.is_alive():
logger.warning("心跳线程异常等待中, requestId:{}", self.msg.get("request_id"))
if time.time() - start_time <= 3:
continue
elif int(time.time() - start_time) <= 5:
logger.warning("心跳线程异常重启中, requestId:{}", self.msg.get("request_id"))
hb.start()
time.sleep(1)
continue
elif int(time.time() - start_time) > 5:
raise Exception("心跳线程启动异常")

def check(self, start_time, imageFileUpload, hb):
create_task_time = time.time() - start_time
@@ -63,11 +91,11 @@ class PullVideoStreamProcess(Process):
raise ServiceException(ExceptionType.ANALYSE_TIMEOUT_EXCEPTION.value[0],
ExceptionType.ANALYSE_TIMEOUT_EXCEPTION.value[1])
# 检测图片上传线程是否正常运行
if not imageFileUpload.is_alive():
if imageFileUpload is not None and not imageFileUpload.is_alive():
logger.error("未检测到图片上传线程活动,图片上传线程可能出现异常, reuqestId:{}", self.msg.get("request_id"))
raise Exception("未检测到图片上传线程活动,图片上传线程可能出现异常!")
# 检测心跳线程是否正常运行
if not hb.is_alive():
if hb is not None and not hb.is_alive():
logger.error("未检测到心跳线程活动,心跳线程可能出现异常, reuqestId:{}", self.msg.get("request_id"))
raise Exception("未检测到心跳线程活动,心跳线程可能出现异常!")

@@ -128,7 +156,7 @@ class OnlinePullVideoStreamProcess(PullVideoStreamProcess):
raise ServiceException(ExceptionType.PULLSTREAM_TIMEOUT_EXCEPTION.value[0],
ExceptionType.PULLSTREAM_TIMEOUT_EXCEPTION.value[1])
cv2_init_num += 1
time.sleep(0.5)
time.sleep(1)
cv2tool.get_video_info()
continue
pull_stream_start_time = time.time()
@@ -144,8 +172,8 @@ class OnlinePullVideoStreamProcess(PullVideoStreamProcess):
stop_pull_stream_step = True
cv2tool.close()
continue
cv2tool.close()
init_pull_num += 1
time.sleep(0.1)
continue
init_pull_num = 1
pull_stream_read_start_time = time.time()
@@ -161,9 +189,10 @@ class OnlinePullVideoStreamProcess(PullVideoStreamProcess):
"all_frame": cv2tool.all_frames})
concurrent_frame += 1
except ServiceException as s:
logger.exception("实时拉流异常: {}, requestId:{}", s.msg, self.msg.get("request_id"))
self.sendPullQueue({"status": "1", "error": {"code": s.code, "msg": s.msg}})
except Exception as e:
logger.exception("实时拉流异常: {}, requestId:{}", e, self.msg.get("request_id"))
logger.exception("实时拉流异常: {}, requestId:{}", str(e), self.msg.get("request_id"))
self.sendPullQueue({"status": "1", "error": {"code": ExceptionType.SERVICE_INNER_EXCEPTION.value[0],
"msg": ExceptionType.SERVICE_INNER_EXCEPTION.value[1]}})
finally:

BIN
concurrency/__pycache__/CommonThread.cpython-38.pyc View File


+ 1
- 1
dsp_master.py View File

@@ -13,4 +13,4 @@ if __name__ == '__main__':
# 获取主程序执行根路径
base_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
torch.multiprocessing.set_start_method('spawn')
Dispatcher.DispatcherService(base_dir).start_service()
Dispatcher.DispatcherService(base_dir).start_service()

+ 1
- 1
enums/BaiduSdkEnum.py View File

@@ -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)


+ 3
- 1
enums/ExceptionEnum.py View File

@@ -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模型!")

@@ -61,6 +61,8 @@ class ExceptionType(Enum):

COORDINATE_ACQUISITION_FAILED = ("SP027", "飞行坐标识别异常!")

PUSH_STREAM_EXCEPTION = ("SP028", "推流异常!")

SERVICE_COMMON_EXCEPTION = ("SP997", "公共服务异常!")

NO_GPU_RESOURCES = ("SP998", "暂无GPU资源可以使用,请稍后再试!")

+ 57
- 24
service/Dispatcher.py View File

@@ -35,16 +35,16 @@ class DispatcherService:
raise Exception("cuda不在活动状态, 请检测显卡驱动是否正常!!!!")

# 初始化alg相关配置 ######################################################################
self.base_dir = base_dir # 根路径
self.context = YmlUtils.getConfigs(base_dir) # 获取alg需要使用的配置
self.context[YmlConstant.BASE_DIR] = base_dir # 将根路径设置到上下文中
self.feedbackThread = None # 初始化反馈线程对象
self.__base_dir = base_dir # 根路径
self.__context = YmlUtils.getConfigs(base_dir) # 获取alg需要使用的配置
self.__context[YmlConstant.BASE_DIR] = base_dir # 将根路径设置到上下文中
self.__feedbackThread = None # 初始化反馈线程对象

# 初始化日志框架 #########################################################################
LogUtils.init_log(self.context) # 初始化日志框架
LogUtils.init_log(self.__context) # 初始化日志框架

# 初始化视频保存文件夹 #####################################################################
FileUtils.create_dir_not_exist(YmlConstant.get_file_path(self.context)) # 创建文件夹
FileUtils.create_dir_not_exist(YmlConstant.get_file_path(self.__context)) # 创建文件夹

# 创建任务记录字典 ########################################################################
self.onlineProcesses = {} # 记录当前正在执行的实时流分析任务
@@ -58,10 +58,10 @@ class DispatcherService:
self.fbQueue = Queue()

# 监听topic信息 ##########################################################################
self.online_topic = YmlConstant.get_online_tasks_topic(self.context)
self.offline_topic = YmlConstant.get_offline_tasks_topic(self.context)
self.image_topic = YmlConstant.get_image_tasks_topic(self.context)
self.recording_task_topic = YmlConstant.get_recording_tasks_topic(self.context)
self.online_topic = YmlConstant.get_online_tasks_topic(self.__context)
self.offline_topic = YmlConstant.get_offline_tasks_topic(self.__context)
self.image_topic = YmlConstant.get_image_tasks_topic(self.__context)
self.recording_task_topic = YmlConstant.get_recording_tasks_topic(self.__context)
self.topics = [self.online_topic, self.offline_topic, self.image_topic, self.recording_task_topic]
self.analysisType = {
self.online_topic: (AnalysisType.ONLINE.value, lambda x, y: self.online(x, y),
@@ -79,16 +79,16 @@ class DispatcherService:
gpu_codes = YmlConstant.GPU_CODES
gpu_array = [g for g in gpu_codes if g in gpu_name]
if len(gpu_array) > 0:
self.context[YmlConstant.GPU_NAME] = gpu_array[0]
self.__context[YmlConstant.GPU_NAME] = gpu_array[0]
if gpu_array[0] == YmlConstant.GPU_2080:
self.context[YmlConstant.GPU_NAME] = YmlConstant.GPU_2080_Ti
self.__context[YmlConstant.GPU_NAME] = YmlConstant.GPU_2080_Ti
else:
raise Exception("GPU资源不在提供的模型所支持的范围内!请先提供对应的GPU模型!")

# 服务调用启动方法
def start_service(self):
# 初始化kafka监听者
customerKafkaConsumer = KafkaUtils.CustomerKafkaConsumer(self.context, topics=self.topics)
customerKafkaConsumer = KafkaUtils.CustomerKafkaConsumer(self.__context, topics=self.topics)
logger.info("(♥◠‿◠)ノ゙ DSP【算法调度服务】启动成功 ლ(´ڡ`ლ)゙")
# 循环消息处理
while True:
@@ -191,7 +191,7 @@ class DispatcherService:
if self.onlineProcesses.get(msg.get(YmlConstant.REQUEST_ID)):
logger.warning("重复任务,请稍后再试!requestId:{}", msg.get(YmlConstant.REQUEST_ID))
return
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.context, YmlConstant.MSG: msg,
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.__context, YmlConstant.MSG: msg,
YmlConstant.GPU_IDS: gpu_ids, YmlConstant.ANALYSE_TYPE: analysisType}
# 创建在线识别进程并启动
oirp = OnlineIntelligentRecognitionProcess(cfg)
@@ -222,7 +222,7 @@ class DispatcherService:
if self.offlineProcesses.get(msg.get(YmlConstant.REQUEST_ID)):
logger.warning("重复任务,请稍后再试!requestId:{}", msg.get(YmlConstant.REQUEST_ID))
return
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.context, YmlConstant.MSG: msg,
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.__context, YmlConstant.MSG: msg,
YmlConstant.GPU_IDS: gpu_ids, YmlConstant.ANALYSE_TYPE: analysisType}
ofirp = OfflineIntelligentRecognitionProcess(cfg)
ofirp.start()
@@ -242,7 +242,7 @@ class DispatcherService:
if pp is not None:
logger.warning("重复任务,请稍后再试!requestId:{}", msg.get(YmlConstant.REQUEST_ID))
return
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.context, YmlConstant.MSG: msg,
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.__context, YmlConstant.MSG: msg,
YmlConstant.GPU_IDS: gpu_ids, YmlConstant.ANALYSE_TYPE: analysisType}
# 创建在线识别进程并启动
imagep = PhotosIntelligentRecognitionProcess(cfg)
@@ -275,11 +275,44 @@ class DispatcherService:
'''

def start_feedback_thread(self):
if self.__feedbackThread is None:
self.__feedbackThread = FeedbackThread(self.fbQueue, self.__context)
self.__feedbackThread.setDaemon(True)
self.__feedbackThread.start()
start_time = time.time()
while True:
if self.__feedbackThread.is_alive():
return
if not self.__feedbackThread.is_alive():
logger.warning("反馈线程异常等待中")
if time.time() - start_time <= 3:
continue
elif int(time.time() - start_time) <= 5:
logger.warning("反馈线程异常重启中")
self.__feedbackThread.start()
time.sleep(2)
continue
elif int(time.time() - start_time) > 5:
raise Exception("反馈线程程启动异常")
# 如果反馈线程为空,启动反馈线程,如果反馈线程意外停止,再次启动反馈线程
if self.feedbackThread is None or not self.feedbackThread.is_alive():
self.feedbackThread = FeedbackThread(self.fbQueue, self.context)
self.feedbackThread.setDaemon(True)
self.feedbackThread.start()
if self.__feedbackThread is not None and not self.__feedbackThread.is_alive():
start_time_1 = time.time()
while True:
if self.__feedbackThread.is_alive():
return
if not self.__feedbackThread.is_alive():
logger.warning("反馈线程异常等待中")
if time.time() - start_time_1 <= 3:
continue
elif time.time() - start_time_1 <= 5:
logger.warning("反馈线程异常重启中")
self.__feedbackThread.start()
time.sleep(1)
continue
elif time.time() - start_time_1 > 5:
raise Exception("反馈线程程启动异常")



'''
在线分析逻辑
@@ -287,7 +320,7 @@ class DispatcherService:

def online(self, message, analysisType):
if YmlConstant.START == message.get(YmlConstant.COMMAND):
gpu_ids = GPUtils.check_gpu_resource(self.context)
gpu_ids = GPUtils.check_gpu_resource(self.__context)
self.startOnlineProcess(message, gpu_ids, analysisType)
elif YmlConstant.STOP == message.get(YmlConstant.COMMAND):
self.stopOnlineProcess(message)
@@ -296,7 +329,7 @@ class DispatcherService:

def offline(self, message, analysisType):
if YmlConstant.START == message.get(YmlConstant.COMMAND):
gpu_ids = GPUtils.check_gpu_resource(self.context)
gpu_ids = GPUtils.check_gpu_resource(self.__context)
self.startOfflineProcess(message, gpu_ids, analysisType)
elif YmlConstant.STOP == message.get(YmlConstant.COMMAND):
self.stopOfflineProcess(message)
@@ -305,7 +338,7 @@ class DispatcherService:

def image(self, message, analysisType):
if YmlConstant.START == message.get(YmlConstant.COMMAND):
gpu_ids = GPUtils.check_gpu_resource(self.context)
gpu_ids = GPUtils.check_gpu_resource(self.__context)
self.startImageProcess(message, gpu_ids, analysisType)
else:
pass
@@ -324,7 +357,7 @@ class DispatcherService:
if self.recordingProcesses.get(msg.get(YmlConstant.REQUEST_ID)):
logger.warning("重复任务,请稍后再试!requestId:{}", msg.get(YmlConstant.REQUEST_ID))
return
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.context, YmlConstant.MSG: msg,
cfg = {YmlConstant.FBQUEUE: self.fbQueue, YmlConstant.CONTEXT: self.__context, YmlConstant.MSG: msg,
YmlConstant.ANALYSE_TYPE: analysisType}
srp = ScreenRecordingProcess(cfg)
srp.start()

+ 3
- 3
test/aliyun/vod.py View File

@@ -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):

+ 6
- 6
test/aliyun/vodTest.py View File

@@ -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' # 点播服务接入地域

+ 3
- 3
test/aliyun/voddemo.py View File

@@ -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
- 3
test/aliyun/vodtest2.py View File

@@ -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' # 点播服务接入地域

+ 39
- 49
test/ffmpeg11/aa.py View File

@@ -49,9 +49,7 @@ def get_video_info(in_file):


if __name__ == '__main__':
file_path = 'https://vod.play.t-aaron.com/customerTrans/edc96ea2115a0723a003730956208134/55547af9-184f0827dae-0004-f90c-f2c-7ec68.mp4'
#file_path = 'https://vod.play.t-aaron.com/customerTrans/edc96ea2115a0723a003730956208134/40b416f7-183b57f6be0-0004-f90c-f2c-7ec68.mp4'
#file_path = 'https://vod.play.t-aaron.com/3301fc8e166f45be88f2214e7a8f4a9d/e29535365b54434d9ed2e8c3b0a175da-fba35541b31a1049ca05b145a283c33a-hd.mp4'
file_path = r'D:\shipin\777.mp4'
video_info = get_video_info(file_path)
print(json.dumps(video_info))
# total_frames = int(video_info['nb_frames'])
@@ -62,47 +60,32 @@ if __name__ == '__main__':
# image_array = numpy.asarray(bytearray(out), dtype="uint8")
# image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
# kwargs={'fflags': 'nobuffer', 'flags': 'low_delay'}
kwargs={
# "hwaccel": "nvdec",
# "vcodec": "h264_cuvid",
# "c:v": "h264_cuvid"
}
output_args = {
# "vcodec": "hevc_nvenc",
# "c:v": "hevc_nvenc",
# "preset": "fast",
}
# i = 1
# process1 = (
# ffmpeg
# .input(file_path, **kwargs)
# .output('pipe:', format='rawvideo', pix_fmt='rgb24', **output_args)
# # .global_args("-an")
# .overwrite_output()
# .run_async(pipe_stdout=True, pipe_stderr=True)
# )
width = int(video_info['width'])
height = int(video_info['height'])
width_2_1 = int(width/2)
height_2_1 = int(height/2)
print("长:", width, "宽:", height)
text = ''
command = ['ffmpeg',
# '-hwaccel', 'cuvid',
'-c:v', 'h264_cuvid',
# '-hwaccel_output_format', 'cuda',
'-resize', '%sx%s' % (width_2_1, height_2_1),
# '-resize', '%sx%s' % (width_2_1, height_2_1),
'-i', file_path,
# '-c:v', 'hevc_nvenc',
# '-pix_fmt', 'yuv420p',
'-f', 'rawvideo',
# '-pix_fmt', 'bgr24',
'-pix_fmt', 'bgr24',
'-an',
'-']
p = sp.Popen(command, stdout=sp.PIPE)
# ai_video_file = cv2.VideoWriter(r"C:\Users\chenyukun\Desktop\shipin\aa.mp4", cv2.VideoWriter_fourcc(*'mp4v'), 30,
# (width, height))
# for line in p.stderr:
# # print(line.strip())
# for line in p.stdout:
# print(text)

command1 = ['ffmpeg',
'-report',
# '-loglevel', 'debug',
'-y', # 不经过确认,输出时直接覆盖同名文件。
'-f', 'rawvideo',
@@ -112,7 +95,7 @@ if __name__ == '__main__':
# '-c:v', 'h264_cuvid',
# '-hwaccel_output_format', 'cuda',
# '-s', "{}x{}".format(self.width * 2, self.height),
'-s', "{}x{}".format(width_2_1, height_2_1),
'-s', "{}x{}".format(width, height),
'-r', str(25),
'-i', '-', # 指定输入文件
'-g', str(5),
@@ -141,30 +124,37 @@ if __name__ == '__main__':
'rtmp://live.push.t-aaron.com/live/THSAr']

# # 管道配置
p1 = sp.Popen(command1, stdin=sp.PIPE)
p1 = sp.Popen(command1, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE, )
start1 = time.time()
num = 0
while True:
num += 1
# if num ==100:
# print(time.time()-start1)
# break
start = time.time()
in_bytes = p.stdout.read(int(width * height*3//8))
# print(type(in_bytes))
# img = (np.frombuffer(in_bytes, np.uint8)).reshape((height*3//4, width//2))
# result = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12)
# result = cv2.resize(result, (int(width / 2), int(height / 2)), interpolation=cv2.INTER_LINEAR)
# result = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
if not in_bytes:
print(in_bytes)
# ai_video_file.release()
p.stdout.close()
p.wait()
break
print(num)
time.sleep(0.001)
# p1.stdin.write(result.tostring())

# num += 1
# # if num ==100:
# # print(time.time()-start1)
# # break
# start = time.time()
in_bytes = p.stdout.read(int(width * height*3))
# # print(type(in_bytes))
img = (np.frombuffer(in_bytes, np.uint8)).reshape((height, width, 3))
# # result = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12)
# # result = cv2.resize(result, (int(width / 2), int(height / 2)), interpolation=cv2.INTER_LINEAR)
# # result = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)

for line in iter(p1.stdout.readline, b''):
print(line)
# if in_bytes:
# p1.stdin.write(img.tostring())
# print(p1.stderr.read().decode('utf-8'))
# for line in iter(p1.std
#
#
# out.readline, ''):
# line = line.decode('utf-8')
# print(line)
# print(num)
# time.sleep(0.001)

# p1.stdin.write(in_frame.tostring())
# frame
# .astype(np.uint8)
@@ -182,4 +172,4 @@ if __name__ == '__main__':
# break
# cv2.imshow('frame', frame)
# time.sleep(1111)
p.kill()
# p.kill()

+ 0
- 0
test/numpy/__init__.py View File


+ 108
- 0
test/numpy/numpy_test.py View File

@@ -0,0 +1,108 @@


import numpy as np
# # 使用标量类型
# dt = np.dtype(np.int32)
# print(dt)
#
# # int8, int16, int32, int64 四种数据类型可以使用字符串 'i1', 'i2','i4','i8' 代替
# dt = np.dtype('i4')
# print(dt)
#
# # 字节顺序标注
# dt = np.dtype('<i4')
# print(dt)
#
# dt = np.dtype([('age', np.int8)])
# print(dt)
#
# dt = np.dtype([('age', np.int8)])
# a = np.array([(10,), (20,), (30,)], dtype=dt)
# print(a)
# print(a['age'])
#
# student = np.dtype([('name', 'S20'), ('age', 'i1'), ('marks', 'f4')])
# print(student)
#
# a = np.array([('abc', 21, 50), ('xyz', 18, 75)], dtype=student)
# print(a)
#
# a = np.arange(32)
# b = a.reshape(2, 4, 4)
# print(b.ndim)
# print(b.shape)
#
# a = np.array([[1, 2, 3], [4, 5, 6]])
# a.shape = (3, 2)
# print(a)
#
# a = np.array([[1, 2, 3], [4, 5, 6]])
# b = a.reshape(3, 2)
# print(b)
# x = np.array([1, 2, 3, 4, 5], dtype=np.int8)
# print(x.itemsize)
# y = np.array([1, 2, 3, 4, 5], dtype=np.float64)
# print(y.itemsize)

# x = np.empty([3, 2], dtype=int)
# print(x)
#
# # 默认为浮点数
# x = np.zeros(5)
# print(x)
# # 设置类型为整数
# y = np.zeros((5,), dtype=int)
# print(y)
#
# # 自定义类型
# z = np.zeros((2, 2), dtype=[('x', 'i4'), ('y', 'i4')])
# print(z)
#
# # 默认为浮点数
# x = np.ones(5)
# print(x)
#
# x = np.ones([2, 2], dtype=int)
# print(x)
#
# s = b'Hello World'
# a = np.frombuffer(s, dtype='S1')
# print(a)
#
# arr1 = np.array([1, 2, 3, 4, 5])
# arr2 = np.frombuffer(arr1, dtype=int)
# print(arr2) # [1 2 3 4 5]
#
#
# list=range(5)
# it=iter(list)
# a = [1,2,3,4,5]
# # 使用迭代器创建 ndarray
# x=np.fromiter(arr1, dtype=float)
# print(x)
# aa = [
# [1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]
# ]
# a = np.array(aa)
# # b = a[1:3, 1:3]
# # c = a[1:3, [1, 2]]
# d = a[...,1:]
# # print(b)
# # print(c)
# print(d)

# a = np.array([[0, 0, 0],
# [10, 10, 10],
# [20, 20, 20],
# [30, 30, 30]])
# b = np.array([1, 2, 3])
# bb = np.tile(b, (4, 1)) # 重复 b 的各个维度
# print(bb)
# # print(a + bb)

a = np.arange(6).reshape(2, 3)
print(a)
print("========================")
print(a.T)

+ 0
- 0
test/opencv/__init__.py View File


+ 198
- 0
test/opencv/test.py View File

@@ -0,0 +1,198 @@

"""
1.色彩空间:[BGR]
(1) HSV: 更类似于人类感觉颜色的方式。H:色相(Hue) S: 饱和度(Saturation0) V: 亮度(Value)
(2) YUV: Y: 亮度信号 U/V: 两个色彩信号 -> 色彩的饱和度
(3) Lab: 有国际照明委员会建立。 L: 整张图到的明亮度 a\b: 负责颜色的多少
"""
import cv2
import numpy as np
from matplotlib import pyplot as plt

# img = cv2.imread(r'C:\Users\chenyukun\Pictures\R.png')
# cv2.imshow("img", img)

# HSV
# hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# cv2.imshow("hsv", hsv)

# YUV
# yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
# cv2.imshow("yuv", yuv)

# LAB
# lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab)
# cv2.imshow("lab", lab)

# 分离颜色通道
# print(img)
# b, g, r = cv2.split(img)
# h, w = np.shape(b)
# # 建立空白数组
# hest = np.zeros([256], dtype=np.int32)
# print(hest)
# # 遍历图片
# for i in [b, g, r]:
# for row in range(h):
# for col in range(w):
# pv = i[row, col]
# hest[pv] += 1
# plt.plot(hest, color='r')
# plt.show()
# cv2.waitKey(100000)

# array1 = np.random.randint(0, 255, (300, 150, 3), dtype=np.uint8)
# array2 = np.random.randint(200, 255, (300, 150, 3), dtype=np.uint8)
# new_array = np.hstack([array1, array2])
# cv2.imshow("new_array", new_array)
# cv2.waitKey(100000)

# 彩色图片 -> BGR
# 查看B
# img_B = img.copy()
# img_B[:, :, 1] = 0
# img_B[:, :, 2] = 0
# cv2.imshow("img_B", img_B)
# cv2.waitKey(100000)

# 查看G
# img_G = img.copy()
# img_G[:, :, 0] = 0
# img_G[:, :, 2] = 0
# cv2.imshow("img_G", img_G)
# cv2.waitKey(100000)

# 查看G
# img_R = img.copy()
# img_R[:, :, 0] = 0
# img_R[:, :, 1] = 0
# cv2.imshow("img_R", img_R)
# cv2.waitKey(100000)

# 深入研究图片的读取
"""
cv2.imread(path, way)
0: 读取灰度图片
1: 读取彩色图片
-1: 读取图片,加载Alpha通道
Alpha通道:
指一张图片的透明与不透明度
"""
img_color = cv2.imread(r'C:\Users\chenyukun\Pictures\R.png')

img_gray = cv2.imread(r'C:\Users\chenyukun\Pictures\R.png', 0)

img_transparent = cv2.imread(r'C:\Users\chenyukun\Pictures\R.png', -1)

"""
彩色图像:三维 B G R -> (373*490)
灰度图像:二维:-> (373*490)
推断出的公式: Y = (B+G+R)/3
官方给出的公式: Y = 0.299R + 0.587G + 0.114B
cv.imshow() -> 不仅仅是array()必须是uint8这种类型的。
"""

# 三通道分离
# b, g, r = cv2.split(img_color)
# new_img = (b+g+r)/3
# new_img = 0.299*r + 0.587*g + 0.114*b
# new_img = new_img.astype('uint8')
# cv2.imshow('new_img', new_img)

# 官方的填充方式: 以初始的灰度二维矩阵进行RGB通道的填充
# new_image = cv2.merge([img_gray,img_gray,img_gray])
# cv2.imshow('new_img', new_image)
# cv2.waitKey(100000)

# 图像的加法 -> 矩阵

# 加法 【前提:必须是一样大小的矩阵:对应的元素求和】
"""
像素点: [0,255]超过255怎么办?【取余】
150+150=300
300%255
"""

"""
图片融合
addWeighted(src1, alpha, src2, beta, gamma)
src1\src2: 第一张和第二张图片
alpha\beta: 第一张图片的权重、第二张图片的权重
gamma: 亮度的调节
"""
# result = cv2.addWeighted(img_color, 0.6, img_color, 0.3, 0)
# cv2.imshow('new_img', result)
# cv2.waitKey(100000)
#
# # 图像的类型转换
# # 原始图片BGR -> RGB
# cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB)

"""
1.沿着x轴进行翻转
2.沿着y轴进行翻转
3.同时沿着x轴和y进行翻转
cv2.flip(src, flipCode)
src: 源图像
flipCode: 翻转形式
0:沿着x轴进行翻转
1(大于等于1): 沿着y轴进行翻转
-1(小于等于-1): 同时沿着x轴和y进行翻转
"""

"""
图像阈值化处理
ret, dst = cv2.threshold(src, thresh, maxval, type)
ret: 阈值返回值(阈值设定的是多少)
dst: 输出的图像
src: 源图像 需要阈值化处理的图像
thresh: 人为指定的阈值
maxval: 当像素超过了阈值,(小于等于阈值)所赋予的值,否则取0
type:
(1) cv2.THRESH_BINARY: 当像素点大于阈值时,取指定255,小于等于阈值时,取0
(2) cv2.THRESH_BINARY_INV: 当像素点大于阈值时,取0。小于等于阈值时,取255
(3) cv2.THRESH_TRUNC: 超过阈值取阈值,低于阈值取自身
(4) cv2.THRESH_TOZERO: 超过阈值不变,低于阈值取0
(5) cv2.THRESH_TOZERO_INV: 超过阈值变为0, 低于阈值不变
注意:当阈值处理彩色图像时,出现粉色等颜色的原因在于: BGR三通道的叠加
"""
# ret, dst = cv2.threshold(img_color, 127, 255, cv2.THRESH_BINARY)
# cv2.imshow('new_img', dst)
# cv2.waitKey(100000)

"""
均值滤波
cv2.blur(src, kernel)
src: 源图像
kernel: 大小(选择多大的矩阵进行平移[3*3最常见])
"""
# new_img = cv2.blur(img_color, (3, 3))
# cv2.imshow('new_img', new_img)
# cv2.waitKey(100000)
"""
方框滤波
cv2.boxFilter(src,depth, ksize, normalize)
src: 源图像
depth: 图像的深度 填-1就ok, 表示与源图像深度相同
ksize: 核大小(3*3)(5*5)
normalize: 是否进行归一化
0:false 不进行归一下(求和,像素点溢出,超过255取255)
1:True 进行归一化,也就是均值滤波
"""
"""
高斯滤波【考虑了权重问题】
cv2.GaussianBlur(src, ksize, sigmaX, sigmaY)
src:源图像
ksize: (3*3)(5*5) 必须为奇数
sigmaX, sigmaY: 高斯核函数在x或y方向上的标准偏差【控制权重】
sigmaX = 0, sigmaX = 0.3 * ((ksize -1) * 0.5 -1) + 0.8
"""
'''
中值滤波
cv2.medianBlur(src, ksize)
src: 源图像
ksize:核大小, 只能传递int(1,3,5,7,9)【(3, 3)(5, 5)】
'''
new_img = cv2.medianBlur(img_color, 5)
cv2.imshow('new_img', new_img)
cv2.waitKey(100000)

+ 0
- 0
test/pachong/__init__.py View File


+ 24
- 0
test/pachong/pa.py View File

@@ -0,0 +1,24 @@
import requests
# url = 'https://www.baidu.com'
# response = requests.get(url)
# print(response)
# print(type(response))
# # 返回的页面是requests库猜测的编码方式,有误
# print(response.text)
# content = response.content.decode('utf-8')
# print(content)

url = 'https://www.baidu.com/s'
kw = {"wd": "猫"}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
}
response = requests.get(url, params=kw, headers=headers)
# 打印requests库猜测的解码方式
print(response.encoding)
content = response.content.decode('utf-8')
print(content)
# 查看状态码
print(response.status_code)



+ 0
- 0
test/元类/__init__.py View File


+ 121
- 0
test/元类/demo1.py View File

@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
class Test:
pass

print(type(Test))
print(type(int))

# 结果都为<class 'type'>,type就是内置的元类,class关键字定义的所有的类以及内置的类都是由元类type实例化产生。

"""
由于在python3中没有经典类和新式类的区别,因此python3中object是所有类的基类,那内置元类type又是什么?
type是object类的类型,总结来说就是type是object的类型,同时,object又是type的基类,这句话看起来就有问题,
到底是先有type还是先有object呢?这个问题有点类似于先有鸡还是先有蛋,我们可以通过代码简单分析一下
在python3中类和类型是同一种东西,因此对象.__class__和type(对象)得到的结果是一致的,object的基类为空,
但是type的基类为object,但是object的类型又是type。
"""
print(type(object))
print(object.__class__)

print(type(type))
print(type.__class__)

print(object.__bases__)
print(type.__bases__)

# python字符串
strs = """
global name
global age
name = 'py'
age = 18
addr = 'xx'
"""
# 定义全局作用域中的名字和值
globals = {
'a': '1',
'b': '2'
}
# 定义局部作用域中的名字和值
locals = {
'x': 3,
'y': 4
}
exec(strs, globals, locals)
print(globals['name'])
print(locals)

# 了解了exec的作用之后,就可以分析class关键字如何借助type元类产生类的步骤:
# 1 定义类名
class_name = 'Test'

# 2 定义类的基类(父类)
class_bases = (object,)

# 3 执行类体代码拿到类的名称空间
class_dic = {}

# 4 定义类体代码(本质是字符串)
class_body = """
def __init__(self,name,age):
self.name=name
self.age=age

def test(self):
print('%s:%s' %(self.name,self.name))
"""
# 5 将字符串转为python能识别的语法:将class_body运行时产生的名字存入class_dic中
exec(class_body, {}, class_dic)
# 查看类的名称空间
print(class_dic)
# 6 调用元类产生类
Test = type(class_name, class_bases, class_dic)
# 7 调用类产生对象
t = Test('python', '12')
t.test()

# 自定义元类
class MyMeta(type): # 自定义元类必须继承type,否则就是普通的类

'''
早于__init__方法执行,必须返回空对象,由于该方法是调用类后第一个运行的方法,
此时并没有对象产生,因此该方法的第一个参数必须是类本身(MyMeta),*args, **kwargs
用来接收调用元类产生对象所需的参数(类名 类的基类 名称空间)
'''
def __new__(cls, *args, **kwargs):
return type.__new__(cls, *args, **kwargs) # 直接调用父类type中的__new__方法

'''
通过__init__控制类的产生,调用自定义元类与调用内置元类type的方式相同,
需要传入类名、类的父类们、类体代码的名称空间,__init__方法中第一个参数来自于__new__方法产生的空对象。
'''
def __init__(self, class_name, class_bases, class_dict):

'''
在该方法内可以控制类的产生
'''
if not class_name.istitle(): # 实现类名首字母必须大写,否则抛出异常
raise NameError('类名的首字母必须大写')

if '__doc__' not in class_dict or len(class_dict['__doc__'].strip())==0: # 实现类必须有文档注释,否则抛出异常
raise TypeError('必须有文档注释')

def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
return 'test'

class Test(metaclass=MyMeta):

'''
我是文档注释
'''

def __init__(self):
self.name = 'python'

def test(self):
print('test')
t = Test()

+ 20
- 18
test/序列化/Test.py View File

@@ -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)

+ 5
- 0
test/日志/test.py View File

@@ -0,0 +1,5 @@


from os import environ

print("LOGURU_TRACE_NO" not in environ)

+ 0
- 0
test/正则/__init__.py View File


BIN
test/正则/__pycache__/re.cpython-38.pyc View File


+ 145
- 0
test/正则/re.py View File

@@ -0,0 +1,145 @@
import re
'''
1.匹配某个字符串
match():只能匹配某个
'''
# text = 'python python'
# result = re.match('py', text)
# print(result)
# print(result.group())

'''
2.点
匹配任意的某个字符【无法匹配换行符】【必须从开头开始匹配】
'''
# text = 'python'
# result = re.match('.', text)
# print(result)
# print(result.group())

'''
3.\d
匹配任意的数字【除了数字外均无法匹配】
'''
# text = '1python'
# result = re.match('\d', text)
# print(result)
# print(result.group())

'''
4.\D
除了数字外均可匹配【数字均无法匹配】
'''
# text = 'python'
# result = re.match('\D', text)
# print(result)
# print(result.group())

'''
5.\s
匹配空白字符【\n、 \t、 \r 、空格】
'''
# text = '\npython'
# result = re.match('\s', text)
# print(result)
# print(result.group())

'''
6.\w
匹配小写的a-z,大写的A-Z,数字和下划线
'''
# text = 'python'
# result = re.match('\w', text)
# print(result)
# print(result.group())

'''
7.\W
匹配除小写\w之外的所有字符
'''
# text = '\npython'
# result = re.match('\W', text)
# print(result)
# print(result.group())

'''
8.[] ->> 组合的方式
只要在中括号内的内容均可匹配
'''
# text = '\npython'
# result = re.match('[\n_]', text)
# print(result)
# print(result.group())

'''
9.星号 *
匹配零个或者多个字符
'''
# text = '139-1234-5678'
# result = re.match('[-\d]*', text)
# print(result)
# print(result.group())

'''
10.加号 +
匹配1个或者多个字符
'''
# text = '139-1234-5678'
# result = re.match('[-\d]+', text)
# print(result)
# print(result.group())

'''
11.问号 ?
匹配0个或者匹配1个
'''
# text = '139-1234-5678'
# result = re.match('[-\d]?', text)
# print(result)
# print(result.group())

'''
12. {m,n} 匹配m到n个
匹配0个或者匹配1个
'''
# text = '139-1234-5678'
# result = re.match('[-\d]{1,5}', text)
# print(result)
# print(result.group())

'''
13. 匹配所有的数字
'''
# text = '139-1234-5678'
# result = re.match('[-0-9]*', text)
# print(result)
# print(result.group())

'''
14. 匹配所有的非数字
'''
# text = '139-1234-5678'
# result = re.match('[^0-9]*', text)
# print(result)
# print(result.group())

# re.match() 必须从字符串开头进行匹配
# re.search() 从左到右进行字符串的遍历,找到就返回
# text = 'aapython'
# result = re.match('py', text)
# print(result)
# print(result.group())

# text = 'aapython'
# result = re.search('py', text)
# print(result)
# print(result.group())

"""
1. 贪婪模式: 正则表达式会尽可能多地匹配字符【默认就是贪婪模式】
2. 非贪婪模式: 正则表达式会尽可能少地匹配字符【?】

转义字符 \
"""



+ 0
- 0
test/类型标注/__init__.py View File


+ 18
- 0
test/类型标注/test.py View File

@@ -0,0 +1,18 @@

from typing import Optional, Union, Any, Set


a: int = 8
b: bool = True
c: str = 'ok'
d: Optional[Union[int, float]] = None
e: float = 9.8
f: bytes = b'32'

d = 5
d = 9.8
d = None

s: Set[int] = {1, 2, '3'}
for ss in s:
print(ss)

+ 19
- 15
test/线程/Test.py View File

@@ -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,25 @@ 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")

dsp算法交互 V 2.7.4转测
1. 修复算法交互分析后上传大视频失败问题bug
2. 修改百度图片子线程异常信息父级线程不打印问题bug

+ 0
- 0
test/设计模式/__init__.py View File


+ 0
- 0
test/设计模式/单例/__init__.py View File


BIN
test/设计模式/单例/__pycache__/single.cpython-38.pyc View File


+ 24
- 0
test/设计模式/单例/demo2.py View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from threading import RLock

class SingletonType(type):
single_lock = RLock()

def __call__(cls, *args, **kwargs): # 创建cls的对象时候调用
with SingletonType.single_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) # 创建cls的对象

return cls._instance


class Singleton(metaclass=SingletonType):
def __init__(self, name):
self.name = name


single_1 = Singleton('第1次创建')
single_2 = Singleton('第2次创建')

print(single_1.name, single_2.name) # 第1次创建 第1次创建
print(single_1 is single_2)

+ 28
- 0
test/设计模式/单例/demo3.py View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from threading import RLock
single_lock = RLock()

def Singleton(cls):
instance = {}

def _singleton_wrapper(*args, **kargs):
with single_lock:
if cls not in instance:
instance[cls] = cls(*args, **kargs)
return instance[cls]

return _singleton_wrapper


@Singleton
class SingletonTest(object):
def __init__(self, name):
self.name = name


slt_1 = SingletonTest('第1次创建')
print(slt_1.name)
slt_2 = SingletonTest('第2次创建')
print(slt_1.name, slt_2.name)

print(slt_1 is slt_2)

+ 18
- 0
test/设计模式/单例/demo4.py View File

@@ -0,0 +1,18 @@
from threading import RLock
class Singleton(object):
single_lock = RLock()

def __init__(self, name):
self.name = name

@classmethod
def instance(cls, *args, **kwargs):
with Singleton.single_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance

single_1 = Singleton.instance('第1次创建')
single_2 = Singleton.instance('第2次创建')

print(single_1 is single_2) # True

+ 22
- 0
test/设计模式/单例/demo5.py View File

@@ -0,0 +1,22 @@
from threading import RLock

class Singleton(object):
single_lock = RLock()

def __init__(self, name):
if hasattr(self, 'name'):
return
self.name = name

def __new__(cls, *args, **kwargs):
with Singleton.single_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)

return Singleton._instance

single_1 = Singleton('第1次创建')
single_2 = Singleton('第2次创建')

print(single_1.name, single_2.name) # 第2次创建 第2次创建
print(single_1 is single_2) # True

+ 15
- 0
test/设计模式/单例/single.py View File

@@ -0,0 +1,15 @@


class DbSingleton():
def __init__(self, host, port, username, password):
self.host = host
self.port = port
self.username = username
self.password = password
self.pool = None # 连接池

def connect(self):
print("建立连接")


db_singleton = DbSingleton('host', 'port', 'username', 'password')

+ 7
- 0
test/设计模式/单例/test.py View File

@@ -0,0 +1,7 @@

from single import db_singleton

if __name__ == '__main__':
print(id(db_singleton))
print(id(db_singleton))
print(id(db_singleton))

+ 0
- 0
test/设计模式/简单工厂模式/__init__.py View File


+ 36
- 0
test/设计模式/简单工厂模式/demo.py View File

@@ -0,0 +1,36 @@
from abc import ABC, abstractmethod


class Fruit(ABC):
def __init__(self, name):
self.name = name

@abstractmethod
def make_juice(self):
pass


class Apple(Fruit):
def make_juice(self):
print(f"制作{self.name}汁")


class Grape(Fruit):
def make_juice(self):
print(f"制作{self.name}酒")


class FruitFactory():
@classmethod
def create_fruit(cls, name):
if name == 'apple':
return Apple('苹果')
elif name == 'grape':
return Grape("葡萄")


fruit1 = FruitFactory.create_fruit('apple')
fruit1.make_juice()

fruit2 = FruitFactory.create_fruit('grape')
fruit2.make_juice()

+ 44
- 0
test/设计模式/简单工厂模式/demo1.py View File

@@ -0,0 +1,44 @@
from abc import ABC, abstractmethod


class Fruit(ABC):
def __init__(self, name):
self.name = name

@abstractmethod
def make_juice(self):
pass


class Apple(Fruit):
def make_juice(self):
print(f"制作{self.name}汁")


class Grape(Fruit):
def make_juice(self):
print(f"制作{self.name}酒")


class AbcFruitFactory(ABC):
@abstractmethod
def create_fruit(self):
pass


class AppleFactory(AbcFruitFactory):
def create_fruit(self):
return Apple('苹果')


class OrangeFactory(AbcFruitFactory):
def create_fruit(self):
return Grape('葡萄')


fruit1 = AppleFactory().create_fruit()
fruit1.make_juice() # 制作苹果汁


fruit2 = OrangeFactory().create_fruit()
fruit2.make_juice()

+ 48
- 29
util/AliyunSdk.py View File

@@ -1,31 +1,33 @@
# -*- 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 +35,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 +65,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 +77,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,
self.__requestId)
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 +115,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 +130,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)

+ 102
- 78
util/Cv2Utils.py View File

@@ -48,8 +48,8 @@ class Cv2Util():
self.width = width
self.height = height
if width > Constant.width:
self.h = int(self.height//2)
self.w = int(self.width//2)
self.h = int(self.height // 2)
self.w = int(self.width // 2)
else:
self.h = int(self.height)
self.w = int(self.width)
@@ -94,8 +94,8 @@ class Cv2Util():
self.height = int(height)
self.wh = self.width * self.height * 3
if width > Constant.width:
self.h = int(self.height//2)
self.w = int(self.width//2)
self.h = int(self.height // 2)
self.w = int(self.width // 2)
else:
self.h = int(self.height)
self.w = int(self.width)
@@ -106,8 +106,9 @@ class Cv2Util():
# if duration:
# self.duration = float(video_stream['duration'])
# self.bit_rate = int(bit_rate) / 1000
self.__logger.info("视频信息, width:{}|height:{}|fps:{}|all_frames:{}|bit_rate:{}, requestId:{}", self.width,
self.height, self.fps, self.all_frames, self.bit_rate, self.requestId)
self.__logger.info("视频信息, width:{}|height:{}|fps:{}|all_frames:{}|bit_rate:{}, requestId:{}",
self.width,
self.height, self.fps, self.all_frames, self.bit_rate, self.requestId)
except ServiceException as s:
self.__logger.error("获取视频信息异常: {}, requestId:{}", s.msg, self.requestId)
self.clear_video_info()
@@ -119,6 +120,7 @@ class Cv2Util():
'''
录屏任务获取视频信息
'''

def get_recording_video_info(self):
try:
video_info = 'ffprobe -show_format -show_streams -of json %s' % self.pullUrl
@@ -149,7 +151,7 @@ class Cv2Util():
up, down = str(fps).split('/')
self.fps = int(eval(up) / eval(down))
self.__logger.info("视频信息, width:{}|height:{}|fps:{}|all_frames:{}, requestId:{}", self.width,
self.height, self.fps, self.all_frames, self.requestId)
self.height, self.fps, self.all_frames, self.requestId)
except ServiceException as s:
self.__logger.error("获取视频信息异常: {}, requestId:{}", s.msg, self.requestId)
self.clear_video_info()
@@ -166,6 +168,7 @@ class Cv2Util():
'''
录屏拉取视频
'''

def recording_pull_p(self):
try:
# 如果视频信息不存在, 不初始化拉流
@@ -308,25 +311,30 @@ class Cv2Util():
return result

def close(self):
self.clear_video_info()
if self.pull_p:
if self.pull_p.stdout:
self.pull_p.stdout.close()
self.pull_p.terminate()
self.pull_p.wait()
self.pull_p = None
self.__logger.info("关闭拉流管道完成, requestId:{}", self.requestId)
if self.p:
if self.p.stdin:
self.p.stdin.close()
self.p.terminate()
self.p.wait()
self.p = None
# self.p.communicate()
# self.p.kill()
self.__logger.info("关闭管道完成, requestId:{}", self.requestId)
if self.or_video_file:
self.or_video_file.release()
self.or_video_file = None
self.__logger.info("关闭原视频写入流完成, requestId:{}", self.requestId)
if self.ai_video_file:
self.ai_video_file.release()
self.ai_video_file = None
self.__logger.info("关闭AI视频写入流完成, requestId:{}", self.requestId)

# 构建 cv2
@@ -402,7 +410,7 @@ class Cv2Util():
'-r', str(self.fps),
'-i', '-', # 指定输入文件
'-g', str(self.fps),
'-maxrate', '8000k',
'-maxrate', '6000k',
# '-profile:v', 'high',
'-b:v', '5000k',
# '-crf', '18',
@@ -423,7 +431,8 @@ class Cv2Util():
'-tune', 'll',
'-f', 'flv',
self.pushUrl]
self.__logger.info("fps:{}|height:{}|width:{}|requestId:{}", self.fps, self.height, self.width, self.requestId)
self.__logger.info("fps:{}|height:{}|width:{}|requestId:{}", self.fps, self.height, self.width,
self.requestId)
self.p = sp.Popen(command, stdin=sp.PIPE, shell=False)
except ServiceException as s:
if self.p:
@@ -442,112 +451,127 @@ class Cv2Util():
self.__logger.exception("初始化p管道异常:{}, requestId:{}", e, self.requestId)

def push_stream(self, frame):
try:
if self.p is None:
self.build_p()
self.p.stdin.write(frame.tostring())
except ServiceException as s:
raise s
except Exception as ex:
self.__logger.exception("推流进管道异常:{}, requestId: {}", ex, self.requestId)
current_retry_num = 0
while True:
try:
time.sleep(1)
self.p_push_retry_num += 1
current_retry_num += 1
if current_retry_num > 3 or self.p_push_retry_num > 600:
raise ServiceException(ExceptionType.PUSH_STREAMING_CHANNEL_IS_OCCUPIED.value[0],
ExceptionType.PUSH_STREAMING_CHANNEL_IS_OCCUPIED.value[1])
current_retry_num = 0
while True:
try:
if self.p is None:
self.build_p()
self.p.stdin.write(frame.tostring())
self.__logger.info("构建p管道重试成功, 当前重试次数: {}, requestId: {}", current_retry_num,
self.requestId)
except ServiceException as ss:
raise ss
except Exception as e:
self.__logger.exception("构建p管道异常:{}, 开始重试, 当前重试次数:{}, requestId: {}", e,
current_retry_num, self.requestId)
self.p.stdin.write(frame.tostring())
self.p_push_retry_num == 0
break
except ServiceException as s:
raise s
except Exception as ex:
if self.p:
if self.p.stdin:
self.p.stdin.close()
self.p.terminate()
self.p.wait()
self.p = None
time.sleep(0.1)
self.p_push_retry_num += 1
current_retry_num += 1
if self.p_push_retry_num > 500:
self.__logger.exception("推流进管道异常:{}, requestId: {}", ex, self.requestId)
raise ServiceException(ExceptionType.PUSH_STREAMING_CHANNEL_IS_OCCUPIED.value[0],
ExceptionType.PUSH_STREAMING_CHANNEL_IS_OCCUPIED.value[1])
if current_retry_num > 3:
self.__logger.exception("推流进管道异常:{}, requestId: {}", ex, self.requestId)
raise ServiceException(ExceptionType.PUSH_STREAM_EXCEPTION.value[0],
ExceptionType.PUSH_STREAM_EXCEPTION.value[1])


def build_or_write(self):
try:
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)

+ 26
- 0
util/ModelUtils.py View File

@@ -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],

BIN
util/__pycache__/LogUtils.cpython-38.pyc View File


+ 730
- 0
vodsdk/AliyunVodUploader.py View File

@@ -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

+ 327
- 0
vodsdk/AliyunVodUtils.py View File

@@ -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

+ 87
- 0
vodsdk/UploadAttachedMediaRequest.py View File

@@ -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



+ 84
- 0
vodsdk/UploadImageRequest.py View File

@@ -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

+ 86
- 0
vodsdk/UploadVideoRequest.py View File

@@ -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

+ 3
- 0
vodsdk/__init__.py View File

@@ -0,0 +1,3 @@
__version__ = '1.3.1'



BIN
vodsdk/__pycache__/AliyunVodUploader.cpython-38.pyc View File


BIN
vodsdk/__pycache__/AliyunVodUtils.cpython-38.pyc View File


BIN
vodsdk/__pycache__/UploadVideoRequest.cpython-38.pyc View File


BIN
vodsdk/__pycache__/__init__.cpython-38.pyc View File


Loading…
Cancel
Save