SASS
This commit is contained in:
parent
d0148eb6a8
commit
4a0f7b2761
|
|
@ -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
|
||||
|
|
|
|||
60
send.log
60
send.log
|
|
@ -4526,3 +4526,63 @@
|
|||
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 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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版本
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
__version__ = '1.3.1'
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue