From 4a0f7b276177754d8b1a5a54b9b5f5affd448b0f Mon Sep 17 00:00:00 2001 From: thsw Date: Mon, 25 Jul 2022 11:45:17 +0800 Subject: [PATCH] SASS --- logs/send/SendPics.log | 90 +++ send.log | 62 +- voduploadsdk/AliyunVodUploader.py | 670 ++++++++++++++++++ voduploadsdk/AliyunVodUtils.py | 325 +++++++++ voduploadsdk/ChangeLog.txt | 20 + voduploadsdk/UploadAttachedMediaRequest.py | 87 +++ voduploadsdk/UploadImageRequest.py | 84 +++ voduploadsdk/UploadVideoRequest.py | 86 +++ voduploadsdk/__init__.py | 3 + .../AliyunVodUploader.cpython-38.pyc | Bin 0 -> 22885 bytes .../__pycache__/AliyunVodUtils.cpython-38.pyc | Bin 0 -> 9723 bytes .../UploadAttachedMediaRequest.cpython-38.pyc | Bin 0 -> 3591 bytes .../UploadImageRequest.cpython-38.pyc | Bin 0 -> 3308 bytes .../UploadVideoRequest.cpython-38.pyc | Bin 0 -> 3356 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 155 bytes 15 files changed, 1426 insertions(+), 1 deletion(-) create mode 100644 voduploadsdk/AliyunVodUploader.py create mode 100644 voduploadsdk/AliyunVodUtils.py create mode 100755 voduploadsdk/ChangeLog.txt create mode 100644 voduploadsdk/UploadAttachedMediaRequest.py create mode 100644 voduploadsdk/UploadImageRequest.py create mode 100644 voduploadsdk/UploadVideoRequest.py create mode 100644 voduploadsdk/__init__.py create mode 100644 voduploadsdk/__pycache__/AliyunVodUploader.cpython-38.pyc create mode 100644 voduploadsdk/__pycache__/AliyunVodUtils.cpython-38.pyc create mode 100644 voduploadsdk/__pycache__/UploadAttachedMediaRequest.cpython-38.pyc create mode 100644 voduploadsdk/__pycache__/UploadImageRequest.cpython-38.pyc create mode 100644 voduploadsdk/__pycache__/UploadVideoRequest.cpython-38.pyc create mode 100644 voduploadsdk/__pycache__/__init__.cpython-38.pyc diff --git a/logs/send/SendPics.log b/logs/send/SendPics.log index cbeb5fd..37b8c79 100644 --- a/logs/send/SendPics.log +++ b/logs/send/SendPics.log @@ -16841,3 +16841,93 @@ UnboundLocalError: local variable 'e' referenced before assignment 2022-07-25 10:12:43.114 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping 2022-07-25 10:13:02.843 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127988 s 2022-07-25 10:14:43.551 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:15:02.965 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.121396 s +2022-07-25 10:16:43.699 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:17:03.063 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.098603 s +2022-07-25 10:18:43.863 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:19:03.175 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112009 s +2022-07-25 10:20:44.084 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:21:03.287 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111967 s +2022-07-25 10:22:44.280 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:23:03.443 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.156022 s +2022-07-25 10:24:44.449 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:25:03.563 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.119975 s +2022-07-25 10:26:44.616 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:27:03.676 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112962 s +2022-07-25 10:28:44.831 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:29:03.788 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111969 s +2022-07-25 10:30:45.028 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:31:03.871 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.083104 s +2022-07-25 10:32:45.215 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:33:03.963 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.092260 s +2022-07-25 10:34:45.466 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:35:04.063 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.099702 s +2022-07-25 10:36:45.691 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:37:04.175 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112054 s +2022-07-25 10:38:45.979 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:39:04.301 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.126237 s +2022-07-25 10:40:46.190 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:41:04.423 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.121860 s +2022-07-25 10:42:46.342 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:43:04.576 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.152303 s +2022-07-25 10:44:46.598 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:45:04.675 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.099589 s +2022-07-25 10:46:46.923 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:47:04.789 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.114156 s +2022-07-25 10:48:47.090 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:49:04.891 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.101807 s +2022-07-25 10:50:47.268 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:51:05.007 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.115997 s +2022-07-25 10:52:47.427 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:53:05.119 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112000 s +2022-07-25 10:54:47.591 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:55:05.231 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112039 s +2022-07-25 10:56:47.802 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:57:05.345 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.113767 s +2022-07-25 10:58:48.083 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 10:59:05.459 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.114193 s +2022-07-25 11:00:48.328 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:01:05.565 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.105492 s +2022-07-25 11:02:48.510 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:03:05.676 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111103 s +2022-07-25 11:04:48.707 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:05:05.787 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111479 s +2022-07-25 11:06:48.895 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:07:05.899 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111978 s +2022-07-25 11:08:49.173 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:09:06.011 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111985 s +2022-07-25 11:10:49.343 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:11:06.123 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112255 s +2022-07-25 11:12:49.512 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:13:06.239 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.115715 s +2022-07-25 11:14:49.651 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:15:06.355 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.116034 s +2022-07-25 11:16:49.840 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:17:06.471 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.115963 s +2022-07-25 11:18:50.017 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:19:06.583 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112029 s +2022-07-25 11:20:50.322 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:21:06.699 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.115968 s +2022-07-25 11:22:50.491 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:23:06.803 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.104037 s +2022-07-25 11:24:50.630 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:25:06.915 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111996 s +2022-07-25 11:26:50.854 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:27:07.027 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111968 s +2022-07-25 11:28:51.015 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:29:07.143 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.116001 s +2022-07-25 11:30:51.183 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:31:07.237 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.094352 s +2022-07-25 11:32:51.342 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:33:07.323 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.085640 s +2022-07-25 11:34:51.598 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:35:07.435 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112048 s +2022-07-25 11:36:51.849 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:37:07.556 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.120592 s +2022-07-25 11:38:52.096 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:39:07.667 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111360 s +2022-07-25 11:40:52.259 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:41:07.783 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.116053 s +2022-07-25 11:42:52.434 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 11:43:07.895 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111956 s +2022-07-25 11:44:52.673 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping diff --git a/send.log b/send.log index 9134452..7d9143d 100644 --- a/send.log +++ b/send.log @@ -4525,4 +4525,64 @@ 2022-07-25 09:22:29.227 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping 2022-07-25 09:24:29.783 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping 2022-07-25 09:26:30.286 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping -2022-07-25 09:28:30.821 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping \ No newline at end of file +2022-07-25 09:28:30.821 [INFO][Send-tranfer-oss:main][343][logs.send.SendPics.log]- send main process sleeping +2022-07-25 08:48:57.599 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.123932 s +2022-07-25 08:50:57.727 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128049 s +2022-07-25 08:52:57.855 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127999 s +2022-07-25 08:54:57.983 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127999 s +2022-07-25 08:56:58.111 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128048 s +2022-07-25 08:58:58.239 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127960 s +2022-07-25 09:00:58.367 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128023 s +2022-07-25 09:02:58.495 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128010 s +2022-07-25 09:04:58.624 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128663 s +2022-07-25 09:06:58.751 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127334 s +2022-07-25 09:08:58.856 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.105123 s +2022-07-25 09:10:58.987 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.130843 s +2022-07-25 09:12:59.115 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128024 s +2022-07-25 09:14:59.243 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127798 s +2022-07-25 09:16:59.371 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128213 s +2022-07-25 09:18:59.499 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127971 s +2022-07-25 09:20:59.627 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128035 s +2022-07-25 09:22:59.755 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127983 s +2022-07-25 09:24:59.883 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127979 s +2022-07-25 09:27:00.012 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.129012 s +2022-07-25 09:29:00.139 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127028 s +2022-07-25 09:31:00.259 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.119955 s +2022-07-25 09:33:00.387 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128004 s +2022-07-25 09:35:00.515 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128230 s +2022-07-25 09:37:00.643 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127766 s +2022-07-25 09:39:00.760 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.117024 s +2022-07-25 09:41:00.863 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.102978 s +2022-07-25 09:43:00.993 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.129858 s +2022-07-25 09:45:01.119 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.126178 s +2022-07-25 09:47:01.248 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128554 s +2022-07-25 09:49:01.371 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.123453 s +2022-07-25 09:51:01.499 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127984 s +2022-07-25 09:53:01.606 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.106856 s +2022-07-25 09:55:01.731 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.125129 s +2022-07-25 09:57:01.832 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.100842 s +2022-07-25 09:59:01.937 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.105474 s +2022-07-25 10:01:02.096 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.158746 s +2022-07-25 10:03:02.209 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112861 s +2022-07-25 10:05:02.335 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.126062 s +2022-07-25 10:07:02.463 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127995 s +2022-07-25 10:09:02.587 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.124004 s +2022-07-25 10:11:02.715 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.128045 s +2022-07-25 10:13:02.843 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.127988 s +2022-07-25 10:15:02.965 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.121396 s +2022-07-25 10:17:03.063 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.098603 s +2022-07-25 10:19:03.175 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112009 s +2022-07-25 10:21:03.287 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111967 s +2022-07-25 10:23:03.443 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.156022 s +2022-07-25 10:25:03.563 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.119975 s +2022-07-25 10:27:03.676 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112962 s +2022-07-25 10:29:03.788 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.111969 s +2022-07-25 10:31:03.871 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.083104 s +2022-07-25 10:33:03.963 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.092260 s +2022-07-25 10:35:04.063 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.099702 s +2022-07-25 10:37:04.175 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.112054 s +2022-07-25 10:39:04.301 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.126237 s +2022-07-25 10:41:04.423 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.121860 s +2022-07-25 10:43:04.576 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.152303 s +2022-07-25 10:45:04.675 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.099589 s +2022-07-25 10:47:04.789 [INFO][Send-tranfer-oss:mintor-offline-ending][134][logs.send.SendPics.log]- child process sleeping:120.114156 s \ No newline at end of file diff --git a/voduploadsdk/AliyunVodUploader.py b/voduploadsdk/AliyunVodUploader.py new file mode 100644 index 0000000..c0356c9 --- /dev/null +++ b/voduploadsdk/AliyunVodUploader.py @@ -0,0 +1,670 @@ +# -*- coding: UTF-8 -*- +import json +import oss2 +import base64 +import requests +from oss2 import compat +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 voduploadsdk.AliyunVodUtils import * +from voduploadsdk.UploadVideoRequest import UploadVideoRequest + +VOD_MAX_TITLE_LENGTH = 128 +VOD_MAX_DESCRIPTION_LENGTH = 1024 + +class AliyunVodUploader: + + def __init__(self, accessKeyId, accessKeySecret, 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.__accessKeyId = accessKeyId + self.__accessKeySecret = accessKeySecret + self.__ecsRegion = ecsRegionId + self.__vodApiRegion = None + self.__connTimeout = 3 + self.__bucketClient = None + self.__maxRetryTimes = 3 + self.__vodClient = None + self.__EnableCrc = True + + # 分片上传参数 + self.__multipartThreshold = 10 * 1024 * 1024 # 分片上传的阈值,超过此值开启分片上传 + self.__multipartPartSize = 10 * 1024 * 1024 # 分片大小,单位byte + self.__multipartThreadsNum = 3 # 分片上传时并行上传的线程数,暂时为串行上传,不支持并行,后续会支持。 + + self.setApiRegion('cn-shanghai') + + + 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 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): + + if totalBytes: + rate = int(100 * (float(consumedBytes) / float(totalBytes))) + else: + rate = 0 + + print ("[%s]uploaded %s bytes, percent %s%s" % (AliyunVodUtils.getCurrentTimeStr(), consumedBytes, format(rate), '%')) + sys.stdout.flush() + + + def __initVodClient(self): + return client.AcsClient(self.__accessKeyId, self.__accessKeySecret, self.__vodApiRegion, + auto_retry=True, max_retry_time=self.__maxRetryTimes, timeout=self.__connTimeout) + + 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) + + result = self.__requestUploadInfo(request, 'video') + logger.info("CreateUploadVideo, FilePath: %s, VideoId: %s" % (uploadVideoRequest.filePath, result['VideoId'])) + return result + + # 刷新上传凭证 + def __refresh_upload_video(self, videoId): + request = RefreshUploadVideoRequest.RefreshUploadVideoRequest(); + request.set_VideoId(videoId) + + result = self.__requestUploadInfo(request, 'video') + logger.info("RefreshUploadVideo, VideoId %s" % (result['VideoId'])) + 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 + + + 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) + 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 %s Finish, MediaId: %s, FilePath: %s, Destination: %s/%s" % ( + uploadInfo['MediaType'], uploadInfo['MediaId'], filePath, bucketHost, object)) + 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): + 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 + + 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) + 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.__bucket.upload_part(self.__object, self.__uploadId, partNumber, SizedFileAdapter(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.__bucket.complete_multipart_upload(self.__object, self.__uploadId, self.__finishedParts, headers=self.__headers) + + return result + + + def __reportUploadProgress(self, uploadMethod, donePartsCount, doneBytes): + 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=1) + + + 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 diff --git a/voduploadsdk/AliyunVodUtils.py b/voduploadsdk/AliyunVodUtils.py new file mode 100644 index 0000000..5a477b9 --- /dev/null +++ b/voduploadsdk/AliyunVodUtils.py @@ -0,0 +1,325 @@ +# -*- coding: UTF-8 -*- +import os,sys +import hashlib +import datetime +import functools +import logging +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) + +logger = AliyunVodLog + + +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: + # 可能原因:AK错误、账号无权限、参数错误等 + raise AliyunVodException('ServerException', e.get_error_code(), e.get_error_msg(), e.get_http_status(), e.get_request_id()) + logger.error("ServerException: %s", e) + except ClientException as e: + # 可能原因:本地网络故障(如不能连接外网)等 + raise AliyunVodException('ClientException', e.get_error_code(), e.get_error_msg()) + logger.error("ClientException: %s", e) + except OssError as e: + # 可能原因:上传凭证过期等 + raise AliyunVodException('OssError', e.code, e.message, e.status, e.request_id) + logger.error("OssError: %s", e) + except IOError as e: + # 可能原因:文件URL不能访问、本地文件无法读取等 + raise AliyunVodException('IOError', repr(e), traceback.format_exc()) + logger.error("IOError: %s", traceback.format_exc()) + except OSError as e: + # 可能原因:本地文件不存在等 + raise AliyunVodException('OSError', repr(e), traceback.format_exc()) + logger.error("OSError: %s", traceback.format_exc()) + except AliyunVodException as e: + # 可能原因:参数错误 + raise e + logger.error("VodException: %s", e) + except Exception as e: + raise AliyunVodException('UnkownException', repr(e), traceback.format_exc()) + logger.error("UnkownException: %s", traceback.format_exc()) + except: + raise AliyunVodException('UnkownError', 'UnkownError', traceback.format_exc()) + logger.error("UnkownError: %s", 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 + diff --git a/voduploadsdk/ChangeLog.txt b/voduploadsdk/ChangeLog.txt new file mode 100755 index 0000000..084c65b --- /dev/null +++ b/voduploadsdk/ChangeLog.txt @@ -0,0 +1,20 @@ +2019-04-12 Version: 1.3.1 +1. 上传时可指定应用ID,以实现多应用体系的资源隔离 +2. 支持上传时指定工作流ID,可自动化媒体处理 + +2019-02-12 Version: 1.3.0 +1. 可指定点播中心(默认为上海)和存储区域,便于海外上传 +2. 支持辅助媒资(水印、字幕文件等)的上传 +3. 支持上传时设置UserData等个性化配置和更多元数据 +4. 上传网络文件调整为先下载到本地,再上传到点播,以支持大文件上传(最大48.8TB) +5. 改进m3u8视频的上传,提供默认解析m3u8分片信息的接口,也可自定义分片列表 + +2018-07-05 Version: 1.2.1 +1. 支持设置视频存储区域UploadVideoRequest.setStorageLocation +2. 修复上传大文件时上传凭证过期未刷新的问题 + +2017-12-21 Version: 1.1.1 +1. 支持上传本地单个视频、m3u8视频(含ts分片)、图片文件到点播 +2. 支持上传网络上的(HTTP/HTTPS链接,含OSS链接)的单个视频、m3u8视频(含ts分片)、图片文件到点播. +3. 支持Python2、Python3版本 + diff --git a/voduploadsdk/UploadAttachedMediaRequest.py b/voduploadsdk/UploadAttachedMediaRequest.py new file mode 100644 index 0000000..a09cfe9 --- /dev/null +++ b/voduploadsdk/UploadAttachedMediaRequest.py @@ -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 voduploadsdk.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 + + diff --git a/voduploadsdk/UploadImageRequest.py b/voduploadsdk/UploadImageRequest.py new file mode 100644 index 0000000..73f6baa --- /dev/null +++ b/voduploadsdk/UploadImageRequest.py @@ -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 voduploadsdk.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 diff --git a/voduploadsdk/UploadVideoRequest.py b/voduploadsdk/UploadVideoRequest.py new file mode 100644 index 0000000..afe3b35 --- /dev/null +++ b/voduploadsdk/UploadVideoRequest.py @@ -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 voduploadsdk.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 diff --git a/voduploadsdk/__init__.py b/voduploadsdk/__init__.py new file mode 100644 index 0000000..7bd766b --- /dev/null +++ b/voduploadsdk/__init__.py @@ -0,0 +1,3 @@ +__version__ = '1.3.1' + + diff --git a/voduploadsdk/__pycache__/AliyunVodUploader.cpython-38.pyc b/voduploadsdk/__pycache__/AliyunVodUploader.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..946f397263bb256833422fe3a9245bffcf14992c GIT binary patch literal 22885 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 literal 0 HcmV?d00001 diff --git a/voduploadsdk/__pycache__/AliyunVodUtils.cpython-38.pyc b/voduploadsdk/__pycache__/AliyunVodUtils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7e2315fafc6b6d362354b51c42c964b656a4aa7 GIT binary patch literal 9723 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 literal 0 HcmV?d00001 diff --git a/voduploadsdk/__pycache__/UploadAttachedMediaRequest.cpython-38.pyc b/voduploadsdk/__pycache__/UploadAttachedMediaRequest.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ff2ce9c8048c7f43e8601b6d3e658be32044404 GIT binary patch literal 3591 zcmb7HTW=dh6y9Cm)=uK2=_S1ZLs~&yrS?O)v{415H;8Dh(k4Z+ke1EPBwn)Kb!Wy& z6S)$ish|>-iUbuxMM|Df#Y4qQ5ek<-!PCA%lJ<#ro;YV5J6nsm^;&c0%+5J8-<&y@ z^<-aPMuX?nor_a{0{0gVqE8G4??4kT0-;(-qdGM_eL{OzqcIv^(rEmqQ8H-aik3^- zKj@m4A&1Br&lZAAR=vQcV^Z2qnbC`ky7pyut;U1|85A4y+=ZG?t_EjcZAz1-I8pLP zM9nD^TabBMGG4Ly?1-pMNmqJ|OuHTvIkLGVad6KOGGBH;lD-09`kpZbG zIK=iz1)(T?;`%TH0P=`uQlO|4)I18haDt~7Xu!Q1(8j?S9|3j8t5MgVA?E=Nxn#@o z$ONJrVbaNyIoJmF+ujhcfRMKDFtz{rfRjqV8KF$tt_P>7ddwD#fc>0V64WG-WrlFc z*rjno1E*GDz8ocGDXU_%P$)C6nzxlz^G;AHG$pLYf`yYOUVG!sd|6h!%oDgzEvJY2 zQ3tu`)4KC2yk%NRhkG~ZernLzl3t3@12j$(z{P2jrk1o)f|@i9eUfHq5A-S8OZ%WV zX_odwpQZzJ2lN>_NOwZtLwC{L(D%|kbO`!Bx|i;QK1+4*qJ!c7?d?@`5)q`xV><&) zd<>*+)HMrS6sWGKp=eCecwJjg)b)C-9$$&+4XLD>NhxY7npQNUXpf@3iuNgRM(q)7 zlOdar6TtQHnj_)jrlD;QyrnX#d`dYVddnz*1;Bqs$ojp{|6YBx@!itK>i268e|h}# z!?oX+{`vLG^)K!{{`A(yZ(nV!-fziFUcLZRYk%B&va-5%_s8}7x7Tk!So`UlwYzt= zcH&IdxZfHVbJ?(WswP~Y2{ExyWub{7geV$Z5rQQ&RdAfUF2jThv#>`niO#(xcg0;~ zVbXz!9H(Ji+A|{Tp-ed3tx7lWL$d~Pbk>%3*e}2`_6)lafHY1e>}nO1GV=kSo%VwH zahgknalyQ4ju*+}NK7DM3b6r<2O8f6<9}xg<)Fd}vMlBcwr_*wseQb_{5err2)tnK z^@8V46{-ue9QeocCkn!q>{!)yW?_3#m>te*+ zjC}?!y9~LuF$1n02T5?`6?F3?@Tt1?NpeO1$hZ-|k*I^4TV_4Ruftn&IZfmB*pRlO z3pJ+kA=nQZ${2h}1x&^t0NIQ}@pU}`bHGC+CW@u(rDh5}$9cd@1LOW2?1DHVsYYQ!($t5FIF&iR6^oTf+p7) zO+>3(Q2h~5$hU(U1W;F=E?DnJfF0`$tWC39aM=hr9xt z0}(jLx5L3uAH!PyK?KV)LS4ys3uH$G2=eVf`T)p>t*?oR2%00E?`umbXB3O7^<(k( zp&(dRn6a!1d^C8-_gdDqn(Z}a_#Q-oC4wJ7f?o*y1tj6ohE8IYRnq^rS!kYW Qoa!yC76NNa+%)z703S4vfdBvi literal 0 HcmV?d00001 diff --git a/voduploadsdk/__pycache__/UploadImageRequest.cpython-38.pyc b/voduploadsdk/__pycache__/UploadImageRequest.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47b84f96e43e2f96d859ecff329225f5cf42c9fa GIT binary patch literal 3308 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 literal 0 HcmV?d00001 diff --git a/voduploadsdk/__pycache__/__init__.cpython-38.pyc b/voduploadsdk/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e05e3d6de565aa590c4ae107452bbf018792567 GIT binary patch literal 155 zcmWIL<>g`kf)oGlCP@J4#~=lylKGT!2j zk1tCtD$dN$i;rK)P{agO2PS@L>lfwcm*{6Fre!C_SLWyBmznC9<)@SulIYq;;_jDO68>5fh_zC#0&tW-zHQ5 literal 0 HcmV?d00001