piaoquan.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. import random
  2. import re
  3. import time
  4. import json
  5. import cffi
  6. import requests
  7. from urllib.parse import urlencode, urlparse
  8. from common import Common, AliyunLogger, Feishu
  9. from common.sql_help import sqlCollect
  10. class PQ:
  11. """
  12. 获取视频链接
  13. """
  14. @classmethod
  15. def get_pw_url(cls, user_id):
  16. url = f"https://admin.piaoquantv.com/manager/video/detail/{user_id}"
  17. payload = {}
  18. headers = {
  19. 'authority': 'admin.piaoquantv.com',
  20. 'accept': 'application/json, text/plain, */*',
  21. 'accept-language': 'zh-CN,zh;q=0.9',
  22. 'cache-control': 'no-cache',
  23. 'cookie': 'SESSION=YjU3MzgwNTMtM2QyYi00YjljLWI3YWUtZTBjNWYwMGQzYWNl',
  24. 'pragma': 'no-cache',
  25. 'referer': f'https://admin.piaoquantv.com/cms/post-detail/{user_id}/detail',
  26. 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
  27. 'sec-ch-ua-mobile': '?0',
  28. 'sec-ch-ua-platform': '"macOS"',
  29. 'sec-fetch-dest': 'empty',
  30. 'sec-fetch-mode': 'cors',
  31. 'sec-fetch-site': 'same-origin',
  32. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
  33. }
  34. response = requests.request("GET", url, headers=headers, data=payload)
  35. data = response.json()
  36. try:
  37. video_url = data["content"]["transedVideoPath"]
  38. return video_url
  39. except Exception as e:
  40. Common.logger("video").warning(f"获取视频链接失败:{e}\n")
  41. return ""
  42. """
  43. 获取视频链接
  44. """
  45. @classmethod
  46. def get_audio_url(cls, task_mark, user_id, title, mark):
  47. url = f"https://admin.piaoquantv.com/manager/video/detail/{user_id}"
  48. payload = {}
  49. headers = {
  50. 'authority': 'admin.piaoquantv.com',
  51. 'accept': 'application/json, text/plain, */*',
  52. 'accept-language': 'zh-CN,zh;q=0.9',
  53. 'cache-control': 'no-cache',
  54. 'cookie': 'SESSION=YjU3MzgwNTMtM2QyYi00YjljLWI3YWUtZTBjNWYwMGQzYWNl',
  55. 'pragma': 'no-cache',
  56. 'referer': f'https://admin.piaoquantv.com/cms/post-detail/{user_id}/detail',
  57. 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
  58. 'sec-ch-ua-mobile': '?0',
  59. 'sec-ch-ua-platform': '"macOS"',
  60. 'sec-fetch-dest': 'empty',
  61. 'sec-fetch-mode': 'cors',
  62. 'sec-fetch-site': 'same-origin',
  63. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
  64. }
  65. response = requests.request("GET", url, headers=headers, data=payload)
  66. data = response.json()
  67. try:
  68. list = []
  69. video_id = data["content"]["id"]
  70. if mark:
  71. status = sqlCollect.is_used(task_mark, video_id, mark, "票圈")
  72. else:
  73. status = True
  74. if status:
  75. if title == '' or title == None:
  76. new_title = data["content"]["title"]
  77. else:
  78. if '/' in title:
  79. titles = [t for t in title.split('/') if t and t != "None"]
  80. else:
  81. titles = [title]
  82. new_title = random.choice(titles)
  83. video_url = data["content"]["transedVideoPath"]
  84. cover = data["content"]["coverImgPath"]
  85. all_data = {"video_id": video_id, "title": new_title, "cover": cover, "video_url": video_url, "rule": "无", "old_title": data["content"]["title"]}
  86. list.append(all_data)
  87. return list
  88. return list
  89. except Exception as e:
  90. Common.logger("video").warning(f"获取视频链接失败:{e}\n")
  91. return ""
  92. """
  93. 获取用户下的所有视频
  94. """
  95. @classmethod
  96. def get_pq_url(cls, task_mark, user_id, number, mark, channel_id, name):
  97. url = f"https://admin.piaoquantv.com/manager/video/page?uid={user_id}&pageNum=1&pageSize=100"
  98. payload = {}
  99. headers = {
  100. 'accept': 'application/json, text/plain, */*',
  101. 'cookie': 'SESSION=NjRmMGVjNTAtNzJiNi00ODE0LThjYzQtYmZiNTJhMDNiZTcz',
  102. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
  103. }
  104. list = []
  105. try:
  106. response = requests.request("GET", url, headers=headers, data=payload)
  107. data = response.json()
  108. content = data["content"]["objs"]
  109. for url in content:
  110. video_id = url["id"]
  111. status = sqlCollect.is_used(task_mark, video_id, mark, "票圈")
  112. cover = url["coverImgPath"]
  113. video_url = url["transedVideoPath"]
  114. old_title = url["title"]
  115. log_data = f"user:{user_id},,video_id:{video_id},,video_url:{video_url},,original_title:{old_title}"
  116. AliyunLogger.logging(channel_id, name, user_id, video_id, "扫描到一条视频", "2001", log_data)
  117. if status:
  118. AliyunLogger.logging(channel_id, name, user_id, video_id, "该视频已改造过", "2002", log_data)
  119. continue
  120. AliyunLogger.logging(channel_id, name, user_id, video_id, "符合规则等待改造", "2004", log_data)
  121. all_data = {"video_id": video_id, "cover": cover, "video_url": video_url, "rule": "无", "old_title": old_title}
  122. list.append(all_data)
  123. if len(list) == int(number):
  124. Common.logger("pq").info(f"获取视频总数:{len(list)}\n")
  125. return list
  126. Common.logger("pq").info(f"获取票圈视频总数:{len(list)}\n")
  127. return list
  128. except Exception as e:
  129. Common.logger("pq").warning(f"获取音频视频链接失败:{e}\n")
  130. return list
  131. """
  132. 获取封面
  133. """
  134. @classmethod
  135. def get_cover(cls, uid):
  136. time.sleep(1)
  137. url = "https://admin.piaoquantv.com/manager/video/multiCover/listV2"
  138. payload = json.dumps({
  139. "videoId": uid,
  140. "range": "2h"
  141. })
  142. headers = {
  143. 'accept': 'application/json',
  144. 'accept-language': 'zh-CN,zh;q=0.9',
  145. 'cache-control': 'no-cache',
  146. 'content-type': 'application/json',
  147. 'cookie': 'SESSION=YjU3MzgwNTMtM2QyYi00YjljLWI3YWUtZTBjNWYwMGQzYWNl',
  148. 'origin': 'https://admin.piaoquantv.com',
  149. 'pragma': 'no-cache',
  150. 'priority': 'u=1, i',
  151. 'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
  152. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
  153. }
  154. response = requests.request("POST", url, headers=headers, data=payload)
  155. data = response.json()
  156. content = data["content"]
  157. if len(content) == 1:
  158. return content[0]["coverUrl"]
  159. max_share_count = 0
  160. selected_cover_url = ""
  161. for item in content:
  162. share_count = item.get("shareWeight")
  163. if share_count is not None and share_count > max_share_count:
  164. max_share_count = share_count
  165. selected_cover_url = item["coverUrl"]
  166. elif share_count == max_share_count and item["createUser"] == "用户":
  167. selected_cover_url = item["coverUrl"]
  168. return selected_cover_url
  169. """
  170. 获取标题
  171. """
  172. @classmethod
  173. def get_title(cls, uid):
  174. url = "https://admin.piaoquantv.com/manager/video/multiTitleV2/listV2"
  175. payload = json.dumps({
  176. "videoId": uid,
  177. "range": "4h"
  178. })
  179. headers = {
  180. 'accept': 'application/json',
  181. 'accept-language': 'zh-CN,zh;q=0.9',
  182. 'cache-control': 'no-cache',
  183. 'content-type': 'application/json',
  184. 'cookie': 'SESSION=YjU3MzgwNTMtM2QyYi00YjljLWI3YWUtZTBjNWYwMGQzYWNl',
  185. 'origin': 'https://admin.piaoquantv.com',
  186. 'pragma': 'no-cache',
  187. 'priority': 'u=1, i',
  188. 'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
  189. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
  190. }
  191. response = requests.request("POST", url, headers=headers, data=payload)
  192. data = response.json()
  193. content = data["content"]
  194. if len(content) == 1:
  195. return content[0]["title"]
  196. max_share_count = 0
  197. selected_title = ""
  198. for item in content:
  199. share_count = item.get("shareWeight")
  200. if share_count is not None and share_count > max_share_count:
  201. max_share_count = share_count
  202. selected_title = item["title"]
  203. elif share_count == max_share_count and item["createUser"] == "用户":
  204. selected_title = item["title"]
  205. return selected_title
  206. """
  207. 新生成视频上传到对应账号下
  208. """
  209. @classmethod
  210. def insert_piaoquantv(cls, new_video_path, new_title, cover, n_id):
  211. url = "https://vlogapi.piaoquantv.com/longvideoapi/crawler/video/send"
  212. headers = {
  213. 'User-Agent': 'PQSpeed/486 CFNetwork/1410.1 Darwin/22.6.0',
  214. 'cookie': 'JSESSIONID=4DEA2B5173BB9A9E82DB772C0ACDBC9F; JSESSIONID=D02C334150025222A0B824A98B539B78',
  215. 'referer': 'http://appspeed.piaoquantv.com',
  216. 'token': '524a8bc871dbb0f4d4717895083172ab37c02d2f',
  217. 'accept-language': 'zh-CN,zh-Hans;q=0.9',
  218. 'Content-Type': 'application/x-www-form-urlencoded'
  219. }
  220. payload = {
  221. 'deviceToken': '9ef064f2f7869b3fd67d6141f8a899175dddc91240971172f1f2a662ef891408',
  222. 'fileExtensions': 'MP4',
  223. 'loginUid': n_id,
  224. 'networkType': 'Wi-Fi',
  225. 'platform': 'iOS',
  226. 'requestId': 'fb972cbd4f390afcfd3da1869cd7d001',
  227. 'sessionId': '362290597725ce1fa870d7be4f46dcc2',
  228. 'subSessionId': '362290597725ce1fa870d7be4f46dcc2',
  229. 'title': new_title,
  230. 'token': '524a8bc871dbb0f4d4717895083172ab37c02d2f',
  231. 'uid': n_id,
  232. 'versionCode': '486',
  233. 'versionName': '3.4.12',
  234. 'videoFromScene': '1',
  235. 'videoPath': new_video_path,
  236. 'viewStatus': '1'
  237. }
  238. if cover:
  239. payload['coverImgPath'] = cover
  240. encoded_payload = urlencode(payload)
  241. response = requests.request("POST", url, headers=headers, data=encoded_payload)
  242. data = response.json()
  243. code = data["code"]
  244. if code == 0:
  245. new_video_id = data["data"]["id"]
  246. return new_video_id
  247. else:
  248. return ''
  249. """
  250. 单点视频重新获取视频链接
  251. """
  252. @classmethod
  253. def get_dd_video_url(cls, wx_msg):
  254. try:
  255. url = "http://8.217.190.241:8888/api/wei_xin/msg/callback"
  256. # payload = json.loads(wx_msg)
  257. headers = {
  258. 'Content-Type': 'application/json'
  259. }
  260. response = requests.request("POST", url, headers=headers, data=wx_msg.encode())
  261. response = response.json()
  262. url_video = response['video_url']
  263. return url_video
  264. except Exception as e:
  265. print(e)
  266. return None
  267. "视频号加密视频解密"
  268. @classmethod
  269. def decrypt_video(cls, data: bytes, decode_key: int, enc_length: int = 131072) -> bytes:
  270. try:
  271. ffi = cffi.FFI()
  272. # lib = ffi.dlopen(r'/Users/tzld/Desktop/video_rewriting/libsph_decrypt.dylib')
  273. lib = ffi.dlopen(r'/root/video_rewriting/libsph_decrypt.so')
  274. ffi.cdef('void decrypt(unsigned char *data, const size_t data_length, const uint32_t key);')
  275. c_data = ffi.new('unsigned char[]', list(data))
  276. lib.decrypt(c_data, enc_length, decode_key)
  277. data = bytes(ffi.buffer(c_data, len(data))[:])
  278. return data
  279. except Exception as e:
  280. Common.logger("dd-sph").info(f"decrypt_video获取异常,异常信息{e}")
  281. return data
  282. """
  283. 单点视频号视频下载
  284. """
  285. @classmethod
  286. def dd_sph_download_video(cls, video_url, video_path_url, video_id, video, channel_id):
  287. new_video = video_path_url + str(video_id) + '.mp4'
  288. if channel_id == '单点视频':
  289. is_encrypted = video.get('is_encrypted', "0")
  290. if video['source'] == "视频号" and int(is_encrypted) == 1:
  291. decode_key = video['decode_key']
  292. if decode_key == None:
  293. return new_video
  294. decode_key = int(video['decode_key'])
  295. data, enc_length = None, 0
  296. for i in range(3):
  297. try:
  298. response = requests.get(url=video_url, timeout=10)
  299. data = response.content
  300. data_length = int(response.headers.get("Content-Range", '0').split('/')[-1])
  301. enc_length = int(response.headers.get('X-enclen', 131072))
  302. if len(data) == data_length:
  303. break
  304. except TimeoutError:
  305. continue
  306. if not data:
  307. v_id = video["video_id"]
  308. sqlCollect.update_shp_dd_vid_4(v_id)
  309. from_user_name = video['from_user_name'] # 来源用户
  310. from_group_name = video['from_group_name'] # 来源群组
  311. source = video['source'] # 渠道
  312. text = (
  313. f"**渠道**: {source}\n"
  314. f"**来源用户**: {from_user_name}\n"
  315. f"**来源群组**: {from_group_name}\n"
  316. f"**原视频链接**: {video['video_url']}\n"
  317. f"**原视频封面**: {video['cover']}\n"
  318. f"**原视频标题**: {video['old_title']}\n"
  319. )
  320. Feishu.finish_bot(text,
  321. "https://open.feishu.cn/open-apis/bot/v2/hook/493b3d4c-5fae-4a9d-980b-1dd86636524e",
  322. "【 视频下载失败 】")
  323. return new_video
  324. video_url = cls.decrypt_video(data=data, decode_key=decode_key, enc_length=enc_length)
  325. with open(f"{new_video}", 'wb') as f:
  326. f.write(video_url)
  327. return new_video
  328. url_video = video_url
  329. for i in range(3):
  330. try:
  331. payload = {}
  332. headers = {}
  333. response = requests.request("GET", url_video, headers=headers, data=payload)
  334. if response.status_code == 200:
  335. with open(f"{new_video}", "wb") as file:
  336. # 将响应内容写入文件
  337. file.write(response.content)
  338. time.sleep(5)
  339. return new_video
  340. else:
  341. if channel_id == '单点视频':
  342. wx_msg = video['wx_msg']
  343. if wx_msg:
  344. url_videos = cls.get_dd_video_url(wx_msg)
  345. if url_videos:
  346. url_video = url_videos
  347. except Exception:
  348. if channel_id == '单点视频':
  349. wx_msg = video['wx_msg']
  350. if wx_msg:
  351. url_videos = cls.get_dd_video_url(wx_msg)
  352. if url_videos:
  353. url_video = url_videos
  354. if i == 3:
  355. return None
  356. return None
  357. """
  358. 视频号视频下载
  359. """
  360. @classmethod
  361. def sph_download_video(cls, video_url, video_path_url, video_id, video):
  362. decode_key = int(video['decode_key'])
  363. data, enc_length = None, 0
  364. for i in range(3):
  365. try:
  366. response = requests.get(url=video_url, timeout=10)
  367. data = response.content
  368. data_length = int(response.headers.get("Content-Range", '0').split('/')[-1])
  369. enc_length = int(response.headers.get('X-enclen', 131072))
  370. if len(data) == data_length:
  371. break
  372. except TimeoutError:
  373. continue
  374. if not data:
  375. return None
  376. video_url = cls.decrypt_video(data=data, decode_key=decode_key, enc_length=enc_length)
  377. new_video = video_path_url + str(video_id) + '.mp4'
  378. for i in range(3):
  379. try:
  380. with open(f"{new_video}", 'wb') as f:
  381. f.write(video_url)
  382. return new_video
  383. except Exception:
  384. if i == 3:
  385. return None
  386. """票圈/快手创作者"""
  387. @classmethod
  388. def download_video(cls, video_url, video_path_url, video_id):
  389. try:
  390. for i in range(3):
  391. payload = {}
  392. headers = {}
  393. response = requests.request("GET", video_url, headers=headers, data=payload)
  394. if response.status_code == 200:
  395. # 以二进制写入模式打开文件
  396. video = video_path_url + str(video_id) + '.mp4'
  397. with open(f"{video}", "wb") as file:
  398. # 将响应内容写入文件
  399. file.write(response.content)
  400. time.sleep(5)
  401. return video
  402. return None
  403. except Exception:
  404. return None
  405. """抖音"""
  406. @classmethod
  407. def download_dy_video(cls, video_url, video_path_url, video_id):
  408. try:
  409. video = video_path_url + str(video_id) + '.mp4'
  410. for i in range(3):
  411. payload = {}
  412. headers = {
  413. 'accept': '*/*',
  414. 'accept-encoding': 'identity;q=1, *;q=0',
  415. 'accept-language': 'zh-CN,zh;q=0.9',
  416. 'cache-control': 'no-cache',
  417. 'connection': 'keep-alive',
  418. 'host': urlparse(video_url).netloc,
  419. 'pragma': 'no-cache',
  420. 'range': 'bytes=0-',
  421. 'referer': video_url,
  422. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  423. }
  424. response = requests.request("GET", video_url, headers=headers, data=payload)
  425. if response.status_code == 206:
  426. # 以二进制写入模式打开文件
  427. with open(f"{video}", "wb") as file:
  428. # 将响应内容写入文件
  429. file.write(response.content)
  430. time.sleep(5)
  431. return video
  432. return video
  433. except Exception as e:
  434. print(e)
  435. return None
  436. """
  437. 票圈站内视频下载
  438. """
  439. @classmethod
  440. def download_video_jpg(cls, video_url, video_path_url, video_id):
  441. try:
  442. payload = {}
  443. headers = {}
  444. response = requests.request("GET", video_url, headers=headers, data=payload)
  445. if response.status_code == 200:
  446. # 以二进制写入模式打开文件
  447. video = video_path_url + str(video_id) + '.jpg'
  448. with open(f"{video}", "wb") as file:
  449. # 将响应内容写入文件
  450. file.write(response.content)
  451. time.sleep(5)
  452. return video
  453. except Exception:
  454. return None
  455. if __name__ == '__main__':
  456. url ='https://v5-dy-o-abtest.zjcdn.com/24958f9e7ef091a31c409c0dd1e83a6f/675fc6bc/video/tos/cn/tos-cn-ve-15/oIB5AwLndiATiANQIziB1TfHED3igPr1AD5Kje/?a=1128&ch=11&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C3&cv=1&br=515&bt=515&cs=0&ds=6&ft=J33IIDDhNF5VQsYesNousauzXzySY~bbUBvThbLfK&mime_type=video_mp4&qs=0&rc=aTg2M2Y3ZjU1NGg3ZTU8aUBpamVoZ285cjh0djMzNGkzM0AxNjIzYjUuNV4xXmFiLTRiYSMwM2FlMmRjY3BgLS1kLTBzcw%3D%3D&btag=80010e000b0001&cc=46&cquery=105E_103Q_103W_103Y_100b&dy_q=1734326142&feature_id=f0150a16a324336cda5d6dd0b69ed299&l=20241216131541780499AB5185B80CA219&req_cdn_type='
  457. a = PQ.download_dy_video(url,'/Users/z/Downloads/','70100016')
  458. print(a)