123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- # coding=utf-8
- # !/usr/bin/python
- # by嗷呜(finally)
- import sys
- import os
- sys.path.append("..")
- import re
- import hashlib
- import hmac
- import random
- import string
- from Crypto.Util.Padding import unpad
- from concurrent.futures import ThreadPoolExecutor
- from Crypto.PublicKey import RSA
- from Crypto.Cipher import PKCS1_v1_5, AES
- from base64 import b64encode, b64decode
- import json
- import time
- from base.spider import Spider
- class Spider(Spider):
- def getName(self):
- return "电影猎手"
- def init(self, extend=""):
- self.device = self.device_id()
- self.host = self.gethost()
- pass
- def isVideoFormat(self, url):
- pass
- def manualVideoCheck(self):
- pass
- def action(self, action):
- pass
- def destroy(self):
- pass
- t = str(int(time.time()))
- def homeContent(self, filter):
- result = {}
- filters = {}
- classes = []
- bba = self.url()
- data = self.fetch(f"{self.host}/api/v1/app/config?pack={bba[0]}&signature={bba[1]}", headers=self.header()).text
- data1 = self.aes(data)
- dy = {"class":"类型","area":"地区","lang":"语言","year":"年份","letter":"字母","by":"排序","sort":"排序"}
- data1['data']['movie_screen']['sort'].pop(0)
- for item in data1['data']['movie_screen']['sort']:
- item['n'] = item.pop('name')
- item['v'] = item.pop('value')
- for item in data1['data']['movie_screen']['filter']:
- has_non_empty_field = False
- classes.append({"type_name": item["name"], "type_id": str(item["id"])})
- for key in dy:
- if key in item and item[key]:
- has_non_empty_field = True
- break
- if has_non_empty_field:
- filters[str(item["id"])] = []
- filters[str(item["id"])].append(
- {"key": 'sort', "name": '排序', "value": data1['data']['movie_screen']['sort']})
- for dkey in item:
- if dkey in dy and item[dkey]:
- item[dkey].pop(0)
- value_array = [
- {"n": value.strip(), "v": value.strip()}
- for value in item[dkey]
- if value.strip() != ""
- ]
- filters[str(item["id"])].append(
- {"key": dkey, "name": dy[dkey], "value": value_array}
- )
- result["class"] = classes
- result["filters"] = filters
- return result
- def homeVideoContent(self):
- bba = self.url()
- url = f'{self.host}/api/v1/movie/index_recommend?pack={bba[0]}&signature={bba[1]}'
- data = self.fetch(url, headers=self.header()).json()
- videos = []
- for item in data['data']:
- if len(item['list']) > 0:
- for it in item['list']:
- try:
- videos.append(self.voides(it))
- except Exception as e:
- continue
- result = {"list": videos}
- return result
- def categoryContent(self, tid, pg, filter, extend):
- body = {"type_id": tid, "sort": extend.get("sort", "by_default"), "class": extend.get("class", "类型"),
- "area": extend.get("area", "地区"), "year": extend.get("year", "年份"), "page": str(pg),
- "pageSize": "21"}
- result = {}
- list = []
- bba = self.url(body)
- url = f"{self.host}/api/v1/movie/screen/list?pack={bba[0]}&signature={bba[1]}"
- data = self.fetch(url, headers=self.header()).json()['data']['list']
- for item in data:
- list.append(self.voides(item))
- result["list"] = list
- result["page"] = pg
- result["pagecount"] = 9999
- result["limit"] = 90
- result["total"] = 999999
- return result
- def detailContent(self, ids):
- body = {"id": ids[0]}
- bba = self.url(body)
- url = f'{self.host}/api/v1/movie/detail?pack={bba[0]}&signature={bba[1]}'
- data = self.fetch(url, headers=self.header()).json()['data']
- video = {'vod_name': data.get('name'),'type_name': data.get('type_name'),'vod_year': data.get('year'),'vod_area': data.get('area'),'vod_remarks': data.get('dynami'),'vod_content': data.get('content')}
- play = []
- names = []
- tasks = []
- for itt in data["play_from"]:
- name = itt["name"]
- a = []
- if len(itt["list"]) > 0:
- names.append(name)
- play.append(self.playeach(itt['list']))
- else:
- tasks.append({"movie_id": ids[0], "from_code": itt["code"]})
- names.append(name)
- if tasks:
- with ThreadPoolExecutor(max_workers=len(tasks)) as executor:
- results = executor.map(self.playlist, tasks)
- for result in results:
- if result:
- play.append(result)
- else:
- play.append("")
- video["vod_play_from"] = "$$$".join(names)
- video["vod_play_url"] = "$$$".join(play)
- result = {"list": [video]}
- return result
- def searchContent(self, key, quick, pg=1):
- body = {"keyword": key, "sort": "", "type_id": "0", "page": str(pg), "pageSize": "10",
- "res_type": "by_movie_name"}
- bba = self.url(body)
- url = f"{self.host}/api/v1/movie/search?pack={bba[0]}&signature={bba[1]}"
- data = self.fetch(url, headers=self.header()).json()['data'].get('list')
- videos = []
- for it in data:
- try:
- videos.append(self.voides(it))
- except Exception as e:
- continue
- result = {"list": videos, "page": pg}
- return result
- def playerContent(self, flag, id, vipFlags):
- url = id
- if "m3u8" not in url and "mp4" not in url:
- try:
- add = id.split('|||')
- data = {"from_code": add[0], "play_url": add[1], "episode_id": add[2], "type": "play"}
- bba = self.url(data)
- data2 = self.fetch(f"{self.host}/api/v1/movie_addr/parse_url?pack={bba[0]}&signature={bba[1]}",
- headers=self.header()).json()['data']
- url = data2.get('play_url') or data2.get('download_url')
- try:
- url1 = self.fetch(url, headers=self.header(), allow_redirects=False).headers['Location']
- if url1 and "http" in url1:
- url = url1
- except:
- pass
- except Exception as e:
- pass
- if '.jpg' in url or '.jpeg' in url or '.png' in url:
- url = self.getProxyUrl() + "&url=" + b64encode(url.encode('utf-8')).decode('utf-8') + "&type=m3u8"
- result = {}
- result["parse"] = 0
- result["url"] = url
- result["header"] = {'user-agent': 'okhttp/4.9.2'}
- return result
- def localProxy(self, param):
- url = b64decode(param["url"]).decode('utf-8')
- durl = url[:url.rfind('/')]
- data = self.fetch(url, headers=self.header()).content.decode("utf-8")
- lines = data.strip().split('\n')
- for index, string in enumerate(lines):
- # if 'URI="' in string and 'http' not in string:
- # lines[index] = index
- # 暂时预留,貌似用不到
- if '#EXT' not in string and 'http' not in string:
- lines[index] = durl + ('' if string.startswith('/') else '/') + string
- data = '\n'.join(lines)
- return [200, "application/vnd.apple.mpegur", data]
- def device_id(self):
- characters = string.ascii_lowercase + string.digits
- random_string = ''.join(random.choices(characters, k=32))
- return random_string
- def gethost(self):
- headers = {
- 'User-Agent': 'okhttp/4.9.2',
- 'Connection': 'Keep-Alive',
- }
- response = self.fetch('https://app-site.ecoliving168.com/domain_v5.json', headers=headers).json()
- url = response['api_service'].replace('/api/', '')
- return url
- def header(self):
- headers = {
- 'User-Agent': 'Android',
- 'Accept': 'application/prs.55App.v2+json',
- 'timestamp': self.t,
- 'x-client-setting': '{"pure-mode":1}',
- 'x-client-uuid': '{"device_id":' + self.device + '}, "type":1,"brand":"Redmi", "model":"M2012K10C", "system_version":30, "sdk_version":"3.1.0.7"}',
- 'x-client-version': '3096 '
- }
- return headers
- def url(self, id=None):
- if not id:
- id = {}
- id["timestamp"] = self.t
- public_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA02F/kPg5A2NX4qZ5JSns+bjhVMCC6JbTiTKpbgNgiXU+Kkorg6Dj76gS68gB8llhbUKCXjIdygnHPrxVHWfzmzisq9P9awmXBkCk74Skglx2LKHa/mNz9ivg6YzQ5pQFUEWS0DfomGBXVtqvBlOXMCRxp69oWaMsnfjnBV+0J7vHbXzUIkqBLdXSNfM9Ag5qdRDrJC3CqB65EJ3ARWVzZTTcXSdMW9i3qzEZPawPNPe5yPYbMZIoXLcrqvEZnRK1oak67/ihf7iwPJqdc+68ZYEmmdqwunOvRdjq89fQMVelmqcRD9RYe08v+xDxG9Co9z7hcXGTsUquMxkh29uNawIDAQAB'
- encrypted_text = json.dumps(id)
- public_key = RSA.import_key(b64decode(public_key))
- cipher = PKCS1_v1_5.new(public_key)
- encrypted_message = cipher.encrypt(encrypted_text.encode('utf-8'))
- encrypted_message_base64 = b64encode(encrypted_message).decode('utf-8')
- result = encrypted_message_base64.replace('+', '-').replace('/', '_').replace('=', '')
- key = '635a580fcb5dc6e60caa39c31a7bde48'
- sign = hmac.new(key.encode(), result.encode(), hashlib.md5).hexdigest()
- return result, sign
- def playlist(self, body):
- try:
- bba = self.url(body)
- url = f'{self.host}/api/v1/movie_addr/list?pack={bba[0]}&signature={bba[1]}'
- data = self.fetch(url, headers=self.header()).json()['data']
- return self.playeach(data)
- except Exception:
- return []
- def playeach(self,data):
- play_urls = []
- for it in data:
- if re.search(r"mp4|m3u8", it["play_url"]):
- play_urls.append(f"{it['episode_name']}${it['play_url']}")
- else:
- play_urls.append(
- f"{it['episode_name']}${it['from_code']}|||{it['play_url']}|||{it['episode_id']}"
- )
- return '#'.join(play_urls)
-
- def voides(self, item):
- if item['name'] or item['title']:
- voide = {
- "vod_id": item.get('id') or item.get('click'),
- 'vod_name': item.get('name') or item.get('title'),
- 'vod_pic': item.get('cover') or item.get('image'),
- 'vod_year': item.get('year') or item.get('label'),
- 'vod_remarks': item.get('dynamic') or item.get('sub_title')
- }
- return voide
- def aes(self, text):
- text = text.replace('-', '+').replace('_', '/') + '=='
- key = b"e6d5de5fcc51f53d"
- iv = b"2f13eef7dfc6c613"
- cipher = AES.new(key, AES.MODE_CBC, iv)
- pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size).decode("utf-8")
- return json.loads(pt)
|