This commit is contained in:
thsw 2022-07-25 11:45:17 +08:00
parent d0148eb6a8
commit 4a0f7b2761
15 changed files with 1426 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

20
voduploadsdk/ChangeLog.txt Executable file
View File

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

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

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

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

3
voduploadsdk/__init__.py Normal file
View File

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

Binary file not shown.

Binary file not shown.