Sync all projects

This commit is contained in:
github-actions[bot]
2026-06-29 03:41:14 +00:00
parent 529cd7c625
commit 8b025e0766
11717 changed files with 6370622 additions and 0 deletions
+404
View File
@@ -0,0 +1,404 @@
# -*- coding: utf-8 -*-
# 🌈 Love
import json
import random
import re
import sys
import threading
import time
from base64 import b64decode, b64encode
from urllib.parse import urlparse
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from pyquery import PyQuery as pq
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
try:self.proxies = json.loads(extend)
except:self.proxies = {}
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
}
# Use working dynamic URLs directly
self.host = self.get_working_host()
self.headers.update({'Origin': self.host, 'Referer': f"{self.host}/"})
self.log(f"使用站点: {self.host}")
print(f"使用站点: {self.host}")
pass
def getName(self):
return "🌈 51吸瓜"
def isVideoFormat(self, url):
# Treat direct media formats as playable without parsing
return any(ext in (url or '') for ext in ['.m3u8', '.mp4', '.ts'])
def manualVideoCheck(self):
return False
def destroy(self):
pass
def homeContent(self, filter):
try:
response = requests.get(self.host, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'class': [], 'list': []}
data = self.getpq(response.text)
result = {}
classes = []
# Try to get categories from different possible locations
category_selectors = [
'.category-list ul li',
'.nav-menu li',
'.menu li',
'nav ul li'
]
for selector in category_selectors:
for k in data(selector).items():
link = k('a')
href = (link.attr('href') or '').strip()
name = (link.text() or '').strip()
# Skip placeholder or invalid entries
if not href or href == '#' or not name:
continue
classes.append({
'type_name': name,
'type_id': href
})
if classes:
break
# If no categories found, create some default ones
if not classes:
classes = [
{'type_name': '首页', 'type_id': '/'},
{'type_name': '最新', 'type_id': '/latest/'},
{'type_name': '热门', 'type_id': '/hot/'}
]
result['class'] = classes
result['list'] = self.getlist(data('#index article a'))
return result
except Exception as e:
print(f"homeContent error: {e}")
return {'class': [], 'list': []}
def homeVideoContent(self):
try:
response = requests.get(self.host, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': []}
data = self.getpq(response.text)
return {'list': self.getlist(data('#index article a, #archive article a'))}
except Exception as e:
print(f"homeVideoContent error: {e}")
return {'list': []}
def categoryContent(self, tid, pg, filter, extend):
try:
if '@folder' in tid:
id = tid.replace('@folder', '')
videos = self.getfod(id)
else:
# Build URL properly
if tid.startswith('/'):
if pg and pg != '1':
url = f"{self.host}{tid}page/{pg}/"
else:
url = f"{self.host}{tid}"
else:
url = f"{self.host}/{tid}"
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': [], 'page': pg, 'pagecount': 1, 'limit': 90, 'total': 0}
data = self.getpq(response.text)
videos = self.getlist(data('#archive article a, #index article a'), tid)
result = {}
result['list'] = videos
result['page'] = pg
result['pagecount'] = 1 if '@folder' in tid else 99999
result['limit'] = 90
result['total'] = 999999
return result
except Exception as e:
print(f"categoryContent error: {e}")
return {'list': [], 'page': pg, 'pagecount': 1, 'limit': 90, 'total': 0}
def detailContent(self, ids):
try:
url = f"{self.host}{ids[0]}" if not ids[0].startswith('http') else ids[0]
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': [{'vod_play_from': '51吸瓜', 'vod_play_url': f'页面加载失败${url}'}]}
data = self.getpq(response.text)
vod = {'vod_play_from': '51吸瓜'}
# Get content/description
try:
clist = []
if data('.tags .keywords a'):
for k in data('.tags .keywords a').items():
title = k.text()
href = k.attr('href')
if title and href:
clist.append('[a=cr:' + json.dumps({'id': href, 'name': title}) + '/]' + title + '[/a]')
vod['vod_content'] = ' '.join(clist) if clist else data('.post-title').text()
except:
vod['vod_content'] = data('.post-title').text() or '51吸瓜视频'
# Get video URLs (build episode list when multiple players exist)
try:
plist = []
used_names = set()
if data('.dplayer'):
for c, k in enumerate(data('.dplayer').items(), start=1):
config_attr = k.attr('data-config')
if config_attr:
try:
config = json.loads(config_attr)
video_url = config.get('video', {}).get('url', '')
# Determine a readable episode name from nearby headings if present
ep_name = ''
try:
parent = k.parents().eq(0)
# search up to a few ancestors for a heading text
for _ in range(3):
if not parent: break
heading = parent.find('h2, h3, h4').eq(0).text() or ''
heading = heading.strip()
if heading:
ep_name = heading
break
parent = parent.parents().eq(0)
except Exception:
ep_name = ''
base_name = ep_name if ep_name else f"视频{c}"
name = base_name
count = 2
# Ensure the name is unique
while name in used_names:
name = f"{base_name} {count}"
count += 1
used_names.add(name)
if video_url:
self.log(f"解析到视频: {name} -> {video_url}")
print(f"解析到视频: {name} -> {video_url}")
plist.append(f"{name}${video_url}")
except:
continue
if plist:
self.log(f"拼装播放列表,共{len(plist)}")
print(f"拼装播放列表,共{len(plist)}")
vod['vod_play_url'] = '#'.join(plist)
else:
vod['vod_play_url'] = f"未找到视频源${url}"
except Exception as e:
vod['vod_play_url'] = f"视频解析失败${url}"
return {'list': [vod]}
except Exception as e:
print(f"detailContent error: {e}")
return {'list': [{'vod_play_from': '51吸瓜', 'vod_play_url': f'详情页加载失败${ids[0] if ids else ""}'}]}
def searchContent(self, key, quick, pg="1"):
try:
url = f"{self.host}/search/{key}/{pg}" if pg != "1" else f"{self.host}/search/{key}/"
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': [], 'page': pg}
data = self.getpq(response.text)
videos = self.getlist(data('#archive article a, #index article a'))
return {'list': videos, 'page': pg}
except Exception as e:
print(f"searchContent error: {e}")
return {'list': [], 'page': pg}
def playerContent(self, flag, id, vipFlags):
url = id
p = 1
if self.isVideoFormat(url):
# m3u8/mp4 direct play; when using proxy setting, wrap to proxy for m3u8
if '.m3u8' in url:
url = self.proxy(url)
p = 0
self.log(f"播放请求: parse={p}, url={url}")
print(f"播放请求: parse={p}, url={url}")
return {'parse': p, 'url': url, 'header': self.headers}
def localProxy(self, param):
if param.get('type') == 'img':
res=requests.get(param['url'], headers=self.headers, proxies=self.proxies, timeout=10)
return [200,res.headers.get('Content-Type'),self.aesimg(res.content)]
elif param.get('type') == 'm3u8':return self.m3Proxy(param['url'])
else:return self.tsProxy(param['url'])
def proxy(self, data, type='m3u8'):
if data and len(self.proxies):return f"{self.getProxyUrl()}&url={self.e64(data)}&type={type}"
else:return data
def m3Proxy(self, url):
url=self.d64(url)
ydata = requests.get(url, headers=self.headers, proxies=self.proxies, allow_redirects=False)
data = ydata.content.decode('utf-8')
if ydata.headers.get('Location'):
url = ydata.headers['Location']
data = requests.get(url, headers=self.headers, proxies=self.proxies).content.decode('utf-8')
lines = data.strip().split('\n')
last_r = url[:url.rfind('/')]
parsed_url = urlparse(url)
durl = parsed_url.scheme + "://" + parsed_url.netloc
iskey=True
for index, string in enumerate(lines):
if iskey and 'URI' in string:
pattern = r'URI="([^"]*)"'
match = re.search(pattern, string)
if match:
lines[index] = re.sub(pattern, f'URI="{self.proxy(match.group(1), "mkey")}"', string)
iskey=False
continue
if '#EXT' not in string:
if 'http' not in string:
domain = last_r if string.count('/') < 2 else durl
string = domain + ('' if string.startswith('/') else '/') + string
lines[index] = self.proxy(string, string.split('.')[-1].split('?')[0])
data = '\n'.join(lines)
return [200, "application/vnd.apple.mpegur", data]
def tsProxy(self, url):
url = self.d64(url)
data = requests.get(url, headers=self.headers, proxies=self.proxies, stream=True)
return [200, data.headers['Content-Type'], data.content]
def e64(self, text):
try:
text_bytes = text.encode('utf-8')
encoded_bytes = b64encode(text_bytes)
return encoded_bytes.decode('utf-8')
except Exception as e:
print(f"Base64编码错误: {str(e)}")
return ""
def d64(self, encoded_text):
try:
encoded_bytes = encoded_text.encode('utf-8')
decoded_bytes = b64decode(encoded_bytes)
return decoded_bytes.decode('utf-8')
except Exception as e:
print(f"Base64解码错误: {str(e)}")
return ""
def get_working_host(self):
"""Get working host from known dynamic URLs"""
# Known working URLs from the dynamic gateway
dynamic_urls = [
'https://artist.vgwtswi.xyz',
'https://ability.vgwtswi.xyz',
'https://am.vgwtswi.xyz'
]
# Test each URL to find a working one
for url in dynamic_urls:
try:
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=10)
if response.status_code == 200:
# Verify it has the expected content structure
data = self.getpq(response.text)
articles = data('#index article a')
if len(articles) > 0:
self.log(f"选用可用站点: {url}")
print(f"选用可用站点: {url}")
return url
except Exception as e:
continue
# Fallback to first URL if none work (better than crashing)
self.log(f"未检测到可用站点,回退: {dynamic_urls[0]}")
print(f"未检测到可用站点,回退: {dynamic_urls[0]}")
return dynamic_urls[0]
def getlist(self, data, tid=''):
videos = []
l = '/mrdg' in tid
for k in data.items():
a = k.attr('href')
b = k('h2').text()
# Some pages might not include datePublished; use a fallback
c = k('span[itemprop="datePublished"]').text() or k('.post-meta, .entry-meta, time').text()
if a and b:
videos.append({
'vod_id': f"{a}{'@folder' if l else ''}",
'vod_name': b.replace('\n', ' '),
'vod_pic': self.getimg(k('script').text()),
'vod_remarks': c or '',
'vod_tag': 'folder' if l else '',
'style': {"type": "rect", "ratio": 1.33}
})
return videos
def getfod(self, id):
url = f"{self.host}{id}"
data = self.getpq(requests.get(url, headers=self.headers, proxies=self.proxies).text)
vdata=data('.post-content[itemprop="articleBody"]')
r=['.txt-apps','.line','blockquote','.tags','.content-tabs']
for i in r:vdata.remove(i)
p=vdata('p')
videos=[]
for i,x in enumerate(vdata('h2').items()):
c=i*2
videos.append({
'vod_id': p.eq(c)('a').attr('href'),
'vod_name': p.eq(c).text(),
'vod_pic': f"{self.getProxyUrl()}&url={p.eq(c+1)('img').attr('data-xkrkllgl')}&type=img",
'vod_remarks':x.text()
})
return videos
def getimg(self, text):
match = re.search(r"loadBannerDirect\('([^']+)'", text)
if match:
url = match.group(1)
return f"{self.getProxyUrl()}&url={url}&type=img"
else:
return ''
def aesimg(self, word):
key = b'f5d965df75336270'
iv = b'97b60394abc2fbe1'
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = unpad(cipher.decrypt(word), AES.block_size)
return decrypted
def getpq(self, data):
try:
return pq(data)
except Exception as e:
print(f"{str(e)}")
return pq(data.encode('utf-8'))
+404
View File
@@ -0,0 +1,404 @@
# -*- coding: utf-8 -*-
# 🌈 Love
import json
import random
import re
import sys
import threading
import time
from base64 import b64decode, b64encode
from urllib.parse import urlparse
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from pyquery import PyQuery as pq
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
try:self.proxies = json.loads(extend)
except:self.proxies = {}
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
}
# Use working dynamic URLs directly
self.host = self.get_working_host()
self.headers.update({'Origin': self.host, 'Referer': f"{self.host}/"})
self.log(f"使用站点: {self.host}")
print(f"使用站点: {self.host}")
pass
def getName(self):
return "🌈 51吸瓜"
def isVideoFormat(self, url):
# Treat direct media formats as playable without parsing
return any(ext in (url or '') for ext in ['.m3u8', '.mp4', '.ts'])
def manualVideoCheck(self):
return False
def destroy(self):
pass
def homeContent(self, filter):
try:
response = requests.get(self.host, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'class': [], 'list': []}
data = self.getpq(response.text)
result = {}
classes = []
# Try to get categories from different possible locations
category_selectors = [
'.category-list ul li',
'.nav-menu li',
'.menu li',
'nav ul li'
]
for selector in category_selectors:
for k in data(selector).items():
link = k('a')
href = (link.attr('href') or '').strip()
name = (link.text() or '').strip()
# Skip placeholder or invalid entries
if not href or href == '#' or not name:
continue
classes.append({
'type_name': name,
'type_id': href
})
if classes:
break
# If no categories found, create some default ones
if not classes:
classes = [
{'type_name': '首页', 'type_id': '/'},
{'type_name': '最新', 'type_id': '/latest/'},
{'type_name': '热门', 'type_id': '/hot/'}
]
result['class'] = classes
result['list'] = self.getlist(data('#index article a'))
return result
except Exception as e:
print(f"homeContent error: {e}")
return {'class': [], 'list': []}
def homeVideoContent(self):
try:
response = requests.get(self.host, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': []}
data = self.getpq(response.text)
return {'list': self.getlist(data('#index article a, #archive article a'))}
except Exception as e:
print(f"homeVideoContent error: {e}")
return {'list': []}
def categoryContent(self, tid, pg, filter, extend):
try:
if '@folder' in tid:
id = tid.replace('@folder', '')
videos = self.getfod(id)
else:
# Build URL properly
if tid.startswith('/'):
if pg and pg != '1':
url = f"{self.host}{tid}page/{pg}/"
else:
url = f"{self.host}{tid}"
else:
url = f"{self.host}/{tid}"
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': [], 'page': pg, 'pagecount': 1, 'limit': 90, 'total': 0}
data = self.getpq(response.text)
videos = self.getlist(data('#archive article a, #index article a'), tid)
result = {}
result['list'] = videos
result['page'] = pg
result['pagecount'] = 1 if '@folder' in tid else 99999
result['limit'] = 90
result['total'] = 999999
return result
except Exception as e:
print(f"categoryContent error: {e}")
return {'list': [], 'page': pg, 'pagecount': 1, 'limit': 90, 'total': 0}
def detailContent(self, ids):
try:
url = f"{self.host}{ids[0]}" if not ids[0].startswith('http') else ids[0]
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': [{'vod_play_from': '51吸瓜', 'vod_play_url': f'页面加载失败${url}'}]}
data = self.getpq(response.text)
vod = {'vod_play_from': '51吸瓜'}
# Get content/description
try:
clist = []
if data('.tags .keywords a'):
for k in data('.tags .keywords a').items():
title = k.text()
href = k.attr('href')
if title and href:
clist.append('[a=cr:' + json.dumps({'id': href, 'name': title}) + '/]' + title + '[/a]')
vod['vod_content'] = ' '.join(clist) if clist else data('.post-title').text()
except:
vod['vod_content'] = data('.post-title').text() or '51吸瓜视频'
# Get video URLs (build episode list when multiple players exist)
try:
plist = []
used_names = set()
if data('.dplayer'):
for c, k in enumerate(data('.dplayer').items(), start=1):
config_attr = k.attr('data-config')
if config_attr:
try:
config = json.loads(config_attr)
video_url = config.get('video', {}).get('url', '')
# Determine a readable episode name from nearby headings if present
ep_name = ''
try:
parent = k.parents().eq(0)
# search up to a few ancestors for a heading text
for _ in range(3):
if not parent: break
heading = parent.find('h2, h3, h4').eq(0).text() or ''
heading = heading.strip()
if heading:
ep_name = heading
break
parent = parent.parents().eq(0)
except Exception:
ep_name = ''
base_name = ep_name if ep_name else f"视频{c}"
name = base_name
count = 2
# Ensure the name is unique
while name in used_names:
name = f"{base_name} {count}"
count += 1
used_names.add(name)
if video_url:
self.log(f"解析到视频: {name} -> {video_url}")
print(f"解析到视频: {name} -> {video_url}")
plist.append(f"{name}${video_url}")
except:
continue
if plist:
self.log(f"拼装播放列表,共{len(plist)}")
print(f"拼装播放列表,共{len(plist)}")
vod['vod_play_url'] = '#'.join(plist)
else:
vod['vod_play_url'] = f"未找到视频源${url}"
except Exception as e:
vod['vod_play_url'] = f"视频解析失败${url}"
return {'list': [vod]}
except Exception as e:
print(f"detailContent error: {e}")
return {'list': [{'vod_play_from': '51吸瓜', 'vod_play_url': f'详情页加载失败${ids[0] if ids else ""}'}]}
def searchContent(self, key, quick, pg="1"):
try:
url = f"{self.host}/search/{key}/{pg}" if pg != "1" else f"{self.host}/search/{key}/"
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=15)
if response.status_code != 200:
return {'list': [], 'page': pg}
data = self.getpq(response.text)
videos = self.getlist(data('#archive article a, #index article a'))
return {'list': videos, 'page': pg}
except Exception as e:
print(f"searchContent error: {e}")
return {'list': [], 'page': pg}
def playerContent(self, flag, id, vipFlags):
url = id
p = 1
if self.isVideoFormat(url):
# m3u8/mp4 direct play; when using proxy setting, wrap to proxy for m3u8
if '.m3u8' in url:
url = self.proxy(url)
p = 0
self.log(f"播放请求: parse={p}, url={url}")
print(f"播放请求: parse={p}, url={url}")
return {'parse': p, 'url': url, 'header': self.headers}
def localProxy(self, param):
if param.get('type') == 'img':
res=requests.get(param['url'], headers=self.headers, proxies=self.proxies, timeout=10)
return [200,res.headers.get('Content-Type'),self.aesimg(res.content)]
elif param.get('type') == 'm3u8':return self.m3Proxy(param['url'])
else:return self.tsProxy(param['url'])
def proxy(self, data, type='m3u8'):
if data and len(self.proxies):return f"{self.getProxyUrl()}&url={self.e64(data)}&type={type}"
else:return data
def m3Proxy(self, url):
url=self.d64(url)
ydata = requests.get(url, headers=self.headers, proxies=self.proxies, allow_redirects=False)
data = ydata.content.decode('utf-8')
if ydata.headers.get('Location'):
url = ydata.headers['Location']
data = requests.get(url, headers=self.headers, proxies=self.proxies).content.decode('utf-8')
lines = data.strip().split('\n')
last_r = url[:url.rfind('/')]
parsed_url = urlparse(url)
durl = parsed_url.scheme + "://" + parsed_url.netloc
iskey=True
for index, string in enumerate(lines):
if iskey and 'URI' in string:
pattern = r'URI="([^"]*)"'
match = re.search(pattern, string)
if match:
lines[index] = re.sub(pattern, f'URI="{self.proxy(match.group(1), "mkey")}"', string)
iskey=False
continue
if '#EXT' not in string:
if 'http' not in string:
domain = last_r if string.count('/') < 2 else durl
string = domain + ('' if string.startswith('/') else '/') + string
lines[index] = self.proxy(string, string.split('.')[-1].split('?')[0])
data = '\n'.join(lines)
return [200, "application/vnd.apple.mpegur", data]
def tsProxy(self, url):
url = self.d64(url)
data = requests.get(url, headers=self.headers, proxies=self.proxies, stream=True)
return [200, data.headers['Content-Type'], data.content]
def e64(self, text):
try:
text_bytes = text.encode('utf-8')
encoded_bytes = b64encode(text_bytes)
return encoded_bytes.decode('utf-8')
except Exception as e:
print(f"Base64编码错误: {str(e)}")
return ""
def d64(self, encoded_text):
try:
encoded_bytes = encoded_text.encode('utf-8')
decoded_bytes = b64decode(encoded_bytes)
return decoded_bytes.decode('utf-8')
except Exception as e:
print(f"Base64解码错误: {str(e)}")
return ""
def get_working_host(self):
"""Get working host from known dynamic URLs"""
# Known working URLs from the dynamic gateway
dynamic_urls = [
'https://artist.vgwtswi.xyz',
'https://ability.vgwtswi.xyz',
'https://am.vgwtswi.xyz'
]
# Test each URL to find a working one
for url in dynamic_urls:
try:
response = requests.get(url, headers=self.headers, proxies=self.proxies, timeout=10)
if response.status_code == 200:
# Verify it has the expected content structure
data = self.getpq(response.text)
articles = data('#index article a')
if len(articles) > 0:
self.log(f"选用可用站点: {url}")
print(f"选用可用站点: {url}")
return url
except Exception as e:
continue
# Fallback to first URL if none work (better than crashing)
self.log(f"未检测到可用站点,回退: {dynamic_urls[0]}")
print(f"未检测到可用站点,回退: {dynamic_urls[0]}")
return dynamic_urls[0]
def getlist(self, data, tid=''):
videos = []
l = '/mrdg' in tid
for k in data.items():
a = k.attr('href')
b = k('h2').text()
# Some pages might not include datePublished; use a fallback
c = k('span[itemprop="datePublished"]').text() or k('.post-meta, .entry-meta, time').text()
if a and b:
videos.append({
'vod_id': f"{a}{'@folder' if l else ''}",
'vod_name': b.replace('\n', ' '),
'vod_pic': self.getimg(k('script').text()),
'vod_remarks': c or '',
'vod_tag': 'folder' if l else '',
'style': {"type": "rect", "ratio": 1.33}
})
return videos
def getfod(self, id):
url = f"{self.host}{id}"
data = self.getpq(requests.get(url, headers=self.headers, proxies=self.proxies).text)
vdata=data('.post-content[itemprop="articleBody"]')
r=['.txt-apps','.line','blockquote','.tags','.content-tabs']
for i in r:vdata.remove(i)
p=vdata('p')
videos=[]
for i,x in enumerate(vdata('h2').items()):
c=i*2
videos.append({
'vod_id': p.eq(c)('a').attr('href'),
'vod_name': p.eq(c).text(),
'vod_pic': f"{self.getProxyUrl()}&url={p.eq(c+1)('img').attr('data-xkrkllgl')}&type=img",
'vod_remarks':x.text()
})
return videos
def getimg(self, text):
match = re.search(r"loadBannerDirect\('([^']+)'", text)
if match:
url = match.group(1)
return f"{self.getProxyUrl()}&url={url}&type=img"
else:
return ''
def aesimg(self, word):
key = b'f5d965df75336270'
iv = b'97b60394abc2fbe1'
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = unpad(cipher.decrypt(word), AES.block_size)
return decrypted
def getpq(self, data):
try:
return pq(data)
except Exception as e:
print(f"{str(e)}")
return pq(data.encode('utf-8'))
+181
View File
@@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
import json
import sys
import re
import html as html_parser
from urllib.parse import quote
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
# 建议使用原站或稳定的镜像地址
self.host = "https://www.hanime1.nl"
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': f'{self.host}/',
}
def getName(self):
return "Hanime"
def homeContent(self, filter):
classes = [
{'type_name': '最新上市', 'type_id': 'latest_rank'},
{'type_name': '裏番', 'type_id': '裏番'},
{'type_name': '泡麵番', 'type_id': '泡麵番'},
{'type_name': 'Motion Anime', 'type_id': 'Motion Anime'},
{'type_name': '3DCG', 'type_id': '3DCG'},
{'type_name': '2D動畫', 'type_id': '2D動畫'},
{'type_name': 'AI生成', 'type_id': 'AI生成'},
{'type_name': 'MMD', 'type_id': 'MMD'},
{'type_name': 'Cosplay', 'type_id': 'Cosplay'},
{'type_name': '本日排行', 'type_id': 'daily_rank'},
{'type_name': '本週排行', 'type_id': 'weekly_rank'},
{'type_name': '本月排行', 'type_id': 'monthly_rank'}
]
sort_options = [
{"n": "最新上市", "v": "最新上市"},
{"n": "最新上傳", "v": "最新上傳"},
{"n": "本日排行", "v": "本日排行"},
{"n": "本週排行", "v": "本週排行"},
{"n": "本月排行", "v": "本月排行"},
{"n": "觀看次數", "v": "觀看次數"}
]
filters = {}
for item in classes:
filters[item['type_id']] = [{"key": "sort", "name": "排序", "value": sort_options}]
return {'class': classes, 'filters': filters}
def categoryContent(self, tid, pg, filter, extend):
page = int(pg)
# 映射表:将 type_id 映射为网站识别的 sort 字符串
rank_map = {
'latest_rank': '最新上市',
'daily_rank': '本日排行',
'weekly_rank': '本週排行',
'monthly_rank': '本月排行'
}
# 核心修复逻辑:
# 1. 如果 extend 中有用户选下的 sort,则优先使用
# 2. 如果没有,则尝试从 rank_map 匹配 tid 的默认排序
# 3. 最后保底使用 '最新上市'
sort = extend.get('sort')
if not sort:
sort = rank_map.get(tid, '最新上市')
# 构造请求 URL
if tid in rank_map:
# 排行类标签通常不需要 genre 参数
url = f"{self.host}/search?sort={quote(sort)}&page={page}"
else:
# 普通分类标签(如:裏番)需要 genre 和 sort 同时存在
url = f"{self.host}/search?genre={quote(tid)}&sort={quote(sort)}&page={page}"
try:
content = self.fetch(url, headers=self.headers).text
vods = self.parse_vod_list(content)
# 提取总页数:寻找类似 "第 1 / 100 页" 的结构
pc_match = re.search(r'\/ (\d+)', content)
pagecount = int(pc_match.group(1)) if pc_match else page + 1
return {'list': vods, 'page': page, 'pagecount': pagecount}
except Exception:
return {'list': []}
def parse_vod_list(self, html):
vods = []
seen = set()
# 模式1:搜索结果卡片布局
p1 = re.compile(r'class="video-item-container".*?href="[^"]*v=(\d+)".*?src="([^"]+)".*?class="duration">(.*?)<.*?class="title">(.*?)<', re.S)
# 模式2:首页行列表布局
p2 = re.compile(r'href="[^"]*watch\?v=(\d+)".*?src="([^"]+)".*?class="home-rows-videos-title"[^>]*>(.*?)</div>', re.S)
# 匹配卡片
for vid, pic, dur, title in p1.findall(html):
if vid not in seen:
seen.add(vid)
vods.append({
"vod_id": vid,
"vod_name": html_parser.unescape(title).strip(),
"vod_pic": pic,
"vod_remarks": dur.strip()
})
# 匹配行
if not vods:
for vid, pic, title in p2.findall(html):
if vid not in seen:
seen.add(vid)
vods.append({
"vod_id": vid,
"vod_name": html_parser.unescape(title).strip(),
"vod_pic": pic,
"vod_remarks": ""
})
return vods
def detailContent(self, ids):
vid = ids[0]
url = f"{self.host}/watch?v={vid}"
try:
html = self.fetch(url, headers=self.headers).text
title_match = re.search(r'<meta property="og:title" content="(.*?)"', html)
pic_match = re.search(r'<meta property="og:image" content="(.*?)"', html)
title = title_match.group(1) if title_match else "未知标题"
pic = pic_match.group(1) if pic_match else ""
# 解析播放源
sources = re.findall(r'<source[^>]+src="([^"]+)"', html)
if not sources:
sources = re.findall(r'https?://[^\s"\'<>]+?\.mp4[^\s"\'<>]*', html)
play_parts = []
seen_urls = set()
for s_url in sources:
s_url = html_parser.unescape(s_url).replace('&amp;', '&')
if s_url in seen_urls: continue
seen_urls.add(s_url)
if '1080' in s_url: tag = "1080P"
elif '720' in s_url: tag = "720P"
else: tag = "标清"
play_parts.append(f"{tag}${s_url}")
# 排序:高清在前
play_parts.sort(key=lambda x: 0 if "1080" in x else (1 if "720" in x else 2))
return {'list': [{
"vod_id": vid,
"vod_name": title,
"vod_pic": pic,
"vod_play_from": "Hanime",
"vod_play_url": "#".join(play_parts)
}]}
except Exception:
return {'list': []}
def searchContent(self, key, quick, pg="1", extend=None):
url = f"{self.host}/search?query={quote(key)}&page={pg}"
try:
html = self.fetch(url, headers=self.headers).text
return {'list': self.parse_vod_list(html), 'page': pg}
except Exception:
return {'list': []}
def playerContent(self, flag, id, vipFlags):
# 必须伪造原站 Referer 绕过防盗链
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': 'https://www.hanime1.nl/',
'Connection': 'keep-alive'
}
return {'parse': 0, 'url': id, 'header': headers}
Binary file not shown.
+392
View File
@@ -0,0 +1,392 @@
# -*- coding: utf-8 -*-
import json
import re
import requests
from pyquery import PyQuery as pq
import sys
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
host = 'https://cn.avjoy.me'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
'referer': 'https://cn.avjoy.me/',
'origin': 'https://cn.avjoy.me',
}
def init(self, extend=''):
self.proxies = json.loads(extend).get('proxy', {}) if extend else {}
self.session = requests.Session()
self.session.headers.update(self.headers)
def getName(self):
return "hohoj"
def fetch(self, url, params=None):
try:
resp = self.session.get(url, headers=self.session.headers, params=params,
proxies=self.proxies, timeout=10, allow_redirects=True)
return resp.text
except:
return ''
def fetch_resp(self, url, params=None, extra_headers=None, stream=False):
try:
hdrs = self.session.headers.copy()
if extra_headers:
hdrs.update(extra_headers)
return self.session.get(url, headers=hdrs, params=params,
proxies=self.proxies, timeout=10,
allow_redirects=True, stream=stream)
except Exception:
return None
def homeContent(self, filter):
html = self.fetch(self.host)
return {
'class': [
{'type_name': '最新上传视频', 'type_id': 'videos'},
{'type_name': '视频', 'type_id': 'videos'},
{'type_name': '类别', 'type_id': 'categories'},
{'type_name': '标签', 'type_id': 'tags'}
],
'filters': self.get_filters(),
'list': self.parse_videos_from_list_html(pq(html))
}
def get_filters(self):
return {}
def categoryContent(self, tid, pg, filter, extend):
norm = tid.lstrip('/') if not tid.startswith('http') else tid
if '?' in norm and not norm.startswith('http'):
norm = norm.split('?', 1)[0]
url = f"{self.host}/{norm}" if not norm.startswith('http') else norm
params = (extend or {}).copy()
try:
if int(pg) > 1:
params['page'] = pg
except:
pass
params.pop('o', None)
html = self.fetch(url, params)
doc = pq(html)
m_cur = re.search(r"current_url\s*=\s*\"([^\"]+)\"", html)
if m_cur:
base_path = m_cur.group(1)
if base_path.startswith('/videos/') or base_path.startswith('/search/videos/'):
url = f"{self.host}{base_path}"
html = self.fetch(url, params)
doc = pq(html)
def uniq_append(items, entry):
key = (entry.get('vod_id'), entry.get('vod_name'))
if key and key not in {(i.get('vod_id'), i.get('vod_name')) for i in items}:
items.append(entry)
if tid == 'categories':
items = []
for card in doc('div.content-left .row.content-row > div').items():
a = card.find('a').eq(0)
href = (a.attr('href') or '').strip()
name = card.find('.category-title .title-truncate').text().strip()
pic = (card.find('.thumb-overlay img').attr('src') or '').strip()
if href and name and href.startswith('/videos/'):
cat_id = href.lstrip('/')
if pic and pic.startswith('/'):
pic = f"{self.host}{pic}"
uniq_append(items, {
'vod_id': cat_id,
'vod_name': name,
'vod_pic': pic,
'vod_tag': 'folder',
'style': {"type": "rect", "ratio": 1.1}
})
for a in doc('.dropdown-menu.multi-column-dropdown a').items():
href = (a.attr('href') or '').strip()
name = a.text().strip()
if href.startswith('/videos/') and name:
uniq_append(items, {
'vod_id': href.lstrip('/'),
'vod_name': name,
'vod_pic': '',
'vod_tag': 'folder',
'style': {"type": "rect", "ratio": 1.1}
})
return {
'list': items,
'page': '1',
'pagecount': 1,
'limit': 90,
'total': len(items)
}
if tid == 'tags':
items = []
for a in doc('.popular-tag a').items():
name = a.text().strip()
href = (a.attr('href') or '').strip()
if href.startswith('/search/videos/') and name:
uniq_append(items, {
'vod_id': href.lstrip('/'),
'vod_name': name,
'vod_tag': 'folder',
'style': {"type": "rect", "ratio": 1.0}
})
for a in doc('.trending-searches a').items():
name = a.text().strip()
href = (a.attr('href') or '').strip()
if href.startswith('/search/videos/') and name:
uniq_append(items, {
'vod_id': href.lstrip('/'),
'vod_name': name,
'vod_tag': 'folder',
'style': {"type": "rect", "ratio": 1.0}
})
return {
'list': items,
'page': '1',
'pagecount': 1,
'limit': 90,
'total': len(items)
}
videos = self.parse_videos_from_list_html(doc)
if not videos:
fallback = []
for a in doc('a[href^="/video/"]').items():
href = a.attr('href')
title = a.text().strip()
img = a.parents().find('img').eq(0).attr('src')
if href and title:
uniq_append(fallback, {
'vod_id': href,
'vod_name': title,
'vod_pic': img,
'style': {"type": "rect", "ratio": 1.5}
})
videos = fallback
pagecount = 1
try:
pagecount = doc('.pagination a').length or 1
except:
pagecount = 1
return {
'list': videos,
'page': pg,
'pagecount': pagecount,
'limit': 90,
'total': 999999
}
def detailContent(self, ids):
vid = ids[0]
url = f"{self.host}{vid}" if vid.startswith('/') else f"{self.host}/{vid}"
html = self.fetch(url)
data = pq(html)
title = data('h1').text() or data('title').text() or ''
title = re.sub(r'\s*HoHoJ.*$', '', title)
title = re.sub(r'\s*\|.*$', '', title)
title = title.strip()
poster = data('video#video').attr('poster') or data('meta[property="og:image"]').attr('content')
vod_year = data('.info span').eq(-1).text()
m_vid = re.search(r"video_id\s*=\s*\"(\d+)\"", html)
video_id = m_vid.group(1) if m_vid else ''
if not video_id:
m_url_id = re.search(r"/video/(\d+)", url) or re.search(r"/video/(\d+)", html)
video_id = m_url_id.group(1) if m_url_id else ''
m_vkey = re.search(r"/embed/([a-zA-Z0-9]+)", html)
vkey = m_vkey.group(1) if m_vkey else ''
play_id = video_id or vkey
vod = {
'vod_id': vid,
'vod_name': title,
'vod_play_from': '撸出血',
'vod_play_url': f"{title}${play_id or ''}",
'vod_pic': poster,
'vod_year': vod_year,
}
tags = []
for tag in data('a.tag').items():
name = tag.text().strip()
href = tag.attr('href')
if name and href:
tags.append(f'[a=cr:{json.dumps({"id": href, "name": name})}/]{name}[/a]')
if tags:
vod['vod_content'] = ' '.join(tags)
director_name = data('a[href^="/user/"]').text().strip()
if director_name:
try:
from urllib.parse import quote
director_href = f"/search/videos/{quote(director_name)}"
except:
director_href = f"/search/videos/{director_name}"
director_link = f"[a=cr:{json.dumps({'id': director_href, 'name': director_name})}/]{director_name}[/a]"
vod['vod_content'] = (vod.get('vod_content', '') + ('\n' if vod.get('vod_content') else '') + '导演:' + director_link)
intro = (data('section.video-description').text() or '').strip()
if not intro:
intro = (data('meta[name="description"]').attr('content') or '').strip()
if intro:
vod['vod_content'] = (vod.get('vod_content', '') + ('\n' if vod.get('vod_content') else '') + '影片介绍:' + intro)
return {'list': [vod]}
def searchContent(self, key, quick, pg="1"):
params = {}
try:
if int(pg) > 1:
params['page'] = pg
except:
pass
url = f"{self.host}/search/videos/{requests.utils.quote(key)}"
html = self.fetch(url, params)
if not html:
html = self.fetch(f"{self.host}/search", {'text': key, **params})
return {'list': self.parse_videos_from_list_html(pq(html)), 'page': pg}
def playerContent(self, flag, id, vipFlags):
def pick_best_source(html_text):
sources = []
for m in re.finditer(r"<source[^>]+src=\"([^\"]+)\"[^>]*>", html_text):
frag = html_text[m.start():m.end()]
src = m.group(1)
res_m = re.search(r"res=\'?(\d+)\'?", frag)
label_m = re.search(r"label=\'([^\']+)\'", frag)
res = int(res_m.group(1)) if res_m else 0
label = label_m.group(1) if label_m else ''
sources.append((res, label, src))
if sources:
sources.sort(reverse=True)
return sources[0][2]
mv = re.search(r"<video[^>]+src=\"([^\"]+)\"", html_text)
if mv:
return mv.group(1)
mv2 = re.search(r"var\s+videoSrc\s*=\s*[\"']([^\"']+)[\"']", html_text)
if mv2:
return mv2.group(1)
doc = pq(html_text)
return doc('video source').attr('src') or doc('video').attr('src') or ''
raw = str(id).strip()
if re.match(r'^https?://', raw) and self.isVideoFormat(raw):
return {
'parse': 0,
'url': raw,
'header': {
'user-agent': self.headers['user-agent'],
'referer': self.host,
'origin': self.host,
}
}
m = re.search(r"/video/(\d+)", raw) or re.search(r"id=(\d+)", raw)
if m:
raw = m.group(1)
is_numeric = re.match(r"^\d+$", raw) is not None
video_url = ''
referer_used = ''
if is_numeric:
for path in [f"{self.host}/video/{raw}", f"{self.host}/video/{raw}/"]:
self.session.headers['referer'] = path
play_html = self.fetch(path)
video_url = pick_best_source(play_html)
if video_url:
referer_used = path
break
m_dl = re.search(r"href=\"(/download\\.php\\?id=\\d+[^\"]*label=1080p)\"", play_html)
if not m_dl:
m_dl = re.search(r"href=\"(/download\\.php\\?id=\\d+[^\"]*)\"", play_html)
if m_dl:
dl_url = f"{self.host}{m_dl.group(1)}"
resp = self.fetch_resp(dl_url, extra_headers={'referer': path}, stream=True)
if resp and resp.ok:
resp.close()
video_url = resp.url
referer_used = path
break
if not video_url:
embed_url = f"{self.host}/embed/{raw}" if not is_numeric else f"{self.host}/embed?id={raw}"
self.session.headers['referer'] = embed_url
html = self.fetch(embed_url)
v2 = pick_best_source(html)
if v2:
video_url = v2
referer_used = embed_url
return {
'parse': 0,
'url': video_url or '',
'header': {
'user-agent': self.headers['user-agent'],
'referer': referer_used or self.host,
'origin': self.host,
}
}
def parse_videos_from_list_html(self, doc: pq):
videos = []
for item in doc('.row.content-row > div').items():
link = item.find('a').eq(0).attr('href')
img = item.find('.thumb-overlay img').eq(0).attr('src')
info = item.find('.content-info').eq(0)
title = info.find('.content-title').text().strip()
duration = (item.find('.video-duration, .thumb-overlay .duration, .content-duration, .duration').eq(0).text() or '').strip()
overlay_text = (item.find('.thumb-overlay').text() or '').strip()
hd_flag = bool(item.find('.hd, .icon-hd, .hd-icon, .badge-hd, .label-hd').length) or ('HD' in overlay_text)
if not link or not title:
continue
parts = []
if hd_flag:
parts.append('HD')
if duration:
parts.append(duration)
remarks = ''.join(parts)
videos.append({
'vod_id': link,
'vod_name': re.sub(r'\s*\|.*$', '', re.sub(r'\s*HoHoJ.*$', '', title)).strip(),
'vod_pic': img,
'vod_remarks': remarks or '',
'vod_tag': '',
'style': {"type": "rect", "ratio": 1.5}
})
if not videos:
for info in doc('.content-info').items():
a = info('a').eq(0)
link = a.attr('href')
title = info('.content-title').text().strip()
if not link or not title:
continue
img = info.prev('a').find('img').attr('src') or info.prevAll('a').eq(0).find('img').attr('src')
duration = (info.parents().find('.video-duration, .thumb-overlay .duration, .content-duration, .duration').eq(0).text() or '').strip()
overlay_text = (info.parents().find('.thumb-overlay').text() or '').strip()
hd_flag = bool(info.parents().find('.hd, .icon-hd, .hd-icon, .badge-hd, .label-hd').length) or ('HD' in overlay_text)
parts = []
if hd_flag:
parts.append('HD')
if duration:
parts.append(duration)
remarks = ''.join(parts)
videos.append({
'vod_id': link,
'vod_name': re.sub(r'\s*\|.*$', '', re.sub(r'\s*HoHoJ.*$', '', title)).strip(),
'vod_pic': img,
'vod_remarks': remarks or '',
'vod_tag': '',
'style': {"type": "rect", "ratio": 1.5}
})
return videos
def isVideoFormat(self, url):
return bool(url) and (url.lower().endswith('.mp4') or url.lower().endswith('.m3u8'))
def manualVideoCheck(self):
pass
def destroy(self):
pass
def homeVideoContent(self):
pass
def localProxy(self, param):
pass
def liveContent(self, url):
pass
+315
View File
@@ -0,0 +1,315 @@
# coding = utf-8
# !/usr/bin/python
# 新时代青年 2025.06.25 getApp第三版
import re,sys,uuid,json,base64,urllib3
from Crypto.Cipher import AES
from base.spider import Spider
from Crypto.Util.Padding import pad,unpad
sys.path.append('..')
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class Spider(Spider):
xurl,key,iv,init_data,search_verify = '','','','',''
headerx = {
'User-Agent': 'okhttp/3.10.0' # okhttp/3.14.9
}
def getName(self):
return "首页"
def init(self, extend):
js1=json.loads(extend)
host = js1['host']
if not re.match(r'^https?:\/\/[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(:\d+)?(\/)?$',host):
host = self.fetch(host, headers=self.headerx, timeout=10, verify=False).text.rstrip('/')
api = js1.get('api','/api.php/getappapi')
if str(api) == '2':
api = '/api.php/qijiappapi'
self.xurl = host + api
self.key = js1['datakey']
self.iv = js1.get('dataiv',self.key)
res = self.fetch(self.xurl + '.index/initV119', headers=self.headerx, verify=False).json()
encrypted_data = res['data']
response = self.decrypt(encrypted_data)
init_data = json.loads(response)
self.init_data = init_data
self.search_verify = init_data['config'].get('system_search_verify_status',False)
def homeContent(self, filter):
kjson = self.init_data
result = {"class": [], "filters": {}}
for i in kjson['type_list']:
if not(i['type_name'] in {'全部', 'QQ', 'juo.one'} or '企鹅群' in i['type_name']):
result['class'].append({
"type_id": i['type_id'],
"type_name": i['type_name']
})
name_mapping = {'class': '类型', 'area': '地区', 'lang': '语言', 'year': '年份', 'sort': '排序'}
filter_items = []
for filter_type in i.get('filter_type_list', []):
filter_name = filter_type.get('name')
values = filter_type.get('list', [])
if not values:
continue
value_list = [{"n": value, "v": value} for value in values]
display_name = name_mapping.get(filter_name, filter_name)
key = 'by' if filter_name == 'sort' else filter_name
filter_items.append({
"key": key,
"name": display_name,
"value": value_list
})
type_id = i.get('type_id')
if filter_items:
result["filters"][str(type_id)] = filter_items
return result
def homeVideoContent(self):
videos = []
kjson = self.init_data
for i in kjson['type_list']:
for item in i['recommend_list']:
vod_id = item['vod_id']
name = item['vod_name']
pic = item['vod_pic']
remarks = item['vod_remarks']
video = {
"vod_id": vod_id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
return {'list': videos}
def categoryContent(self, cid, pg, filter, ext):
videos = []
payload = {
'area': ext.get('area','全部'),
'year': ext.get('year','全部'),
'type_id': cid,
'page': str(pg),
'sort': ext.get('sort','最新'),
'lang': ext.get('lang','全部'),
'class': ext.get('class','全部')
}
url = f'{self.xurl}.index/typeFilterVodList'
res = self.post(url=url, headers=self.headerx,data=payload, verify=False).json()
encrypted_data = res['data']
kjson = self.decrypt(encrypted_data)
kjson1 = json.loads(kjson)
for i in kjson1['recommend_list']:
id = i['vod_id']
name = i['vod_name']
pic = i['vod_pic']
remarks = i['vod_remarks']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
return {'list': videos, 'page': pg, 'pagecount': 9999, 'limit': 90, 'total': 999999}
def detailContent(self, ids):
did = ids[0]
payload = {
'vod_id': did,
}
api_endpoints = ['vodDetail', 'vodDetail2']
for endpoint in api_endpoints:
url = f'{self.xurl}.index/{endpoint}'
response = self.post(url=url, headers=self.headerx, data=payload, verify=False)
if response.status_code == 200:
response_data = response.json()
encrypted_data = response_data['data']
kjson1 = self.decrypt(encrypted_data)
kjson = json.loads(kjson1)
break
videos = []
play_form = ''
play_url = ''
lineid = 1
name_count = {}
for line in kjson['vod_play_list']:
keywords = {'防走丢', '', '防失群', '官网'}
player_show = line['player_info']['show']
if any(keyword in player_show for keyword in keywords):
player_show = f'{lineid}线'
line['player_info']['show'] = player_show
count = name_count.get(player_show, 0) + 1
name_count[player_show] = count
if count > 1:
line['player_info']['show'] = f"{player_show}{count}"
play_form += line['player_info']['show'] + '$$$'
parse = line['player_info']['parse']
parse_type = line['player_info']['parse_type']
player_parse_type = line['player_info']['player_parse_type']
kurls = ""
for vod in line['urls']:
token = 'token+' + vod['token']
kurls += f"{str(vod['name'])}${parse},{vod['url']},{token},{player_parse_type},{parse_type}#"
kurls = kurls.rstrip('#')
play_url += kurls + '$$$'
lineid += 1
play_form = play_form.rstrip('$$$')
play_url = play_url.rstrip('$$$')
videos.append({
"vod_id": did,
"vod_name": kjson['vod']['vod_name'],
"vod_actor": kjson['vod']['vod_actor'].replace('演员', ''),
"vod_director": kjson['vod'].get('vod_director', '').replace('导演', ''),
"vod_content": kjson['vod']['vod_content'],
"vod_remarks": kjson['vod']['vod_remarks'],
"vod_year": kjson['vod']['vod_year'] + '',
"vod_area": kjson['vod']['vod_area'],
"vod_play_from": play_form,
"vod_play_url": play_url
})
return {'list': videos}
def playerContent(self, flag, id, vipFlags):
url = ''
aid = id.split(',')
uid = aid[0]
kurl = aid[1]
token = aid[2].replace('token+', '')
player_parse_type = aid[3]
parse_type = aid[4]
if parse_type == '0':
res = {"parse": 0, "url": kurl, "header": {'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 14; 23113RK12C Build/SKQ1.231004.001)'}}
elif parse_type == '2':
res = {"parse": 1, "url": uid+kurl, "header": {'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 14; 23113RK12C Build/SKQ1.231004.001)'}}
elif player_parse_type == '2':
response = self.fetch(url=f'{uid}{kurl}',verify=False)
if response.status_code == 200:
kjson1 = response.json()
res = {"parse": 0, "url": kjson1['url'], "header": {'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 14; 23113RK12C Build/SKQ1.231004.001)'}}
else:
id1 = self.encrypt(kurl)
payload = {
'parse_api': uid,
'url': id1,
'player_parse_type': player_parse_type,
'token': token
}
url1 = f"{self.xurl}.index/vodParse"
response = self.post(url=url1, headers=self.headerx, data=payload, verify=False)
if response.status_code == 200:
response_data = response.json()
encrypted_data = response_data['data']
kjson = self.decrypt(encrypted_data)
kjson1 = json.loads(kjson)
kjson2 = kjson1['json']
kjson3 = json.loads(kjson2)
url = kjson3['url']
res = {"parse": 0, "playUrl": '', "url": url, "header": {'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 14; 23113RK12C Build/SKQ1.231004.001)'}}
return res
def searchContent(self, key, quick, pg="1"):
videos = []
if 'xiaohys.com' in self.xurl:
host = self.xurl.split('api.php')[0]
data = self.fetch(f'{host}index.php/ajax/suggest?mid=1&wd={key}').json()
for i in data['list']:
videos.append({
"vod_id": i['id'],
"vod_name": i['name'],
"vod_pic": i.get('pic')
})
else:
payload = {
'keywords': key,
'type_id': "0",
'page': str(pg)
}
if self.search_verify:
verifi = self.verification()
if verifi is None:
return {'list':[]}
payload['code'] = verifi['code']
payload['key'] = verifi['uuid']
url = f'{self.xurl}.index/searchList'
res = self.post(url=url, data=payload, headers=self.headerx, verify=False).json()
if not res.get('data'):
return {'list':[] ,'msg': res.get('msg')}
encrypted_data = res['data']
kjson = self.decrypt(encrypted_data)
kjson1 = json.loads(kjson)
for i in kjson1['search_list']:
id = i['vod_id']
name = i['vod_name']
pic = i['vod_pic']
remarks = i['vod_year'] + ' ' + i['vod_class']
videos.append({
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
})
return {'list': videos, 'page': pg, 'pagecount': 9999, 'limit': 90, 'total': 999999}
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def decrypt(self, encrypted_data_b64):
key_bytes = self.key.encode('utf-8')
iv_bytes = self.iv.encode('utf-8')
encrypted_data = base64.b64decode(encrypted_data_b64)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
decrypted_padded = cipher.decrypt(encrypted_data)
decrypted = unpad(decrypted_padded, AES.block_size)
return decrypted.decode('utf-8')
def encrypt(self, sencrypted_data):
key_bytes = self.key.encode('utf-8')
iv_bytes = self.iv.encode('utf-8')
data_bytes = sencrypted_data.encode('utf-8')
padded_data = pad(data_bytes, AES.block_size)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted_bytes = cipher.encrypt(padded_data)
encrypted_data_b64 = base64.b64encode(encrypted_bytes).decode('utf-8')
return encrypted_data_b64
def ocr(self, base64img):
dat2 = self.post("https://api.nn.ci/ocr/b64/text", data=base64img, headers=self.headerx, verify=False).text
if dat2:
return dat2
else:
return None
def verification(self):
random_uuid = str(uuid.uuid4())
dat = self.fetch(f'{self.xurl}.verify/create?key={random_uuid}',headers=self.headerx, verify=False).content
base64_img = base64.b64encode(dat).decode('utf-8')
if not dat:
return None
code = self.ocr(base64_img)
if not code:
return None
code = self.replace_code(code)
if not (len(code) == 4 and code.isdigit()):
return None
return {'uuid': random_uuid, 'code': code}
def replace_code(self, text):
replacements = {'y': '9', '': '0', 'q': '0', 'u': '0', 'o': '0', '>': '1', 'd': '0', 'b': '8', '': '2','D': '0', '': '5'}
if len(text) == 3:
text = text.replace('566', '5066')
text = text.replace('066', '1666')
return ''.join(replacements.get(c, c) for c in text)
+354
View File
@@ -0,0 +1,354 @@
var il = 'jsjiami.com.v6',
il_ = ['il'],
lIIIl1ll = [il, '\x6f\x6b\x68\x74\x74\x70\x2f\x33\x2e\x31\x35', '\x69\x6e\x64\x65\x78\x4f\x66', '\x24\x24\x24', '\x73\x70\x6c\x69\x74', '\x74\x72\x69\x6d', '\x26\x26\x26', '\x3a\x2f\x2f', '\x6c\x6f\x67', '\x70\x69\x63\x55\x72\x6c\x3a\x20', '\x72\x65\x70\x6c\x61\x63\x65', '\x70\x75\x73\x68', '\x2f\x66\x69\x6c\x65\x2f\x6c\x69\x76\x65\x73\x6f\x75\x72\x63\x65\x6c\x69\x73\x74', '\x2f\x6c\x69\x76\x65\x73\x6f\x75\x72\x63\x65\x6c\x69\x73\x74', '\x47\x45\x54', '\x70\x61\x72\x73\x65', '\x63\x6f\x6e\x74\x65\x6e\x74', '\x73\x75\x62\x73\x74\x72\x69\x6e\x67', '\x6c\x61\x73\x74\x49\x6e\x64\x65\x78\x4f\x66', '\x6e\x61\x6d\x65', '\x75\x72\x6c', '\x3d\x3d\x3d\x3d\x20\x3e\x3e\x3e\x20', '\x73\x74\x72\x69\x6e\x67\x69\x66\x79', '\x65\x78\x65\x63', '\x74\x65\x73\x74', '\x6d\x61\x74\x63\x68', '\x63\x68\x61\x6e\x6e\x65\x6c', '\x2c\x23\x67\x65\x6e\x72\x65\x23\x0a', '\x75\x72\x6c\x73', '\x64\x61\x74\x61', '\x64\x61\x74\x61\x6c\x69\x73\x74', '\x70\x72\x6f\x76', '\x6c\x69\x73\x74', '\x2d\x2d\x2d', '\x6c\x69\x6e\x65', '\x77\x65\x62\x50\x69\x63\x55\x72\x6c\x3a\x20', '\x23\x45\x58\x54\x4d\x33\x55', '\x22\x63\x68\x61\x6e\x6e\x65\x6c\x22', '\x22\x75\x72\x6c\x73\x22', '\x22\x64\x61\x74\x61\x6c\x69\x73\x74\x22', '\x6c\x65\x6e\x67\x74\x68', '\x23\x67\x65\x6e\x72\x65\x23', '\x7b\x6e\x61\x6d\x65\x7d', '\x7b\x63\x61\x74\x65\x7d', '\u76f4\u64ad\u5217\u8868', '\x6e\x75\x6c\x6c', '\x74\x79\x70\x65\x5f\x69\x64', '\x76\x6f\x64\x5f\x70\x6c\x61\x79\x5f\x75\x72\x6c', '\x68\x61\x73\x4f\x77\x6e\x50\x72\x6f\x70\x65\x72\x74\x79', '\x6a\x6f\x69\x6e', '\x76\x6f\x64\x5f\x70\x6c\x61\x79\x5f\x66\x72\x6f\x6d', '\x6a\x43\x50\x73\x4e\x6a\x77\x69\x4a\x61\x51\x6d\x69\x2e\x63\x6f\x4e\x4f\x6d\x47\x72\x2e\x76\x56\x36\x41\x46\x67\x79\x74\x74\x3d\x3d'];
function Ii1l1III(_0x3b13df, _0x346a54) {
_0x3b13df = ~~'0x' ['concat'](_0x3b13df['slice'](0x0));
var _0x4db44b = lIIIl1ll[_0x3b13df];
return _0x4db44b;
};
(function(_0x209161, _0x5eaa4a) {
var _0x42ecdf = 0x0;
for (_0x5eaa4a = _0x209161['shift'](_0x42ecdf >> 0x2); _0x5eaa4a && _0x5eaa4a !== (_0x209161['pop'](_0x42ecdf >> 0x3) + '')['replace'](/[CPNwJQNOGrVAFgytt=]/g, ''); _0x42ecdf++) {
_0x42ecdf = _0x42ecdf ^ 0x127efd;
}
}(lIIIl1ll, Ii1l1III));
let headers = {
'User-Agent': Ii1l1III('0')
};
let classes = [];
let cates = {};
let picUrl = '';
let webPaths = {};
function init(IiIIi1i) {
let i1Ii11I1 = '';
if (IiIIi1i[Ii1l1III('1')](Ii1l1III('2')) > 0x0) {
i1Ii11I1 = IiIIi1i[Ii1l1III('3')](Ii1l1III('2'))[0x0][Ii1l1III('4')]();
IiIIi1i = IiIIi1i[Ii1l1III('3')](Ii1l1III('2'))[0x1][Ii1l1III('4')]();
}
if (IiIIi1i[Ii1l1III('1')](Ii1l1III('5')) > 0x0) {
picUrl = IiIIi1i[Ii1l1III('3')](Ii1l1III('5'))[0x1][Ii1l1III('4')]();
if (picUrl[Ii1l1III('1')](Ii1l1III('6')) < 0x0) {
picUrl = i1Ii11I1 + picUrl;
}
IiIIi1i = IiIIi1i[Ii1l1III('3')](Ii1l1III('5'))[0x0][Ii1l1III('4')]();
}
console[Ii1l1III('7')](Ii1l1III('8') + picUrl);
let IIlIlI1I = IiIIi1i[Ii1l1III('3')]('\x23');
for (const IlII1I1 of IIlIlI1I) {
if (IlII1I1[Ii1l1III('1')]('\x24') > 0x0) {
let illIl111 = IlII1I1;
let Ill1iIi = IlII1I1[Ii1l1III('3')]('\x24')[0x0];
if (illIl111[Ii1l1III('1')](Ii1l1III('6')) < 0x0) {
illIl111 = illIl111[Ii1l1III('9')]('\x24', '\x24' + i1Ii11I1);
}
classes[Ii1l1III('a')]({
'type_id': illIl111,
'type_name': Ill1iIi[Ii1l1III('9')]('\x21\x21', '')
});
} else {
let II1lIlli = IlII1I1;
if (II1lIlli[Ii1l1III('1')](Ii1l1III('6')) < 0x0) {
II1lIlli = i1Ii11I1 + II1lIlli;
}
II1lIlli = II1lIlli[Ii1l1III('9')](Ii1l1III('b'), Ii1l1III('c'));
let Illi11ll = req(II1lIlli, {
'\x6d\x65\x74\x68\x6f\x64': Ii1l1III('d'),
'\x68\x65\x61\x64\x65\x72\x73': headers
});
try {
let l1lIiill = JSON[Ii1l1III('e')](Illi11ll[Ii1l1III('f')]);
let lillI11l = II1lIlli[Ii1l1III('10')](0x0, II1lIlli[Ii1l1III('11')]('\x2f') + 0x1);
for (const i1iilII1 of l1lIiill) {
let Iillil = i1iilII1[Ii1l1III('12')];
let lI1iIl = i1iilII1[Ii1l1III('13')];
let illIl111 = Iillil + '\x24' + (lI1iIl[Ii1l1III('1')](Ii1l1III('6')) < 0x0 ? lillI11l : '') + lI1iIl;
classes[Ii1l1III('a')]({
'type_id': illIl111,
'type_name': Iillil[Ii1l1III('9')]('\x21\x21', '')
});
webPaths[illIl111] = lillI11l;
}
} catch (Ii1Ii11) {
console[Ii1l1III('7')](Ii1l1III('14') + Ii1Ii11);
}
}
}
}
function home(I1iiIiIl) {
return JSON[Ii1l1III('15')]({
'class': classes,
'filters': null
});
}
function parseM3u(iIi1Ii1I, I1IlIIIi) {
let iI1iiIii = {};
let iiI11111 = /(#EXTINF:.+?),([^,]+?)\s*\n(.+?)\s*\n/g;
let ii1iilil = null;
while ((ii1iilil = iiI11111[Ii1l1III('16')](iIi1Ii1I)) != null) {
let lllli1iI = ii1iilil[0x1];
let il1Ili1I = ii1iilil[0x2];
let liIlll1l = ii1iilil[0x3];
if (il1Ili1I == null || liIlll1l == null || il1Ili1I == '' || liIlll1l == '') {
continue;
}
il1Ili1I = il1Ili1I[Ii1l1III('4')]();
liIlll1l = liIlll1l[Ii1l1III('4')]();
let IiI1lI1l = I1IlIIIi;
let ilIl1i1i = /group-title="(.*?)"/;
if (ilIl1i1i[Ii1l1III('17')](lllli1iI)) {
IiI1lI1l = lllli1iI[Ii1l1III('18')](ilIl1i1i)[0x1];
}
if (!iI1iiIii[IiI1lI1l]) {
iI1iiIii[IiI1lI1l] = [];
}
iI1iiIii[IiI1lI1l][Ii1l1III('a')](il1Ili1I + '\x2c' + liIlll1l);
}
let ll11III1 = '';
for (const li1Ili in iI1iiIii) {
ll11III1 += li1Ili + '\x0a';
let IlIil1ll = iI1iiIii[li1Ili];
for (const li1iI11 of IlIil1ll) {
ll11III1 += li1iI11 + '\x0a';
}
}
return ll11III1;
}
function parseFm(IliiIl1I) {
let lliiI1i1 = '';
let Iii1ll = JSON[Ii1l1III('e')](IliiIl1I);
for (const i1lIlli1 of Iii1ll) {
let I111Il1l = i1lIlli1[Ii1l1III('12')];
let ilI11li = i1lIlli1[Ii1l1III('19')];
lliiI1i1 += I111Il1l + Ii1l1III('1a');
for (const iiilI1iI of ilI11li) {
let I11111l1 = iiilI1iI[Ii1l1III('12')];
let IlI1l1I1 = iiilI1iI[Ii1l1III('1b')];
for (const l1II1lll of IlI1l1I1) {
lliiI1i1 += I11111l1 + '\x2c' + l1II1lll + '\x0a';
}
}
}
return lliiI1i1;
}
function parseLu(iIliI1lI) {
let IIlilI1i = '';
let I11ilI1i = JSON[Ii1l1III('e')](iIliI1lI)[Ii1l1III('1c')];
for (const i1Ii1l1 of I11ilI1i[Ii1l1III('1d')]) {
let I11111l = i1Ii1l1[Ii1l1III('1e')];
let IiIiii1l = i1Ii1l1[Ii1l1III('1f')];
IIlilI1i += I11111l + Ii1l1III('1a');
for (const l1111lI of IiIiii1l) {
let lIlI1iI = l1111lI[Ii1l1III('12')];
let ll11i1II = l1111lI[Ii1l1III('1b')];
for (const Iliilii of ll11i1II) {
IIlilI1i += lIlI1iI + Ii1l1III('20') + Iliilii[Ii1l1III('21')] + '\x2c' + Iliilii[Ii1l1III('13')] + '\x0a';
}
}
}
return IIlilI1i;
}
function getCateData(IliI1i) {
let iI1I1I1I = picUrl;
if (IliI1i[Ii1l1III('1')](Ii1l1III('5')) > 0x0) {
iI1I1I1I = IliI1i[Ii1l1III('3')](Ii1l1III('5'))[0x1][Ii1l1III('4')]();
if (iI1I1I1I[Ii1l1III('1')](Ii1l1III('6')) < 0x0 && webPaths[IliI1i]) {
iI1I1I1I = webPaths[IliI1i] + iI1I1I1I;
}
IliI1i = IliI1i[Ii1l1III('3')](Ii1l1III('5'))[0x0][Ii1l1III('4')]();
}
console[Ii1l1III('7')](Ii1l1III('22') + iI1I1I1I);
let ll1iIiiI = IliI1i[Ii1l1III('3')]('\x24')[0x1];
let i1I1l1i = IliI1i[Ii1l1III('3')]('\x24')[0x0];
if (!cates[IliI1i]) {
cates[IliI1i] = [];
let iIl11Iii = headers;
if (ll1iIiiI[Ii1l1III('1')]('\x7c') > 0x0) {
let ii111I1I = decodeURIComponent(ll1iIiiI[Ii1l1III('3')]('\x7c')[0x1]);
ll1iIiiI = ll1iIiiI[Ii1l1III('3')]('\x7c')[0x0];
for (const II1Ii1l of ii111I1I[Ii1l1III('3')]('\x26')) {
if (II1Ii1l[Ii1l1III('1')]('\x3d') > 0x0) {
let lI1lliii = II1Ii1l[Ii1l1III('3')]('\x3d')[0x0];
let I11Iii1i = II1Ii1l[Ii1l1III('3')]('\x3d')[0x1];
iIl11Iii[lI1lliii] = I11Iii1i;
}
}
}
let I111lilI = req(ll1iIiiI, {
'\x6d\x65\x74\x68\x6f\x64': Ii1l1III('d'),
'\x68\x65\x61\x64\x65\x72\x73': iIl11Iii
});
I111lilI = I111lilI[Ii1l1III('f')][Ii1l1III('4')]();
if (I111lilI[Ii1l1III('1')](Ii1l1III('23')) >= 0x0) {
I111lilI = parseM3u(I111lilI, i1I1l1i);
} else if (I111lilI[Ii1l1III('1')](Ii1l1III('24')) > 0x0 && I111lilI[Ii1l1III('1')](Ii1l1III('25')) > 0x0) {
I111lilI = parseFm(I111lilI);
} else if (I111lilI[Ii1l1III('1')](Ii1l1III('26')) > 0x0 && I111lilI[Ii1l1III('1')](Ii1l1III('25')) > 0x0) {
I111lilI = parseLu(I111lilI);
}
let li1IiiII = (i1I1l1i + '\x0a' + I111lilI[Ii1l1III('9')]('\x0d', ''))[Ii1l1III('3')]('\x0a');
let lli11iI = i1I1l1i;
let IiiIIiIi = null;
let iiiI1l = '';
for (let i1ii1IIl = 0x0; i1ii1IIl < li1IiiII[Ii1l1III('27')]; i1ii1IIl++) {
let lIliIii = li1IiiII[i1ii1IIl][Ii1l1III('9')](/\s+/g, '');
if (lIliIii != '' && lIliIii[Ii1l1III('1')](Ii1l1III('6')) < 0x0 && (lIliIii[Ii1l1III('1')]('\x2c') < 0x0 || lIliIii[Ii1l1III('1')](Ii1l1III('28')) > 0x0)) {
if (iiiI1l != '') {
let ilIIIl = iI1I1I1I[Ii1l1III('9')](Ii1l1III('29'), encodeURIComponent(lli11iI))[Ii1l1III('9')](Ii1l1III('2a'), encodeURIComponent(i1I1l1i));
let ilI1ilI = ilIIIl[Ii1l1III('1')]('\x3c');
let iili1I1i = ilIIIl[Ii1l1III('11')]('\x3e');
if (ilI1ilI > -0x1 && iili1I1i > ilI1ilI) {
let I11Ilili = ilIIIl[Ii1l1III('10')](ilI1ilI, iili1I1i + 0x1);
let I1liliII = new RegExp(I11Ilili[Ii1l1III('9')](/<|>/g, ''));
let lii11liI = lli11iI[Ii1l1III('9')](I1liliII, function(Ili1lIi1, iiliII1l) {
return iiliII1l;
});
ilIIIl = ilIIIl[Ii1l1III('9')](I11Ilili, lii11liI);
console[Ii1l1III('7')](lli11iI + '\x2c\x20' + ilIIIl);
}
let IiiIIiIi = {
'vod_id': IliI1i + Ii1l1III('2') + cates[IliI1i][Ii1l1III('27')],
'vod_name': lli11iI,
'vod_pic': ilIIIl,
'vod_remarks': '',
'type_name': Ii1l1III('2b'),
'vod_year': '',
'vod_area': '',
'vod_actor': '',
'vod_director': '',
'vod_content': '',
'vod_play_from': i1I1l1i,
'vod_play_url': iiiI1l
};
cates[IliI1i][Ii1l1III('a')](IiiIIiIi);
}
lli11iI = lIliIii[Ii1l1III('3')]('\x2c')[0x0][Ii1l1III('4')]();
iiiI1l = '';
} else if (lIliIii[Ii1l1III('1')]('\x2c') > 0x0 && /http|rtmp|rtsp|rsp/ [Ii1l1III('17')](lIliIii)) {
let l1iiI1ii = lIliIii[Ii1l1III('3')]('\x2c');
if (iiiI1l != '') {
iiiI1l += '\x23';
}
iiiI1l += l1iiI1ii[0x0][Ii1l1III('4')]() + '\x24' + l1iiI1ii[0x1][Ii1l1III('4')]();
}
}
if (iiiI1l != '') {
let II1Iliil = iI1I1I1I[Ii1l1III('9')](Ii1l1III('29'), encodeURIComponent(lli11iI))[Ii1l1III('9')](Ii1l1III('2a'), encodeURIComponent(i1I1l1i));
let ilI1ilI = II1Iliil[Ii1l1III('1')]('\x3c');
let iili1I1i = II1Iliil[Ii1l1III('11')]('\x3e');
if (ilI1ilI > -0x1 && iili1I1i > ilI1ilI) {
let I11Ilili = II1Iliil[Ii1l1III('10')](ilI1ilI, iili1I1i + 0x1);
let I1liliII = new RegExp(I11Ilili[Ii1l1III('9')](/<|>/g, ''));
let lii11liI = I1liliII[Ii1l1III('17')](lli11iI) ? lli11iI[Ii1l1III('18')](I1liliII)[0x1] : Ii1l1III('2c');
II1Iliil = II1Iliil[Ii1l1III('9')](I11Ilili, lii11liI);
}
let IiiIIiIi = {
'vod_id': IliI1i + Ii1l1III('2') + cates[IliI1i][Ii1l1III('27')],
'vod_name': lli11iI,
'vod_pic': II1Iliil,
'vod_remarks': '',
'type_name': Ii1l1III('2b'),
'vod_year': '',
'vod_area': '',
'vod_actor': '',
'vod_director': '',
'vod_content': '',
'vod_play_from': i1I1l1i,
'vod_play_url': iiiI1l
};
cates[IliI1i][Ii1l1III('a')](IiiIIiIi);
}
}
return cates[IliI1i];
}
function homeVod(liIIlIl1) {
let iIl1IIii = getCateData(classes[0x0][Ii1l1III('2d')]);
let I1l1iil = JSON[Ii1l1III('15')]({
'list': iIl1IIii
});
return I1l1iil;
}
function category(I1l1i1Ii, l1IiiIli, IIi1Illi, lilIliIl) {
let IIi1i1ll = [];
if (l1IiiIli == 0x1) {
IIi1i1ll = getCateData(I1l1i1Ii);
}
let iIiiIi1i = JSON[Ii1l1III('15')]({
'list': IIi1i1ll
});
return iIiiIi1i;
}
function detail(lIl11iii) {
let I1IIIil = lIl11iii[Ii1l1III('3')](Ii1l1III('2'));
let liiiil1i = I1IIIil[0x0];
let l1l111II = liiiil1i[Ii1l1III('3')]('\x24')[0x0];
let Il1li11i = parseInt(I1IIIil[0x1]);
let Iill11Ii = getCateData(liiiil1i)[Il1li11i];
console[Ii1l1III('7')](JSON[Ii1l1III('15')](Iill11Ii));
if (l1l111II[Ii1l1III('1')]('\x21\x21') >= 0x0) {
l1l111II = l1l111II[Ii1l1III('9')]('\x21\x21', '');
const ii1l1iil = Iill11Ii[Ii1l1III('2e')][Ii1l1III('3')]('\x23');
console[Ii1l1III('7')](JSON[Ii1l1III('15')](ii1l1iil));
let i1Ili1I = {};
let IIIllli1 = {};
for (const i1IiIlIl of ii1l1iil) {
let Ill1iii1 = i1IiIlIl[Ii1l1III('3')]('\x24')[0x0];
let IIiIII11 = l1l111II;
if (Ill1iii1[Ii1l1III('1')](Ii1l1III('20')) > 0x0) {
IIiIII11 = Ill1iii1[Ii1l1III('3')](Ii1l1III('20'))[0x1];
Ill1iii1 = Ill1iii1[Ii1l1III('3')](Ii1l1III('20'))[0x0];
}
if (!i1Ili1I[Ii1l1III('2f')](Ill1iii1)) {
i1Ili1I[Ill1iii1] = 0x1;
} else {
i1Ili1I[Ill1iii1]++;
}
IIiIII11 = l1l111II + (i1Ili1I[Ill1iii1] > 0x1 ? '\x20' + i1Ili1I[Ill1iii1] : '');
if (!IIIllli1[Ii1l1III('2f')](IIiIII11)) {
IIIllli1[IIiIII11] = [];
}
IIIllli1[IIiIII11][Ii1l1III('a')](Ill1iii1 + '\x24' + i1IiIlIl[Ii1l1III('3')]('\x24')[0x1]);
}
let III1i1ii = [];
let iii1lIIi = [];
for (let iliI1I1i in IIIllli1) {
III1i1ii[Ii1l1III('a')](iliI1I1i);
iii1lIIi[Ii1l1III('a')](IIIllli1[iliI1I1i][Ii1l1III('30')]('\x23'));
}
Iill11Ii[Ii1l1III('31')] = III1i1ii[Ii1l1III('30')](Ii1l1III('2'));
Iill11Ii[Ii1l1III('2e')] = iii1lIIi[Ii1l1III('30')](Ii1l1III('2'));
}
return JSON[Ii1l1III('15')]({
'list': [Iill11Ii]
});
}
function play(l1llIIii, illiiIII, lIIIiIiI) {
return JSON[Ii1l1III('15')]({
'parse': 0x0,
'url': illiiIII
});
}
function search(I1lll, lI1iiIII) {
return null;
}
__JS_SPIDER__ = {
'\x69\x6e\x69\x74': init,
'\x68\x6f\x6d\x65': home,
'\x68\x6f\x6d\x65\x56\x6f\x64': homeVod,
'\x63\x61\x74\x65\x67\x6f\x72\x79': category,
'\x64\x65\x74\x61\x69\x6c': detail,
'\x70\x6c\x61\x79': play,
'\x73\x65\x61\x72\x63\x68': search
};;
il = 'jsjiami.com.v6';
+192
View File
@@ -0,0 +1,192 @@
#coding=utf-8
#!/usr/bin/python
import sys
sys.path.append('..')
from base.spider import Spider
import json
import time
import base64
import re
class Spider(Spider): # 元类 默认的元类 type
def getName(self):
return "央视片库"
def init(self,extend=""):
print("============{0}============".format(extend))
pass
def isVideoFormat(self,url):
pass
def manualVideoCheck(self):
pass
def homeContent(self,filter):
result = {}
cateManual = {
"动画片": "动画片",
#"特别节目": "特别节目"
}
classes = []
for k in cateManual:
classes.append({
'type_name':k,
'type_id':cateManual[k]
})
result['class'] = classes
if(filter):
result['filters'] = self.config['filter']
return result
def homeVideoContent(self):
result = {
'list':[]
}
return result
def categoryContent(self,tid,pg,filter,extend):
result = {}
month = ""
year = ""
if 'month' in extend.keys():
month = extend['month']
if 'year' in extend.keys():
year = extend['year']
if year == '':
month = ''
prefix = year + month
url="https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955899450127&area=&sc=&fc=%E5%8A%A8%E7%94%BB%E7%89%87&letter=&p={0}&n=24&serviceId=tvcctv&topv=1&t=json"
if tid=="电视剧":
url="https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955853485115&area=&sc=&fc=%E7%94%B5%E8%A7%86%E5%89%A7&year=&letter=&p={0}&n=24&serviceId=tvcctv&topv=1&t=json"
elif tid=="纪录片":
url="https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955924871139&fc=%E7%BA%AA%E5%BD%95%E7%89%87&channel=&sc=&year=&letter=&p={0}&n=24&serviceId=tvcctv&topv=1&t=json"
elif tid=="4":
url="https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955953877151&channel=&sc=&fc=%E7%89%B9%E5%88%AB%E8%8A%82%E7%9B%AE&bigday=&letter=&p={0}&n=24&serviceId=tvcctv&topv=1&t=json"
suffix = ""
jo = self.fetch(url.format(pg),headers=self.header).json()
vodList=jo["data"]["list"]
videos = []
for vod in vodList:
lastVideo =vod['url']
brief=vod['brief']
if len(brief) == 0:
brief = ' '
if len(lastVideo) == 0:
lastVideo = '_'
guid = tid+'###'+vod["title"]+'###'+lastVideo+'###'+vod['image']+'###'+brief
title = vod["title"]
img = vod['image']
videos.append({
"vod_id":guid,
"vod_name":title,
"vod_pic":img,
"vod_remarks":''
})
result['list'] = videos
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self,array):
aid = array[0].split('###')
if aid[2].find("http")<0:
return {}
tid = aid[0]
logo = aid[3]
lastVideo = aid[2]
title = aid[1]
date = aid[0]
if lastVideo == '_':
return {}
rsp = self.fetch(lastVideo)
htmlTxt=rsp.text
column_id = ""
videoList = []
patternTxt=r"'title':\s*'(.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'url':\s*'(.+?)'"
titleIndex=0
UrlIndex=3
if tid=="电视剧" or tid=="纪录片":
patternTxt=r"'title':\s*'(.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'url':\s*'(.+?)'"
titleIndex=0
UrlIndex=3
elif tid=="特别节目":
patternTxt=r'class="tp1"><a\s*href="(https://.+?)"\s*target="_blank"\s*title="(.+?)"></a></div>'
titleIndex=1
UrlIndex=0
#https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id=VIDA3YcIusJ9mh4c9mw5XHyx230113&serviceId=tvcctv//由于方式不同暂时不做
pattern = re.compile(patternTxt)
ListRe=pattern.findall(htmlTxt)
for value in ListRe:
videoList.append(value[titleIndex]+"$"+value[UrlIndex])
if len(videoList) == 0:
return {}
vod = {
"vod_id":array[0],
"vod_name":title,
"vod_pic":logo,
"type_name":tid,
"vod_year":date,
"vod_area":"",
"vod_remarks":date,
"vod_actor":"",
"vod_director":column_id,
"vod_content":aid[4]
}
vod['vod_play_from'] = 'CCTV'
vod['vod_play_url'] = "#".join(videoList)
result = {
'list':[
vod
]
}
return result
def searchContent(self,key,quick):
result = {
'list':[]
}
return result
def playerContent(self,flag,id,vipFlags):
result = {}
rsp = self.fetch(id)
htmlTxt=rsp.text
pattern = re.compile(r'var\sguid\s*=\s*"(.+?)";')
ListRe=pattern.findall(htmlTxt)
if ListRe==[]:
return result
url = "https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid={0}".format(ListRe[0])
jo = self.fetch(url,headers=self.header).json()
link = jo['hls_url'].strip()
rsp = self.fetch(link,headers=self.header)
content = rsp.text.strip()
arr = content.split('\n')
urlPrefix = self.regStr(link,'(http[s]?://[a-zA-z0-9.]+)/')
subUrl = arr[-1].split('/')
subUrl[3] = '1200'
subUrl[-1] = '1200.m3u8'
hdUrl = urlPrefix + '/'.join(subUrl)
url = urlPrefix + arr[-1]
hdRsp = self.fetch(hdUrl,headers=self.header)
if hdRsp.status_code == 200:
url = hdUrl
result["parse"] = 0
result["playUrl"] = ''
result["url"] = url
result["header"] = ''
return result
config = {
"player": {},
"filter": {"CCTV":[{"key":"cid","name":"频道","value":[{"n":"全部","v":""},{"n":"CCTV-1综合","v":"EPGC1386744804340101"},{"n":"CCTV-2财经","v":"EPGC1386744804340102"},{"n":"CCTV-3综艺","v":"EPGC1386744804340103"},{"n":"CCTV-4中文国际","v":"EPGC1386744804340104"},{"n":"CCTV-5体育","v":"EPGC1386744804340107"},{"n":"CCTV-6电影","v":"EPGC1386744804340108"},{"n":"CCTV-7国防军事","v":"EPGC1386744804340109"},{"n":"CCTV-8电视剧","v":"EPGC1386744804340110"},{"n":"CCTV-9纪录","v":"EPGC1386744804340112"},{"n":"CCTV-10科教","v":"EPGC1386744804340113"},{"n":"CCTV-11戏曲","v":"EPGC1386744804340114"},{"n":"CCTV-12社会与法","v":"EPGC1386744804340115"},{"n":"CCTV-13新闻","v":"EPGC1386744804340116"},{"n":"CCTV-14少儿","v":"EPGC1386744804340117"},{"n":"CCTV-15音乐","v":"EPGC1386744804340118"},{"n":"CCTV-16奥林匹克","v":"EPGC1634630207058998"},{"n":"CCTV-17农业农村","v":"EPGC1563932742616872"},{"n":"CCTV-5+体育赛事","v":"EPGC1468294755566101"}]},{"key":"fc","name":"分类","value":[{"n":"全部","v":""},{"n":"新闻","v":"新闻"},{"n":"体育","v":"体育"},{"n":"综艺","v":"综艺"},{"n":"健康","v":"健康"},{"n":"生活","v":"生活"},{"n":"科教","v":"科教"},{"n":"经济","v":"经济"},{"n":"农业","v":"农业"},{"n":"法治","v":"法治"},{"n":"军事","v":"军事"},{"n":"少儿","v":"少儿"},{"n":"动画","v":"动画"},{"n":"纪实","v":"纪实"},{"n":"戏曲","v":"戏曲"},{"n":"音乐","v":"音乐"},{"n":"影视","v":"影视"}]},{"key":"fl","name":"字母","value":[{"n":"全部","v":""},{"n":"A","v":"A"},{"n":"B","v":"B"},{"n":"C","v":"C"},{"n":"D","v":"D"},{"n":"E","v":"E"},{"n":"F","v":"F"},{"n":"G","v":"G"},{"n":"H","v":"H"},{"n":"I","v":"I"},{"n":"J","v":"J"},{"n":"K","v":"K"},{"n":"L","v":"L"},{"n":"M","v":"M"},{"n":"N","v":"N"},{"n":"O","v":"O"},{"n":"P","v":"P"},{"n":"Q","v":"Q"},{"n":"R","v":"R"},{"n":"S","v":"S"},{"n":"T","v":"T"},{"n":"U","v":"U"},{"n":"V","v":"V"},{"n":"W","v":"W"},{"n":"X","v":"X"},{"n":"Y","v":"Y"},{"n":"Z","v":"Z"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"month","name":"月份","value":[{"n":"全部","v":""},{"n":"12","v":"12"},{"n":"11","v":"11"},{"n":"10","v":"10"},{"n":"09","v":"09"},{"n":"08","v":"08"},{"n":"07","v":"07"},{"n":"06","v":"06"},{"n":"05","v":"05"},{"n":"04","v":"04"},{"n":"03","v":"03"},{"n":"02","v":"02"},{"n":"01","v":"01"}]}]}
}
header = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
"Origin": "https://tv.cctv.com",
"Referer": "https://tv.cctv.com/"
}
def localProxy(self,param):
return [200, "video/MP2T", action, ""]
+213
View File
@@ -0,0 +1,213 @@
# coding=utf-8
# !/usr/bin/python
# 嗷呜
import sys
from base64 import b64encode, b64decode
from Crypto.Hash import MD5, SHA256
sys.path.append("..")
from base.spider import Spider
from Crypto.Cipher import AES
import json
import time
class Spider(Spider):
def getName(self):
return "lav"
def init(self, extend=""):
self.id = self.ms(self.t)[:16]
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def action(self, action):
pass
def destroy(self):
pass
host = "http://sir_new.tiansexyl.tv"
t = str(int(time.time() * 1000))
headers = {'User-Agent': 'okhttp-okgo/jeasonlzy', 'Connection': 'Keep-Alive',
'Content-Type': 'application/x-www-form-urlencoded'}
def homeContent(self, filter):
cateManual = {"演员": "actor", "分类": "avsearch", }
classes = []
for k in cateManual:
classes.append({'type_name': k, 'type_id': cateManual[k]})
j = {'code': 'homePage', 'mod': 'down', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv',
'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn',
'oauth_id': self.id}
body = self.aes(j)
data = self.post(f'{self.host}/api.php?t={self.t}', data=body, headers=self.headers).json()['data']
data1 = self.aes(data, False)['data']
self.r = data1['r']
for i, d in enumerate(data1['avTag']):
# if i == 4:
# break
classes.append({'type_name': d['name'], 'type_id': d['tag']})
resutl = {}
resutl["class"] = classes
return resutl
def homeVideoContent(self):
pass
def categoryContent(self, tid, pg, filter, extend):
id = tid.split("@@")
result = {}
result["page"] = pg
result["pagecount"] = 9999
result["limit"] = 90
result["total"] = 999999
if id[0] == 'avsearch':
if pg == '1':
j = {'code': 'avsearch', 'mod': 'search', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv',
'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn',
'oauth_id': self.id}
if len(id) > 1:
j = {'code': 'find', 'mod': 'tag', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv',
'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn',
'oauth_id': self.id, 'type': 'av', 'dis': 'new', 'page': str(pg), 'tag': id[1]}
elif id[0] == 'actor':
j = {'mod': 'actor', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', 'app_type': 'rn',
'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', 'oauth_id': self.id,
'page': str(pg), 'filter': ''}
if len(id) > 1:
j = {'code': 'eq', 'mod': 'actor', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv',
'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn',
'oauth_id': self.id, 'page': str(pg), 'id': id[1], 'actor': id[2]}
else:
j = {'code': 'search', 'mod': 'av', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv',
'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn',
'oauth_id': self.id, 'page': str(pg), 'tag': id[0]}
body = self.aes(j)
data = self.post(f'{self.host}/api.php?t={self.t}', data=body, headers=self.headers).json()['data']
data1 = self.aes(data, False)['data']
videos = []
if tid == 'avsearch' and len(id) == 1:
for item in data1:
videos.append({"vod_id": id[0] + "@@" + str(item.get('tags')), 'vod_name': item.get('name'),
'vod_pic': self.imgs(item.get('ico')), 'vod_tag': 'folder',
'style': {"type": "rect", "ratio": 1.33}})
elif tid == 'actor' and len(id) == 1:
for item in data1:
videos.append({"vod_id": id[0] + "@@" + str(item.get('id')) + "@@" + item.get('name'),
'vod_name': item.get('name'), 'vod_pic': self.imgs(item.get('cover')),
'vod_tag': 'folder', 'style': {"type": "oval"}})
else:
for item in data1:
if item.get('_id'):
videos.append({"vod_id": str(item.get('id')), 'vod_name': item.get('title'),
'vod_pic': self.imgs(item.get('cover_thumb') or item.get('cover_full')),
'vod_remarks': item.get('good'), 'style': {"type": "rect", "ratio": 1.33}})
result["list"] = videos
return result
def detailContent(self, ids):
id = ids[0]
j = {'code': 'detail', 'mod': 'av', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv',
'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn',
'oauth_id': self.id, 'id': id}
body = self.aes(j)
data = self.post(f'{self.host}/api.php?t={self.t}', data=body, headers=self.headers).json()['data']
data1 = self.aes(data, False)['line']
vod = {}
play = []
for itt in data1:
a = itt['line'].get('s720')
if a:
b = a.split('.')
b[0] = 'https://m3u8'
a = '.'.join(b)
play.append(itt['info']['tips'] + "$" + a)
break
vod["vod_play_from"] = 'LAV'
vod["vod_play_url"] = "#".join(play)
result = {"list": [vod]}
return result
def searchContent(self, key, quick, pg="1"):
pass
def playerContent(self, flag, id, vipFlags):
url = self.getProxyUrl() + "&url=" + b64encode(id.encode('utf-8')).decode('utf-8') + "&type=m3u8"
self.hh = {'User-Agent': 'dd', 'Connection': 'Keep-Alive', 'Referer': self.r}
result = {}
result["parse"] = 0
result["url"] = url
result["header"] = self.hh
return result
def localProxy(self, param):
url = param["url"]
if param.get('type') == "m3u8":
return self.vod(b64decode(url).decode('utf-8'))
else:
return self.img(url)
def vod(self, url):
data = self.fetch(url, headers=self.hh).text
key = bytes.fromhex("13d47399bda541b85e55830528d4e66f1791585b2d2216f23215c4c63ebace31")
iv = bytes.fromhex(data[:32])
data = data[32:]
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
data_bytes = bytes.fromhex(data)
decrypted = cipher.decrypt(data_bytes)
encoded = decrypted.decode("utf-8").replace("\x08", "")
return [200, "application/vnd.apple.mpegur", encoded]
def imgs(self, url):
return self.getProxyUrl() + '&url=' + url
def img(self, url):
type = url.split('.')[-1]
data = self.fetch(url).text
key = bytes.fromhex("ba78f184208d775e1553550f2037f4af22cdcf1d263a65b4d5c74536f084a4b2")
iv = bytes.fromhex(data[:32])
data = data[32:]
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
data_bytes = bytes.fromhex(data)
decrypted = cipher.decrypt(data_bytes)
return [200, f"image/{type}", decrypted]
def ms(self, data, m=False):
h = MD5.new()
if m:
h = SHA256.new()
h.update(data.encode('utf-8'))
return h.hexdigest()
def aes(self, data, operation=True):
key = bytes.fromhex("620f15cfdb5c79c34b3940537b21eda072e22f5d7151456dec3932d7a2b22c53")
t = str(int(time.time()))
ivt = self.ms(t)
if operation:
data = json.dumps(data, separators=(',', ':'))
iv = bytes.fromhex(ivt)
else:
iv = bytes.fromhex(data[:32])
data = data[32:]
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
if operation:
data_bytes = data.encode('utf-8')
encrypted = cipher.encrypt(data_bytes)
ep = f'{ivt}{encrypted.hex()}'
edata = f"data={ep}&timestamp={t}0d27dfacef1338483561a46b246bf36d"
sign = self.ms(self.ms(edata, True))
edata = f"timestamp={t}&data={ep}&sign={sign}"
return edata
else:
data_bytes = bytes.fromhex(data)
decrypted = cipher.decrypt(data_bytes)
return json.loads(decrypted.decode('utf-8'))
+349
View File
@@ -0,0 +1,349 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import urlparse
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import base64
import zlib
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://yhecfhhm.top:2549" # http://104.255.229.161:6688/?r=aHR0cDovL2hoZTQ5LmNvbS8=
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
class Spider(Spider):
def getName(self):
return "丢丢喵"
def init(self, extend):
pass
def searchContentPage(self, key, quick, pg):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeVideoContent(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def decrypt_data(self, Toubu, Zhongbu):
key_bytes = self.get_key_bytes(Toubu)
iv_bytes = key_bytes
encrypted_bytes = self.decode_encrypted_data(Zhongbu)
cipher = self.create_aes_cipher(key_bytes, iv_bytes)
decrypted_padded = self.decrypt_data_bytes(cipher, encrypted_bytes)
decrypted_bytes = self.unpad_data(decrypted_padded)
decompressed_bytes = self.try_decompress(decrypted_bytes)
result = self.decode_to_string(decompressed_bytes)
return result
def get_key_bytes(self, Toubu):
return Toubu[:16].encode('utf-8')
def decode_encrypted_data(self, Zhongbu):
return base64.b64decode(Zhongbu.replace('\n', '').strip())
def create_aes_cipher(self, key_bytes, iv_bytes):
return AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
def decrypt_data_bytes(self, cipher, encrypted_bytes):
return cipher.decrypt(encrypted_bytes)
def unpad_data(self, decrypted_padded):
return unpad(decrypted_padded, AES.block_size)
def try_decompress(self, decrypted_bytes):
try:
return zlib.decompress(decrypted_bytes, zlib.MAX_WBITS | 32)
except:
try:
return zlib.decompress(decrypted_bytes, -zlib.MAX_WBITS)
except:
return None
def decode_to_string(self, decompressed_bytes):
return decompressed_bytes.decode('utf-8', errors='ignore')
def homeContent(self, filter):
result = {"class": []}
res = self.get_main_page()
Toubu = self.extract_toubu(res)
Zhongbu = self.extract_zhongbu(res)
decrypted = self.decrypt_data(Toubu, Zhongbu)
res1 = self.extract_movie_channel(decrypted)
soups = self.parse_html(res1)
self.process_soups(soups, result)
return result
def get_main_page(self):
detail = requests.get(url=xurl + "/main.html", headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_toubu(self, res):
return self.extract_middle_text(res, '头部加载中</p><div data-content="">', '<', 0)
def extract_zhongbu(self, res):
return self.extract_middle_text(res, '中部加载中</p><div data-content="">', '<', 0)
def extract_movie_channel(self, decrypted):
return self.extract_middle_text(decrypted, '电影频道</a></li>', '</ul>', 0)
def parse_html(self, res1):
return BeautifulSoup(res1, "lxml")
def process_soups(self, soups, result):
for soup in soups:
self.process_single_soup(soup, result)
def process_single_soup(self, soup, result):
vods = self.find_links(soup)
self.process_vods(vods, result)
def find_links(self, soup):
return soup.find_all('a')
def process_vods(self, vods, result):
for vod in vods:
self.extract_and_append(vod, result)
def extract_and_append(self, vod, result):
name = vod.text.strip()
id = vod['href']
result["class"].append({"type_id": id, "type_name": name})
def categoryContent(self, cid, pg, filter, ext):
videos = []
page = self.get_page_number(pg)
url = self.build_category_url(cid, page)
res = self.get_category_page(url)
Toubu = self.extract_toubu(res)
Zhongbu = self.extract_zhongbu(res)
decrypted = self.decrypt_data(Toubu, Zhongbu)
doc = self.parse_html(decrypted)
soups = self.find_vodlist_divs(doc)
self.process_vodlist_divs(soups, videos)
result = self.build_category_result(videos, pg)
return result
def get_page_number(self, pg):
return int(pg) if pg else 1
def build_category_url(self, cid, page):
return f'{xurl}{cid}&page={str(page)}'
def get_category_page(self, url):
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_toubu(self, res):
return self.extract_middle_text(res, '头部加载中</p><div data-content="">', '<', 0)
def extract_zhongbu(self, res):
return self.extract_middle_text(res, '中部加载中</p><div data-content="">', '<', 0)
def parse_html(self, decrypted):
return BeautifulSoup(decrypted, "lxml")
def find_vodlist_divs(self, doc):
return doc.find_all('div', class_="vodlist dylist")
def process_vodlist_divs(self, soups, videos):
for soup in soups:
self.process_single_vodlist(soup, videos)
def process_single_vodlist(self, soup, videos):
vods = self.find_links(soup)
for vod in vods:
video = self.extract_video_info(vod)
if video:
videos.append(video)
def find_links(self, soup):
return soup.find_all('a')
def extract_video_info(self, vod):
names = vod.find('div', class_="vodname")
if names is None:
return None
name = names.text.strip()
id = vod['href']
pics = vod.find('div', class_="vodpic lazyload")
pic = pics['data-original']
remarks = vod.find('span', class_="time")
year = remarks.text.strip() if remarks else ""
return {"vod_id": id,"vod_name": name,"vod_pic": pic,"vod_year": year,}
def build_category_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = self.get_first_id(ids)
videos = self.build_video_info(did)
result = self.build_detail_result(videos)
return result
def get_first_id(self, ids):
return ids[0]
def build_video_info(self, did):
return [{"vod_id": did,"vod_play_from": "保重身体","vod_play_url": did}]
def build_detail_result(self, videos):
result = {}
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
res = self.get_player_page(id)
Toubu = self.extract_toubu(res)
Zhongbu = self.extract_zhongbu(res)
decrypted = self.decrypt_data(Toubu, Zhongbu)
real_url = self.extract_real_url(decrypted)
result = self.build_player_result(real_url)
return result
def get_player_page(self, id):
detail = requests.get(url=f"{xurl}{id}", headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_toubu(self, res):
return self.extract_middle_text(res, '头部加载中</p><div data-content="">', '<', 0)
def extract_zhongbu(self, res):
return self.extract_middle_text(res, '中部加载中</p><div data-content="">', '<', 0)
def extract_real_url(self, decrypted):
pattern = r'(https?://[^\s"\'<>]+?\.m3u8|https?:\\/\\/[^\s"\'<>]+?\.m3u8)'
match = re.search(pattern, decrypted)
return match.group(1).replace('\\/', '/')
def build_player_result(self, real_url):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = real_url
result["header"] = headerx
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+187
View File
@@ -0,0 +1,187 @@
import sys
import re
import requests
from bs4 import BeautifulSoup
from base.spider import Spider
from urllib.parse import urljoin, quote,unquote
class Spider(Spider):
def getName(self):
return "WhosTV"
def init(self, extend=""):
self.host = "https://whos.tv"
self.header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Referer": self.host
}
def _decode_cover(self, encoded):
"""解密 data-cover-src(还原前端 coolDecrypt 函数)"""
if not encoded:
return ""
try:
key_hex = encoded[-2:]
key = int(key_hex, 16)
data_hex = encoded[:-2]
chars = []
for i in range(0, len(data_hex), 2):
byte_val = int(data_hex[i:i+2], 16)
chars.append(chr(byte_val ^ key))
return ''.join(chars)
except:
return ""
def homeContent(self, filter):
result = {}
result['class'] = [
{'type_name': '影片库', 'type_id': '/videos'},
{'type_name': '女优库', 'type_id': '/actresses'}
]
return result
def categoryContent(self, tid, pg, filter, extend):
result = {}
url = f"{self.host}{tid}"
if int(pg) > 1:
url += f"/page-{pg}"
rsp = self.fetch(url, headers=self.header)
soup = BeautifulSoup(rsp.text, 'html.parser')
videos = []
# 女优名录页
if tid == "/actresses":
items = soup.find_all('a', href=re.compile(r'^/actresses/.'))
for item in items:
img = item.find('img')
if not img:
continue
name = img.get('alt', '').strip()
href = item.get('href')
if not name or href == "/actresses" or "page-" in href:
continue
pic_url = img.get('src', '')
count_text = ""
icon_span = item.find('span', class_=re.compile(r'icon-\[lucide--film\]'))
if icon_span:
parent_flex = icon_span.find_parent('span', class_='flex')
if parent_flex:
count_text = parent_flex.get_text(strip=True) + "部作品"
videos.append({
"vod_id": href,
"vod_name": name,
"vod_pic": pic_url,
"vod_remarks": count_text if count_text else "作品集",
"vod_tag": "folder"
})
# 影片网格页(包括女优个人页的作品列表)
else:
items = soup.find_all('a', href=re.compile(r'^/videos/.'))
for item in items:
h3 = item.find('h3')
v_name = h3.get_text(strip=True) if h3 else item.get('alt', '')
if not v_name:
continue
div_cover = item.find('div', attrs={'data-cover-src': True})
if div_cover:
encoded = div_cover.get('data-cover-src')
real_pic = self._decode_cover(encoded)
else:
real_pic = ""
real_pic = real_pic
remarks = self.regStr(v_name, r'([A-Z0-9]+-[0-9]+)')
videos.append({
"vod_id": item.get('href'),
"vod_name": v_name,
"vod_pic": real_pic,
"vod_remarks": remarks if remarks else ""
})
result['list'] = videos
result['page'] = pg
result['pagecount'] = 999
result['limit'] = len(videos)
result['total'] = 9999
if tid.startswith("/actresses/"):
h1_tag = soup.find('h1')
if h1_tag:
result['type_name'] = h1_tag.get_text(strip=True)
return result
def detailContent(self, ids):
vodId = ids[0]
if vodId.startswith("/actresses/"):
return self.categoryContent(vodId, "1", None, None)
url = self.host + vodId
rsp = self.fetch(url, headers=self.header)
soup = BeautifulSoup(rsp.text, 'html.parser')
title_meta = soup.find('meta', property="og:title")
title = title_meta.get('content') if title_meta else ""
pic_meta = soup.find('meta', property="og:image")
pic = pic_meta.get('content') if pic_meta else ""
pic = pic
source = soup.find('source', type="application/x-mpegURL")
play_url = source.get('src') if source else ""
actor_tags = soup.select('a[href^="/actresses/"]')
actors = ",".join([a.get_text(strip=True) for a in actor_tags if a.get_text(strip=True)])
tag_tags = soup.select('a[href^="/tags/"] span.truncate')
tags = ",".join([t.get_text(strip=True) for t in tag_tags])
vod = {
"vod_id": vodId,
"vod_name": title,
"vod_pic": pic,
"type_name": tags,
"vod_actor": actors,
"vod_content": title,
"vod_play_from": "WhosTV",
"vod_play_url": "全高清$" + play_url if play_url else ""
}
return {'list': [vod]}
def playerContent(self, flag, id, vipFlags):
return {
"parse": 0,
"url": id,
"header": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Referer": "https://whos.tv/",
"Origin": "https://whos.tv"
}
}
def searchContent(self, key, quick):
url = f"{self.host}/result?serach={key}"
rsp = self.fetch(url, headers=self.header)
soup = BeautifulSoup(rsp.text, 'html.parser')
videos = []
items = soup.find_all('a', href=re.compile(r'^/videos/.'))
for item in items:
h3 = item.find('h3')
if h3:
v_name = h3.get_text(strip=True)
div_cover = item.find('div', attrs={'data-cover-src': True})
if div_cover:
encoded = div_cover.get('data-cover-src')
real_pic = self._decode_cover(encoded)
else:
real_pic = ""
real_pic = real_pic
remarks = self.regStr(v_name, r'([A-Z0-9]+-[0-9]+)')
videos.append({
"vod_id": item.get('href'),
"vod_name": v_name,
"vod_pic": real_pic,
"vod_remarks": remarks if remarks else ""
})
return {"list": videos}
+310
View File
@@ -0,0 +1,310 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import hashlib
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://www.r5nu.com"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
class Spider(Spider):
global xurl
global headerx
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def generate_signature_params(self, payload):
SECRET_KEY = "s5dVVmAyt75nCrHPAdV2y1i+koJNaxh6jNTkiKgSSurRQDJSt4AH7Z8GawIF92Tc"
t = str(int(time.time() * 1000))
signature_s = hashlib.md5((t + SECRET_KEY).encode('utf-8')).hexdigest()
final_params = payload.copy()
final_params['t'] = t
final_params['s'] = signature_s
return final_params
def homeContent(self, filter):
result = {"class": []}
menu_data = self.fetch_menu_data()
result["class"] = self.parse_menu_data(menu_data)
return result
def fetch_menu_data(self):
payload = {
'action': 'getmenu',
'vtype': '',
'index': '-1',
'state': '0',
'ttt': '',
'p': ''
}
final_params = self.generate_signature_params(payload)
urlz = f'{xurl}/web/abcdefg.ashx'
response = requests.post(url=urlz, headers=headerx, data=final_params)
res = response.text
return res
def parse_menu_data(self, html_content):
doc = BeautifulSoup(html_content, "lxml")
soups = doc.find_all('div', class_="head_bottom")[:2]
class_list = []
for soup in soups:
vods = soup.find_all('li')
for vod in vods:
name = self.process_category_name(vod.text.strip())
id = self.process_category_id(vod['onclick'])
class_list.append({"type_id": id, "type_name": name})
return class_list
def process_category_name(self, name):
replacements = {
'npiik685Lck0FDMUZdiEww==': '亚洲无码',
's/1sDU0O4YiSQcOWSJSU0w==': '欧美无码',
'DwciqD3gIgL7nw/s7sILpA==': '中文字幕',
'w3MG36tpsp7Q2laGGkz8/w==': '经典三级',
'QlWqEFTdBnS7fJE2QeRWPA==': '国产主播',
'QWTsC1myBO2tSCLQ/sEnew==': '韩国主播',
'gN2L7RnWjEzBfcovmlSAmQ==': 'ASMR',
'8KibIBlYoyvWx9DCZhxk2w==': '恐怖色情',
'1BnJg5tSsn5AM2C07wItyA==': '网红视频',
'CCNquvXakHIecCOudFO8yg==': '国产视频',
'sUaKzVcQ5ehvRZ3kt27KAQ==': '人妖伪娘',
'B5RKLzIG+WCEscBNxlwr+A==': '动漫卡通',
'WBm+mcc3ZOug3T/VO4KxRA==': '华人原创',
'nA3l008NjTxmTW+BuW5pOQ==': 'JVID',
'eRP3JFAXTQwWoiTAu95jeA==': 'SWAG',
'fGfFjWIpFa1ughCZLhAf9w==': '明星换脸'
}
for old, new in replacements.items():
name = name.replace(old, new)
return name
def process_category_id(self, onclick_value):
id = onclick_value.replace("toLinkpage('video-", '').replace(".html');", '').replace("duanpian-", '')
return id
def homeVideoContent(self):
pass
def categoryContent(self, cid, pg, filter, ext):
page = self.process_page_number(pg)
payload = self.build_category_payload(cid, page)
data = self.fetch_category_data(payload)
videos = self.parse_video_list(data)
result = self.build_category_result(videos, pg)
return result
def process_page_number(self, pg):
if pg:
return int(pg)
else:
return 1
def build_category_payload(self, cid, page):
payload = {
'action': 'getvideos',
'vtype': cid,
'pageindex': str(page),
'pagesize': '12',
'tags': '全部',
'sortindex': '1'
}
return payload
def fetch_category_data(self, payload):
final_params = self.generate_signature_params(payload)
timestamp = int(time.time() * 1000)
urlz = f'{xurl}/web/abcdefg.ashx?v={timestamp}'
detail = requests.post(url=urlz, headers=headerx, data=final_params)
detail.encoding = "utf-8"
return detail.json()
def parse_video_list(self, data):
videos = []
for vod in data['videos']:
name = vod['title']
id = vod['vurl']
pic = vod['coverimg']
remark = vod['updatedate']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
return videos
def build_category_result(self, videos, pg):
result = {
'list': videos,
'page': pg,
'pagecount': 9999,
'limit': 90,
'total': 999999
}
return result
def detailContent(self, ids):
did = ids[0]
videos = self.build_video_details(did)
result = self.build_detail_result(videos)
return result
def build_video_details(self, did):
videos = []
videos.append({
"vod_id": did,
"vod_play_from": "在线观看",
"vod_play_url": did
})
return videos
def build_detail_result(self, videos):
result = {
'list': videos
}
return result
def _is_url_valid(self, url: str, headers: dict, timeout: int = 1) -> bool:
try:
detail = requests.get(url=url, headers=headerx, timeout=timeout)
return detail.status_code == 200 and bool(detail.text.strip())
except requests.exceptions.RequestException:
return False
def _find_valid_url(self, id: str, url_templates: list, headers: dict) -> str:
for template in url_templates:
url = template.format(id)
if self._is_url_valid(url, headers):
return url
return url_templates[-1].format(id)
def playerContent(self, flag, id, vipFlags):
encoded_id = self.encode_video_id(id)
url_template_map = self.get_url_template_map()
final_id = self.find_valid_play_url(encoded_id, url_template_map)
result = self.build_player_result(final_id)
return result
def encode_video_id(self, id):
return quote(id, safe='/.')
def get_url_template_map(self):
return {
'yazhouwuma': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'oumeiwuma': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'zhongwenzimu': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'jingdiansanji': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'guochanzhubo': ['https://1x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://3x1.lv99t.com/changpian{}'],
'hanguozhubo': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'asmr': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'kongbu': ['https://3x1.lv99t.com/changpian{}', 'https://2x1.lv99t.com/changpian{}',
'https://1x1.lv99t.com/changpian{}'],
'zhubo': ['https://1x2.lv99t.com{}', 'https://2x2.lv99t.com{}',
'https://3x2.lv99t.com{}'],
'katong': ['https://3x2.lv99t.com{}', 'https://2x2.lv99t.com{}',
'https://1x2.lv99t.com{}'],
'wanghongshipin': ['https://3x1.lv99t.com/duanpian{}', 'https://2x1.lv99t.com/duanpian{}',
'https://1x1.lv99t.com/duanpian{}'],
'biantairenyao': ['https://3x1.lv99t.com/duanpian{}', 'https://2x1.lv99t.com/duanpian{}',
'https://1x1.lv99t.com/duanpian{}'],
'weiniang': ['https://3x1.lv99t.com/duanpian{}', 'https://2x1.lv99t.com/duanpian{}',
'https://1x1.lv99t.com/duanpian{}'],
'huaren': ['https://3x1.lv99t.com/duanpian{}', 'https://2x1.lv99t.com/duanpian{}',
'https://1x1.lv99t.com/duanpian{}'],
'jvid': ['https://3x1.lv99t.com/duanpian{}', 'https://2x1.lv99t.com/duanpian{}',
'https://1x1.lv99t.com/duanpian{}'],
'swag': ['https://2x1.lv99t.com/duanpian{}', 'https://1x1.lv99t.com/duanpian{}',
'https://3x1.lv99t.com/duanpian{}'],
'AI': ['https://3x1.lv99t.com/duanpian{}', 'https://2x1.lv99t.com/duanpian{}',
'https://1x1.lv99t.com/duanpian{}'],
}
def find_valid_play_url(self, id, url_template_map):
final_id = id
for type_name, templates in url_template_map.items():
if type_name in id:
final_id = self._find_valid_url(id, templates, headers=headerx)
break
return final_id
def build_player_result(self, final_id):
result = {
"parse": 0,
"playUrl": '',
"url": final_id,
"header": headerx
}
return result
def searchContentPage(self, key, quick, pg):
pass
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+495
View File
@@ -0,0 +1,495 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://css.gztzyyp.com" # 首页 https://kea9da.com/home/
xurl1 = "https://chees.sxgtlj.com"
xurl2 = "https://kea9da.com"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
class Spider(Spider):
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "4", "type_name": "自拍视频"},
{"type_id": "5", "type_name": "淫妻作乐"},
{"type_id": "142", "type_name": "热门探花"},
{"type_id": "64", "type_name": "国产传媒"},
{"type_id": "6", "type_name": "开放青年"},
{"type_id": "119", "type_name": "JVID专区"},
{"type_id": "139", "type_name": "SWAG专区"},
{"type_id": "60", "type_name": "直播录像"},
{"type_id": "157", "type_name": "AI换脸"},
{"type_id": "9", "type_name": "短视频"},
{"type_id": "140", "type_name": "无码破解"},
{"type_id": "39", "type_name": "动漫卡通"},
{"type_id": "58", "type_name": "女性向爱纯"},
{"type_id": "65", "type_name": "GIGA女战士"},
{"type_id": "141", "type_name": "男男视频"},
{"type_id": "40", "type_name": "无码中字"},
{"type_id": "43", "type_name": "熟女人妻"},
{"type_id": "44", "type_name": "美艳巨乳"},
{"type_id": "41", "type_name": "SM系列"},
{"type_id": "45", "type_name": "丝袜制服"},
{"type_id": "118", "type_name": "蕾丝边"},
{"type_id": "46", "type_name": "中文有码"},
{"type_id": "47", "type_name": "欧美系列"}],
}
return result
def decrypt_data(self, encrypted_base64_data):
try:
self._validate_decryption_input(encrypted_base64_data)
key, iv = self._prepare_decryption_params()
decoded_base64_str = self._custom_base64_decode(encrypted_base64_data)
ciphertext = self._decode_base64_data(decoded_base64_str)
decrypted_bytes = self._perform_aes_decryption(ciphertext, key, iv)
plain_text = self._unpad_and_decode(decrypted_bytes)
return self._parse_decryption_result(plain_text)
except Exception as e:
return self._handle_decryption_error(e)
def _validate_decryption_input(self, encrypted_base64_data):
if not encrypted_base64_data:
raise ValueError("加密数据不能为空")
def _prepare_decryption_params(self):
aes_key_str = "22946bc50fd63164b79df55070a85a92"
aes_iv_str = "kaixin1234567890"
key = aes_key_str.encode('utf-8')
iv = aes_iv_str.encode('utf-8')
return key, iv
def _custom_base64_decode(self, encoded_str):
decoded_str = encoded_str.replace('-', '+').replace('_', '/')
while len(decoded_str) % 4 != 0:
decoded_str += '='
return decoded_str
def _decode_base64_data(self, decoded_base64_str):
return base64.b64decode(decoded_base64_str)
def _perform_aes_decryption(self, ciphertext, key, iv):
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_bytes = cipher.decrypt(ciphertext)
return decrypted_bytes
def _unpad_and_decode(self, decrypted_bytes):
plain_text_bytes = unpad(decrypted_bytes, AES.block_size)
plain_text = plain_text_bytes.decode('utf-8')
return plain_text
def _parse_decryption_result(self, plain_text):
try:
json_result = json.loads(plain_text)
return json_result
except json.JSONDecodeError as e:
return plain_text
def _handle_decryption_error(self, exception):
raise Exception(f"解密失败: {str(exception)}")
def homeVideoContent(self):
try:
data = self._fetch_latest_data()
decrypted_data = self._decrypt_video_data(data['data'])
pic_urls = self._fetch_picture_contents(decrypted_data['latest'])
videos = self._build_video_list(decrypted_data['latest'], pic_urls)
return {'list': videos}
except Exception as e:
return self._handle_home_content_error(e)
def _fetch_latest_data(self):
url = f'{xurl}/public2/json/latest.json'
detail = requests.get(url=url, headers=headerx, timeout=10)
detail.encoding = "utf-8"
return detail.json()
def _decrypt_video_data(self, encrypted_data):
return self.decrypt_data(encrypted_data)
def _fetch_picture_contents(self, latest_videos):
pic_urls = {}
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
future_to_vod = {
executor.submit(requests.get, url=vod['titlepic'], headers=headerx): vod
for vod in latest_videos
}
for future in concurrent.futures.as_completed(future_to_vod):
vod = future_to_vod[future]
try:
response = future.result()
response.encoding = "utf-8"
pic_urls[vod['id']] = response.text
except Exception as e:
pic_urls[vod['id']] = ""
return pic_urls
def _build_video_list(self, latest_videos, pic_urls):
videos = []
for vod in latest_videos:
name = vod['title']
id = vod['id']
pic = pic_urls.get(id, "")
date_obj = datetime.datetime.fromtimestamp(int(vod['newstime']))
remark = date_obj.strftime('%Y-%m-%d')
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
return videos
def _handle_home_content_error(self, exception):
return {'list': []}
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
page = self._parse_page(pg)
data = self._fetch_category_data(cid, page)
if not data or 'data' not in data:
return {'list': []}
pic_urls = self._fetch_picture_urls(data['data'])
videos = self._process_videos(data['data'], pic_urls)
result['list'] = videos
result['page'] = page
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def _parse_page(self, pg):
try:
return int(pg) if pg else 1
except (ValueError, TypeError):
return 1
def _fetch_category_data(self, cid, page):
try:
url = f'{xurl}/public2/json/category/{cid}-{str(page)}.json'
detail = requests.get(url=url, headers=headerx, timeout=10)
detail.encoding = "utf-8"
data = detail.json()
return self.decrypt_data(data['data'])
except Exception:
return None
def _fetch_picture_urls(self, vod_data):
pic_urls = {}
try:
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
future_to_vod = {
executor.submit(self._fetch_picture, vod['titlepic']): vod
for vod in vod_data
}
for future in concurrent.futures.as_completed(future_to_vod):
vod = future_to_vod[future]
try:
pic_content = future.result()
pic_urls[vod['id']] = pic_content
except Exception:
pic_urls[vod['id']] = ""
except Exception:
pass
return pic_urls
def _fetch_picture(self, url):
try:
response = requests.get(url=url, headers=headerx, timeout=10)
response.encoding = "utf-8"
return response.text
except Exception:
return ""
def _process_videos(self, vod_data, pic_urls):
videos = []
for vod in vod_data:
try:
name = vod.get('title', '')
id = vod.get('id', '')
pic = pic_urls.get(id, "")
remark = self._format_date(vod.get('newstime', 0))
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
except Exception:
continue
return videos
def _format_date(self, timestamp):
try:
date_obj = datetime.datetime.fromtimestamp(int(timestamp))
return date_obj.strftime('%Y-%m-%d')
except Exception:
return ""
def detailContent(self, ids):
result = {}
videos = []
if not ids:
result['list'] = videos
return result
did = ids[0]
data = self._fetch_video_detail(did)
if not data:
result['list'] = videos
return result
video_info = self._extract_video_info(data, did)
videos.append(video_info)
result['list'] = videos
return result
def _fetch_video_detail(self, did):
try:
url = f'{xurl}/public2/json/video/{did}.json'
detail = requests.get(url=url, headers=headerx, timeout=10)
detail.encoding = "utf-8"
data = detail.json()
return self.decrypt_data(data['data'])
except Exception:
return None
def _extract_video_info(self, data, did):
content = self._get_video_content(data)
remarks = data.get('category_name', '')
year = self._format_year(data.get('newstime', 0))
area = self._get_area(data)
xianlu = "乐哥1$$$乐哥2"
bofang = self._format_play_url(data)
return {
"vod_id": did,
"vod_remarks": remarks,
"vod_year": year,
"vod_area": area,
"vod_content": content,
"vod_play_from": xianlu,
"vod_play_url": bofang
}
def _get_video_content(self, data):
try:
next_title = data['prev_and_next']['next']['title']
return '乐哥为您介绍剧情' + next_title
except Exception:
return '乐哥为您介绍剧情'
def _format_year(self, timestamp):
try:
date_obj = datetime.datetime.fromtimestamp(int(timestamp))
return date_obj.strftime('%Y-%m-%d')
except Exception:
return ""
def _get_area(self, data):
try:
return data['breadcrumb'][0]['title']
except Exception:
return ""
def _format_play_url(self, data):
try:
mp4_url = data.get('mp4', '')
m3u8_url = data.get('m3u8', '')
return f"乐哥mp4${mp4_url}$$$乐哥m3u8${xurl1}{m3u8_url}"
except Exception:
return ""
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headerx
return result
def encrypt_aes_cbc_pkcs7_urlsafe(self, plaintext_str):
try:
key_str = "22946bc50fd63164b79df55070a85a92"
iv_str = "kaixin1234567890"
plaintext_bytes = self._encode_plaintext(plaintext_str)
key = self._encode_key(key_str)
iv = self._encode_iv(iv_str)
cipher = self._create_cipher(key, iv)
padded_bytes = self._pad_plaintext(plaintext_bytes)
ciphertext = self._encrypt_data(cipher, padded_bytes)
urlsafe_b64 = self._encode_urlsafe_base64(ciphertext)
return urlsafe_b64
except Exception:
return ""
def _encode_plaintext(self, plaintext_str):
try:
return plaintext_str.encode('utf-8')
except Exception:
return b""
def _encode_key(self, key_str):
try:
return key_str.encode('utf-8')
except Exception:
return b""
def _encode_iv(self, iv_str):
try:
return iv_str.encode('utf-8')
except Exception:
return b""
def _create_cipher(self, key, iv):
try:
return AES.new(key, AES.MODE_CBC, iv)
except Exception:
raise
def _pad_plaintext(self, plaintext_bytes):
try:
return pad(plaintext_bytes, AES.block_size)
except Exception:
raise
def _encrypt_data(self, cipher, padded_bytes):
try:
return cipher.encrypt(padded_bytes)
except Exception:
raise
def _encode_urlsafe_base64(self, ciphertext):
try:
standard_b64 = base64.b64encode(ciphertext).decode('utf-8')
urlsafe_b64 = standard_b64.replace('+', '-').replace('/', '_').rstrip('=')
return urlsafe_b64
except Exception:
return ""
def searchContentPage(self, key, quick, pg):
result = {}
videos = []
page = self._parse_page(pg)
encrypted_key = self.encrypt_aes_cbc_pkcs7_urlsafe(key)
data = self._fetch_search_data(encrypted_key, page)
if data and 'data' in data:
videos = self._process_search_results(data['data'])
result['list'] = videos
result['page'] = page
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def _parse_page(self, pg):
try:
return int(pg) if pg else 1
except (ValueError, TypeError):
return 1
def _fetch_search_data(self, encrypted_key, page):
try:
url = f'{xurl2}/api/v2/search?keyword={encrypted_key}&classid=0&page={str(page)}'
detail = requests.get(url=url, headers=headerx, timeout=10)
detail.encoding = "utf-8"
data = detail.json()
return self.decrypt_data(data['data'])
except Exception:
return None
def _process_search_results(self, search_data):
videos = []
for vod in search_data:
try:
video = self._create_video_item(vod)
videos.append(video)
except Exception:
continue
return videos
def _create_video_item(self, vod):
name = vod.get('title', '')
id = vod.get('id', '')
pic = "https://i02piccdn.sogoucdn.com/0aa7931c95f0b15a"
remark = self._format_date(vod.get('newstime', 0))
return {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
def _format_date(self, timestamp):
try:
date_obj = datetime.datetime.fromtimestamp(int(timestamp))
return date_obj.strftime('%Y-%m-%d')
except Exception:
return ""
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+334
View File
@@ -0,0 +1,334 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import urlparse
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
headerx = {
'User-Agent': 'com.android.chrome/131.0.6778.200 (Linux;Android 9) AndroidXMedia3/1.8.0'
}
xurl1 = "http://3344br.com/"
class Spider(Spider):
def getName(self):
return "丢丢喵"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeVideoContent(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def decrypt_m3u8(self, ciphertext):
real_key = self.get_real_key()
final_iv = self.build_final_iv()
encrypted_bytes = base64.b64decode(ciphertext)
decrypted_bytes = self.aes_decrypt(real_key, final_iv, encrypted_bytes)
result_text = self.finalize_result(decrypted_bytes)
return result_text
def get_real_key(self):
B64_KEY = "SWRUSnEwSGtscHVJNm11OGlCJU9PQCF2ZF40SyZ1WFc="
return base64.b64decode(B64_KEY)
def build_final_iv(self):
B64_IV_BASE = "JDB2QGtySDdWMg=="
SUFFIX = "883346"
iv_prefix = base64.b64decode(B64_IV_BASE).decode('utf-8')
return (iv_prefix + SUFFIX).encode('utf-8')
def aes_decrypt(self, real_key, final_iv, encrypted_bytes):
cipher = AES.new(real_key, AES.MODE_CBC, final_iv)
decrypted_bytes = cipher.decrypt(encrypted_bytes)
return unpad(decrypted_bytes, AES.block_size).decode('utf-8')
def finalize_result(self, decrypted_bytes):
return decrypted_bytes.replace('"', '')
def get_real_base_url(self, start_url):
res = self.fetch_start_page(start_url)
target_host_b64 = self.extract_target_host_b64(res)
if not target_host_b64:
return start_url
target_host = base64.b64decode(target_host_b64).decode('utf-8')
u_val, p_val = self.build_params(start_url)
check_url = f"{target_host}/?u={u_val}&p={p_val}"
redirect_url_1 = self.get_redirect(check_url)
if not redirect_url_1:
return check_url
redirect_url_2 = self.get_redirect(redirect_url_1)
real_url = redirect_url_2 if redirect_url_2 else redirect_url_1
return real_url.rstrip('/')
def fetch_start_page(self, start_url):
detail = requests.get(url=start_url, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_target_host_b64(self, res):
match = re.search(r'window\.atob\("(.*?)"\)', res)
return match.group(1) if match else None
def build_params(self, start_url):
parsed_url = urlparse(start_url)
origin = f"{parsed_url.scheme}://{parsed_url.netloc}"
path_and_search = parsed_url.path
if parsed_url.query:
path_and_search += f"?{parsed_url.query}"
if not path_and_search:
path_and_search = "/"
u_val = base64.b64encode(origin.encode('utf-8')).decode('utf-8')
p_val = base64.b64encode(path_and_search.encode('utf-8')).decode('utf-8')
return u_val, p_val
def get_redirect(self, url):
response = requests.get(url=url, headers=headerx, allow_redirects=False)
return response.headers.get('Location')
def homeContent(self, filter):
xurl = self.get_real_base_url(xurl1)
url = self.build_home_url(xurl)
res = self.fetch_home_page(url)
doc = self.parse_html(res)
soups = self.find_row_items(doc)
classes = self.extract_classes(soups)
result = self.build_home_result(classes)
return result
def build_home_url(self, xurl):
return f'{xurl}/index/home.html'
def fetch_home_page(self, url):
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def parse_html(self, html):
return BeautifulSoup(html, "lxml")
def find_row_items(self, doc):
return doc.find_all('ul', class_="row-item-content")[:1]
def extract_classes(self, soups):
classes = []
for soup in soups:
for vod in soup.find_all('a'):
classes.append(self.parse_class_item(vod))
return classes
def parse_class_item(self, vod):
names = vod.find('span', class_="menu-desktop-content-item")
name = names['title']
name = self.decrypt_m3u8(name)
return {"type_id": name, "type_name": name}
def build_home_result(self, classes):
result = {"class": classes}
return result
def get_page_url(self, category_name, page_num):
raw_path = f"/juqing/list-{category_name}-{page_num}.html"
b64_bytes = base64.b64encode(raw_path.encode('utf-8'))
b64_str = b64_bytes.decode('utf-8')
url_encoded = urllib.parse.quote(b64_str)
final_url = f"/cYc{url_encoded}.html"
return final_url
def categoryContent(self, cid, pg, filter, ext):
xurl = self.get_real_base_url(xurl1)
page = self.get_page_number(pg)
url1 = self.get_page_url(cid, page)
url = f'{xurl}{url1}'
res = self.fetch_category_page(url)
doc = self.parse_html(res)
soups = self.find_video_lists(doc)
videos = self.extract_videos(soups, xurl)
return self.build_category_result(videos, pg)
def get_page_number(self, pg):
return int(pg) if pg else 1
def fetch_category_page(self, url):
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def parse_html(self, html):
return BeautifulSoup(html, "lxml")
def find_video_lists(self, doc):
return doc.find_all('div', class_="video-list")
def extract_videos(self, soups, xurl):
videos = []
for soup in soups:
for vod in soup.find_all('a'):
videos.append(self.parse_video_item(vod, xurl))
return videos
def parse_video_item(self, vod, xurl):
names = vod.find('div', class_="video-item-title")
raw_title = names['title']
name = self.decrypt_m3u8(raw_title)
vid = vod['href']
pics = vod.find('img', class_="video-item-img")
pic = f"{xurl}{pics['src']}"
remarks = vod.find('div', class_="video-item-date")
remark = remarks.text.strip() if remarks else ""
return {"vod_id": vid,"vod_name": name,"vod_pic": pic,"vod_remarks": remark}
def build_category_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def _fetch_page_content(self, url):
response = requests.get(url=url, headers=headerx)
response.encoding = "utf-8"
return response.text
def _decode_config(self, html, key):
start_str = f"{key} = decodeString('"
encoded_str = self.extract_middle_text(html, start_str, "'", 0)
return base64.b64decode(encoded_str).decode('utf-8')
def _build_play_url(self, html):
video_path = self._decode_config(html, "var video ")
host1 = self._decode_config(html, "m3u8_host ")
host2 = self._decode_config(html, "m3u8_host1")
return f"线路1${host1}{video_path}#线路2${host2}{video_path}"
def detailContent(self, ids):
did = ids[0]
base_url = self.get_real_base_url(xurl1)
full_url = f'{base_url}{did}'
page_content = self._fetch_page_content(full_url)
play_url = self._build_play_url(page_content)
return {'list': [{"vod_id": did,"vod_name": "温馨提醒📢注意身体","vod_play_from": "四色专线","vod_play_url": play_url}]}
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headerx
return result
def searchContentPage(self, key, quick, pg):
pass
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+285
View File
@@ -0,0 +1,285 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import urlparse
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://pornlax.com"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
class Spider(Spider):
def getName(self):
return "丢丢喵"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeVideoContent(self):
pass
def searchContentPage(self, key, quick, pg):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def homeContent(self, filter):
result = {"class": []}
res = self.get_response()
doc = self.parse_response(res)
soups = self.find_sugg_divs(doc)
self.process_soups(soups, result)
return result
def get_response(self):
detail = requests.get(url=xurl, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def parse_response(self, res):
return BeautifulSoup(res, "lxml")
def find_sugg_divs(self, doc):
return doc.find_all('div', class_="sugg")
def process_soups(self, soups, result):
for soup in soups:
self.process_single_soup(soup, result)
def process_single_soup(self, soup, result):
vods = self.find_links(soup)
self.process_vods(vods, result)
def find_links(self, soup):
return soup.find_all('a')
def process_vods(self, vods, result):
for vod in vods:
self.extract_and_append(vod, result)
def extract_and_append(self, vod, result):
name = vod.text.strip()
id = vod['href']
result["class"].append({"type_id": id, "type_name": name})
def categoryContent(self, cid, pg, filter, ext):
page = self.get_page_number(pg)
data = self.build_request_data(cid, page)
res = self.send_post_request(data)
doc = self.parse_html(res)
soups = self.find_video_divs(doc)
videos = self.process_video_divs(soups)
return self.build_result(videos, pg)
def get_page_number(self, pg):
return int(pg) if pg else 1
def build_request_data(self, cid, page):
fenge = cid.split("videos/")
return {'mix': 'video-next3','value': fenge[1],'page': str(page),}
def send_post_request(self, data):
detail = requests.post('https://pornlax.com/hash-pornlax', headers=headerx, data=data)
detail.encoding = "utf-8"
return detail.text
def parse_html(self, res):
return BeautifulSoup(res, "lxml")
def find_video_divs(self, doc):
return doc.find_all('div', class_="prev")
def process_video_divs(self, soups):
videos = []
for vod in soups:
video = self.extract_video_info(vod)
videos.append(video)
return videos
def extract_video_info(self, vod):
names = vod.find('div', class_="name")
name = names.text.strip()
id = vod.find('a')['href']
pic = vod.find('img')['src']
remarks = vod.find('div', class_="info")
remark = remarks.text.strip() if remarks else ""
return {"vod_id": id,"vod_name": name,"vod_pic": pic,"vod_remarks": remark}
def build_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
data = self.build_detail_data(did)
res = self.get_iframe_response(data)
cond = self.extract_middle_text(res, 'src="', '"', 0)
res = self.get_player_response(cond)
matches = self.extract_play_info(res)
bofang = self.build_play_string(matches)
videos = self.build_video_list(did, bofang)
result = self.build_detail_result(videos)
return result
def build_detail_data(self, did):
fenge = did.split("/")
return {'mix': 'moviesiframe2','num': fenge[2],}
def get_iframe_response(self, data):
detail = requests.post('https://pornlax.com/hash-pornlax', headers=headerx, data=data)
detail.encoding = "utf-8"
return detail.text
def get_player_response(self, cond):
detail = requests.get(url=cond, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_play_info(self, res):
pattern = re.compile(r"html:\s*'(.*?)'.*?url:\s*'(.*?)'", re.DOTALL)
return pattern.findall(res)
def build_play_string(self, matches):
bofang = ''
for name, id in matches:
bofang = bofang + name + '$' + id + '#'
return bofang[:-1]
def build_video_list(self, did, bofang):
return [{"vod_id": did,"vod_play_from": "小心腰专线","vod_play_url": bofang}]
def build_detail_result(self, videos):
result = {}
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headerx
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+220
View File
@@ -0,0 +1,220 @@
import requests
from bs4 import BeautifulSoup
import re
from base.spider import Spider
import sys
import json
import base64
import urllib.parse
sys.path.append('..')
murl = "https://www.6vyd.com/web/index.html"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0'
}
response = requests.get(murl, allow_redirects=True)
xurl = response.url
nurl = xurl + '/web/abcdefg.ashx?action=getindexdata'
durl = xurl + '/web/abcdefg.ashx?action=getvideo&vid='
pm = ''
class Spider(Spider):
global xurl
global headerx
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "1003", "type_name": "亚洲无码"},
{"type_id": "3022", "type_name": "欧美无码"},
{"type_id": "3026", "type_name": "中文字幕"},
{"type_id": "3025", "type_name": "经典三级"},
{"type_id": "5", "type_name": "国产主播"},
{"type_id": "134", "type_name": "韩国主播"},
{"type_id": "3137", "type_name": "ASMR"},
{"type_id": "3138", "type_name": "恐怖色情"},
{"type_id": "131", "type_name": "网红视频"},
{"type_id": "132", "type_name": "国产视频"},
{"type_id": "3023", "type_name": "人妖伪娘"},
{"type_id": "130", "type_name": "动漫卡通"},
{"type_id": "3088", "type_name": "华人原创"},
{"type_id": "3135", "type_name": "JVID"},
{"type_id": "3136", "type_name": "SWAG"},
{"type_id": "3134", "type_name": "明星换脸"}]}
return result
def homeVideoContent(self):
videos = []
try:
detail = requests.get(url=nurl, headers=headerx)
detail.encoding = "utf-8"
res = detail.json()
video_list = res.get("videos", [])
for video in video_list:
name = video.get("title", "")
id = video.get("id", "")
pic = video.get("coverimg", "")
remarks = video.get("updatedate", "")
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
result = {'list': videos}
return result
except:
pass
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
if pg:
page = int(pg)
else:
page = 1
if page == '1':
url = f'{xurl}/web/abcdefg.ashx?action=getvideos&vtype={cid}&pageindex=1&pagesize=12'
else:
url = f'{xurl}/web/abcdefg.ashx?action=getvideos&vtype={cid}&pageindex={str(page)}&pagesize=12'
try:
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
res = detail.json()
video_list = res.get("videos", [])
for video in video_list:
name = video.get("title", "")
id = video.get("id", "")
pic = video.get("coverimg", "")
remarks = video.get("updatedate", "")
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
except:
pass
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 99
result['limit'] = 90
result['total'] = 99
return result
def detailContent(self, ids):
did = ids[0]
result = {}
videos = []
playurl = ''
if 'http' not in did:
did = durl + did
res1 = requests.get(url=did, headers=headerx)
res1.encoding = "utf-8"
res = res1.json()
node = res.get("data", {}).get("Table", [{}])[0]
vod_id = node.get("id", "")
vod_name = node.get("title", "")
vod_pic = node.get("coverimg", "")
vod_remarks = node.get("updatedate", "")
vod_content = node.get("title", "")
playFrom = []
playList = []
if node.get("vurl"):
base_url1 = res.get("xldata", {}).get("value", "")
base_url2 = res.get("xldata", {}).get("value1", "")
if base_url1:
full_vurl1 = base_url1 + node.get("vurl", "")
playFrom.append("播放源1")
playList.append(full_vurl1)
if base_url2:
full_vurl2 = base_url2 + node.get("vurl", "")
playFrom.append("播放源2")
playList.append(full_vurl2)
videos.append({
"vod_id": vod_id,
"vod_name": vod_name,
"vod_pic": vod_pic,
"vod_remarks": vod_remarks,
"vod_content": vod_content,
"vod_play_from": "$$$".join(playFrom),
"vod_play_url": "$$$".join(playList)
})
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
parts = id.split("http")
xiutan = 1
if xiutan == 1:
if len(parts) > 1:
before_https, after_https = parts[0], 'http' + parts[1]
result = {}
result["parse"] = xiutan
result["playUrl"] = ''
result["url"] = after_https
result["header"] = headerx
return result
def searchContentPage(self, key, quick, page):
result = {}
videos = []
url = f'{xurl}/web/abcdefg.ashx?action=search&p={key}&pageindex={str(page)}&pagesize=12'
detail = requests.post(url=url, headers=headerx)
detail.encoding = "utf-8"
res = detail.json()
video_list = res.get("data", [])
for video in video_list:
name = video.get("title", "")
id = video.get("id", "")
pic = video.get("imgurl", "")
remarks = video.get("updatedate", "")
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
result['list'] = videos
result['page'] = page
result['pagecount'] = 60
result['limit'] = 30
result['total'] = 999999
return result
def searchContent(self, key, quick):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+199
View File
@@ -0,0 +1,199 @@
#Kyele
import sys
import json
import time
import re
import requests
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def __init__(self):
super().__init__()
self.base = 'https://www.pandalive.co.kr'; self.api = 'https://api.pandalive.co.kr'; self.session = requests.Session()
self.ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
self.common_headers = {'User-Agent': self.ua, 'Accept': 'application/json, text/plain, */*', 'Origin': self.base, 'Referer': self.base + '/'}
self.x_device_info = {"t": "webPc", "v": "1.0", "ui": "0", "ck": {"sessKeyAsp": ""}}; self.extra_cookie = ''
def init(self, extend=""):
try:
if extend:
cfg = extend if isinstance(extend, dict) else json.loads(extend)
self.x_device_info = cfg.get('x_device_info', self.x_device_info); self.extra_cookie = cfg.get('cookie', '')
except Exception: pass
try:
self.session.headers.update(self.common_headers)
if self.extra_cookie: self.session.headers['Cookie'] = self.extra_cookie
self.session.get(self.base, timeout=8); self._app_token()
except Exception: pass
return self
def getName(self): return 'PandaLive'
def isVideoFormat(self, url): return url.endswith('.m3u8') or url.endswith('.mp4')
def manualVideoCheck(self): return False
def destroy(self):
try: self.session.close()
except Exception: pass
def _app_token(self):
headers = self._with_x_device_info(dict(self.session.headers))
return self.session.get(f'{self.api}/v1/member/app_token', headers=headers, timeout=8)
def _list_live(self, page=None, page_size=None, order_by='user', only_new='N'):
if page_size is None: page_size = 60
limit = page_size; offset = 0 if page is None else max(0, (page - 1) * limit)
headers = self._with_x_device_info(dict(self.session.headers))
headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
data = {'orderBy': order_by, 'onlyNewBj': only_new, 'limit': str(limit), 'offset': str(offset)}
try:
r = self.session.post(f'{self.api}/v1/live', data=data, headers=headers, timeout=8)
j = {}
try: j = r.json()
except Exception: pass
if isinstance(j, dict) and isinstance(j.get('list'), list) and len(j['list']) > 0: return j
except Exception: pass
try: return self.session.get(f'{self.api}/v1/live', timeout=8).json()
except Exception: return {}
def _list_live_page(self, page, page_size, order_by='user', only_new='N'):
j = self._list_live(page=page, page_size=page_size, order_by=order_by, only_new=only_new)
return j.get('list', []) if isinstance(j, dict) else []
def _list_live_aggregate(self, max_pages=5, page_size=60, min_expect=60, order_by='user', only_new='N'):
try:
cache_key = f'pandalive_agg_v2_{order_by}_{only_new}'; cached = self.getCache(cache_key)
if isinstance(cached, dict) and isinstance(cached.get('list'), list): return cached['list']
except Exception: pass
seen = set(); result = []
first = self._list_live(page=None, page_size=page_size, order_by=order_by, only_new=only_new)
items = first.get('list', []) if isinstance(first, dict) else []
for it in items:
code = it.get('code') or it.get('userId')
if code and code not in seen: seen.add(code); result.append(it)
p = 1
while len(result) < min_expect and p <= max_pages:
page_items = self._list_live_page(page=p, page_size=page_size, order_by=order_by, only_new=only_new)
added = 0
for it in page_items:
code = it.get('code') or it.get('userId')
if code and code not in seen: seen.add(code); result.append(it); added += 1
if added == 0 and p > 1: break
p += 1
try:
payload = {"expiresAt": int(time.time()) + 20, "list": result}
self.setCache(f'pandalive_agg_v2_{order_by}_{only_new}', payload)
except Exception: pass
return result
def _live_play(self, play_id):
body = {'play_id': play_id, 'device': 'webPc', 'player': 'ivs'}
headers = self._with_x_device_info(dict(self.session.headers))
headers['Content-Type'] = 'application/json'; headers['Referer'] = f'{self.base}/play/{play_id.split("_")[0]}'
r = self.session.post(f'{self.api}/v1/live/play', headers=headers, data=json.dumps(body), timeout=8)
if r.status_code != 200:
try: self._app_token()
except Exception: pass
r = self.session.post(f'{self.api}/v1/live/play', headers=headers, data=json.dumps(body), timeout=8)
try: return r.json()
except Exception: return {'result': False, 'status': r.status_code, 'text': r.text}
def _with_x_device_info(self, headers):
try: headers['x-device-info'] = json.dumps(self.x_device_info, separators=(',', ':'))
except Exception: headers['x-device-info'] = '{"t":"webPc","v":"1.0","ui":"0","ck":{"sessKeyAsp":""}}'
return headers
def homeContent(self, filter):
classes = [{"type_name": "LIVE", "type_id": "live"}]
if filter:
filters = {"live": [{"key": "sort", "value": [
{"n": "观看次数", "v": "user-N"}, {"n": "热门", "v": "hot-N"},
{"n": "最新", "v": "new-N"}, {"n": "新人", "v": "user-Y"}
]}]}
else: filters = {}
return {"class": classes, "filters": filters}
def homeVideoContent(self):
items = self._list_live_aggregate(max_pages=3, page_size=60, min_expect=48, order_by='user', only_new='N')
if not items:
items = (self._list_live().get('list', []))
return {"list": [self._to_vod(it) for it in items[:48]]}
def categoryContent(self, tid, pg, filter, extend):
page_size = 24; p = 1
try: p = int(pg)
except Exception: pass
order_by, only_new = 'user', 'N'
try:
if isinstance(extend, str):
try: extend = json.loads(extend) if extend.strip().startswith('{') else {}
except Exception: extend = {}
if isinstance(extend, dict):
s = extend.get('sort')
if isinstance(s, str) and '-' in s:
ab = s.split('-', 1)
if len(ab) == 2: order_by, only_new = (ab[0] or 'user'), (ab[1] or 'N')
if (order_by, only_new) == ('user', 'N') and (tid or '').lower() != 'live':
order_by, only_new = self._tid_to_sort(tid)
except Exception: pass
server_items = self._list_live_page(page=p, page_size=page_size, order_by=order_by, only_new=only_new)
if len(server_items) >= page_size:
return {"page": p, "pagecount": 99999, "limit": page_size, "total": 999999, "list": [self._to_vod(it) for it in server_items]}
all_items = self._list_live_aggregate(max_pages=12, page_size=100, min_expect=120, order_by=order_by, only_new=only_new)
total = len(all_items)
if total <= 0:
base_list = self._list_live().get('list', [])
total = len(base_list)
if total <= 0: return {"page": p, "pagecount": 1, "limit": page_size, "total": 0, "list": []}
start = (p - 1) * page_size; end = start + page_size
part = base_list[start:end]
videos = [self._to_vod(it) for it in part]
return {"page": p, "pagecount": (total + page_size - 1)//page_size, "limit": page_size, "total": total, "list": videos}
start = ((p - 1) * page_size) % total; part = []
for i in range(page_size): part.append(all_items[(start + i) % total])
return {"page": p, "pagecount": 99999, "limit": page_size, "total": 999999, "list": [self._to_vod(it) for it in part]}
def _tid_to_sort(self, tid):
t = (tid or '').lower()
if t == 'live_newbj': return 'user', 'Y'
if t == 'live_hot': return 'hot', 'N'
if t == 'live_new': return 'new', 'N'
return 'user', 'N'
def detailContent(self, ids):
vid = ids[0]; parts = vid.split('|'); play_id = parts[0]
user_id = parts[1] if len(parts) > 1 else play_id.split('_')[0]
title = parts[2] if len(parts) > 2 else user_id
vod = {"vod_id": vid, "vod_name": title, "vod_pic": "", "type_name": "LIVE", "vod_year": "", "vod_area": "", "vod_remarks": "PandaLive", "vod_actor": "", "vod_director": "", "vod_content": title, "vod_play_from": "爱看影视", "vod_play_url": f"爱看影视${vid}"}
return {"list": [vod]}
def searchContent(self, key, quick, pg="1"):
items = self._list_live().get('list', [])
key_l = key.lower(); result = []
for it in items:
title = str(it.get('title', '')); user_id = str(it.get('userId', '')); user_nick = str(it.get('userNick', ''))
if key_l in title.lower() or key_l in user_id.lower() or key_l in user_nick.lower():
result.append(self._to_vod(it))
return {"list": result, "page": 1}
def playerContent(self, flag, id, vipFlags):
try:
vid = id; parts = vid.split('|'); play_id = parts[0]
j = self._live_play(play_id); m3u8 = self._find_first_m3u8(j) if isinstance(j, dict) else ''
if not m3u8: return {"parse": 1, "playUrl": "", "url": f"{self.base}/play/{play_id.split('_')[0]}", "header": self._play_headers()}
return {"parse": 0, "playUrl": "", "url": m3u8, "header": self._play_headers()}
except Exception: return {"parse": 1, "playUrl": "", "url": f"{self.base}", "header": self._play_headers()}
def liveContent(self, url):
try:
play_id = url; j = self._live_play(play_id)
m3u8 = self._find_first_m3u8(j)
if m3u8: return {"parse": 0, "url": m3u8, "header": self._play_headers()}
except Exception: pass
return {"parse": 1, "url": f"{self.base}/play/{url}", "header": self._play_headers()}
def localProxy(self, param):
action = param.get('action') if isinstance(param, dict) else None
if action == 'play':
play_id = param.get('play_id', ''); j = self._live_play(play_id)
m3u8 = self._find_first_m3u8(j)
if m3u8: return self._redirect(m3u8)
return None
def _redirect(self, url):
return {"code": 302, "headers": {"Location": url}}
def _find_first_m3u8(self, obj):
try:
text = json.dumps(obj, ensure_ascii=False)
m = re.search(r'https?://[^\s"\\]+\.m3u8[^\s"\\]*', text)
if m: return m.group(0)
except Exception: pass
return ''
def _play_headers(self):
return {'User-Agent': self.ua, 'Referer': self.base + '/', 'Origin': self.base}
def _to_vod(self, it):
title = it.get('title') or it.get('userNick') or it.get('userId') or 'LIVE'
pic = it.get('thumbUrl') or it.get('ivsThumbnail') or ''
user_id = it.get('userId', ''); play_id = it.get('code', user_id); vod_id = f"{play_id}|{user_id}|{title}"
remarks = f"观众 {it.get('user', 0)} | 点赞 {it.get('likeCnt', 0)}"
return {'vod_id': vod_id, 'vod_name': title, 'vod_pic': pic, 'vod_remarks': remarks}
+250
View File
@@ -0,0 +1,250 @@
import requests
from bs4 import BeautifulSoup
import re
from base.spider import Spider
import sys
import json
import base64
import urllib.parse
sys.path.append('..')
murl = "https://3642.7rnr.com/web/index.html"
headerx = {
'User-Agent': "Mozilla/5.0 (Linux; Android 13; M2102J2SC Build/TKQ1.221114.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/143.0.7499.3 Mobile Safari/537.36",
'Accept-Encoding': "gzip, deflate, br, zstd"
}
response = requests.get(murl, allow_redirects=True)
xurl = response.url
nurl = xurl + '/web/abcdefg.ashx'
pm = ''
class Spider(Spider):
global xurl
global headerx
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "1003", "type_name": "亚洲无码"},
{"type_id": "3022", "type_name": "欧美无码"},
{"type_id": "3026", "type_name": "中文字幕"},
{"type_id": "3025", "type_name": "经典三级"},
{"type_id": "5", "type_name": "国产主播"},
{"type_id": "134", "type_name": "韩国主播"},
{"type_id": "3137", "type_name": "ASMR"},
{"type_id": "3138", "type_name": "恐怖色情"},
{"type_id": "131", "type_name": "网红视频"},
{"type_id": "132", "type_name": "国产视频"},
{"type_id": "3023", "type_name": "人妖伪娘"},
{"type_id": "130", "type_name": "动漫卡通"},
{"type_id": "3088", "type_name": "华人原创"},
{"type_id": "3135", "type_name": "JVID"},
{"type_id": "3136", "type_name": "SWAG"},
{"type_id": "3134", "type_name": "明星换脸"}]}
return result
def homeVideoContent(self):
videos = []
try:
payload = {
'action': "getindexdata",
't': "1762753963537691",
's': "5ad87a586f5aae9c2ca4f913d45f8958"}
detail = requests.post(url=nurl, data=payload, headers=headerx)
detail.encoding = "utf-8"
res = detail.json()
video_list = res.get("videos", [])
for video in video_list:
name = video.get("title", "")
id = video.get("id", "")
pic = video.get("coverimg", "")
remarks = video.get("updatedate", "")
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
result = {'list': videos}
return result
except:
pass
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
if pg:
page = int(pg)
else:
page = 1
if page == '1':
payload1 = {
'action': "getvideos",
'vtype': {cid},
'pageindex': "1",
'pagesize': "12",
'tags': "全部",
'sortindex': "1",
't': "176275570014518",
's': "ff4218e4cafd552c4d0c93eb935c14f1"}
else:
payload1 = {
'action': "getvideos",
'vtype': {cid},
'pageindex': {str(page)},
'pagesize': "12",
'tags': "全部",
'sortindex': "1",
't': "176275570014518",
's': "ff4218e4cafd552c4d0c93eb935c14f1"}
try:
detail = requests.post(url=nurl, data=payload1, headers=headerx)
detail.encoding = "utf-8"
res = detail.json()
video_list = res.get("videos", [])
for video in video_list:
name = video.get("title", "")
id = video.get("id", "")
pic = video.get("coverimg", "")
remarks = video.get("updatedate", "")
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
except:
pass
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 99
result['limit'] = 90
result['total'] = 99
return result
def detailContent(self, ids):
did = ids[0]
result = {}
videos = []
playurl = ''
payload2 = {
'action': "getvideo",
'vid': did,
't': "1762756475175265",
's': "656d5b40c2122f86bc35895dc58fd113"}
res1 = requests.post(url=nurl, data=payload2, headers=headerx)
res1.encoding = "utf-8"
res = res1.json()
node = res.get("data", {}).get("Table", [{}])[0]
vod_id = node.get("id", "")
vod_name = node.get("title", "")
vod_pic = node.get("coverimg", "")
vod_remarks = node.get("updatedate", "")
vod_content = node.get("title", "")
playFrom = []
playList = []
if node.get("vurl"):
base_url1 = res.get("xldata", {}).get("value", "")
base_url2 = res.get("xldata", {}).get("value1", "")
if base_url1:
full_vurl1 = base_url1 + node.get("vurl", "")
playFrom.append("播放源1")
playList.append(full_vurl1)
if base_url2:
full_vurl2 = base_url2 + node.get("vurl", "")
playFrom.append("播放源2")
playList.append(full_vurl2)
videos.append({
"vod_id": vod_id,
"vod_name": vod_name,
"vod_pic": vod_pic,
"vod_remarks": vod_remarks,
"vod_content": vod_content,
"vod_play_from": "$$$".join(playFrom),
"vod_play_url": "$$$".join(playList)
})
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
parts = id.split("http")
xiutan = 1
if xiutan == 1:
if len(parts) > 1:
before_https, after_https = parts[0], 'http' + parts[1]
result = {}
result["parse"] = xiutan
result["playUrl"] = ''
result["url"] = after_https
result["header"] = headerx
return result
def searchContentPage(self, key, quick, page):
result = {}
videos = []
payload3 = {
'action': "search",
'p': {key},
'pageindex': {str(page)},
'pagesize': "12",
'channelid': "0",
't': "1762756927087982",
's': "2392bd117b4e6e35b5ec1fa9bc380b6f"}
detail = requests.post(url=nurl, data=payload3, headers=headerx)
detail.encoding = "utf-8"
res = detail.json()
video_list = res.get("data", [])
for video in video_list:
name = video.get("title", "")
id = video.get("id", "")
pic = video.get("imgurl", "")
remarks = video.get("updatedate", "")
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
result['list'] = videos
result['page'] = page
result['pagecount'] = 60
result['limit'] = 30
result['total'] = 999999
return result
def searchContent(self, key, quick):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+303
View File
@@ -0,0 +1,303 @@
import sys
import urllib.parse
import re
from lxml import etree
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def getName(self):
return "禁片天堂"
def init(self, extend):
pass
def homeContent(self, filter):
cateManual = {
"中文": "278",
"巨乳": "15",
"熟女": "95",
"騎乘位": "74",
"口交": "34",
"癡女": "75",
"潮吹": "32",
"企劃片": "84",
"美尻": "156",
"打手槍": "98",
"戲劇、連續劇": "58",
"制服": "19",
"美腿": "157",
"舔鮑": "122",
"美乳": "166",
"搭訕": "12",
"妄想族": "184",
"第一人稱視點": "167",
"媽媽系": "193",
"人妻・主婦": "26",
"多種職業": "84",
"羞辱": "163",
"女教師": "131",
"淫語": "151",
"肉感": "136",
"愛美臀": "111",
"背後位": "178",
"調教": "395",
"處男": "23",
"護士": "283",
"修長": "147",
"露內褲": "169",
"絲襪": "115",
"愛巨乳": "200",
"眼鏡": "290",
"超乳": "211",
"顏面騎乘": "263",
"惡作劇": "145",
"義母": "144",
"淫亂・過激系": "63",
"愛美腿": "11",
"爆乳": "483",
"女上司": "137",
"正太": "415",
"穿衣幹砲": "179",
"緊身皮衣": "304",
"學園": "421",
"空姐": "132",
"粉絲感謝祭": "190",
"背面騎乗位": "646",
"秘書": "363",
"女主播": "106",
"反向搭訕": "305",
"健身教練": "233",
"部下・同僚": "150",
"舞蹈": "130",
"緊身衣激凸": "321",
"3D影片": "508",
"早洩": "403"
}
result = {'class': [{'type_name': k, 'type_id': v} for k, v in cateManual.items()]}
return result
def homeVideoContent(self):
return {}
def categoryContent(self, tid, pg, filter, extend):
result = {}
url = f'https://jptt.tv/tag_list?tid={tid}&idx={pg}'
try:
rsp = self.fetch(url)
root = etree.HTML(rsp.text)
videos = root.xpath('//div[contains(@class,"oneVideo")]')
vodList = []
for video in videos:
try:
name_elements = video.xpath('.//h3/text()')
if not name_elements:
continue
name = name_elements[0].strip()
img_elements = video.xpath('.//img/@src')
if not img_elements:
continue
img = img_elements[0]
if not img.startswith('http'):
img = 'https://jptt.tv' + img
desc_elements = video.xpath('.//p[contains(@class,"p_duration")]/text()')
desc = desc_elements[0].strip() if desc_elements else ''
link_elements = video.xpath('.//a/@href')
if not link_elements:
continue
link = link_elements[0]
vodList.append({
"vod_name": name,
"vod_pic": img,
"vod_remarks": desc,
"vod_id": link
})
except Exception as e:
print(f"[categoryContent video parse error]: {e}")
continue
result['list'] = vodList
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
except Exception as e:
print(f"[categoryContent fetch error]: {e}")
result['list'] = []
result['page'] = pg
result['pagecount'] = 0
result['limit'] = 0
result['total'] = 0
return result
def detailContent(self, array):
tid = array[0]
url = tid if tid.startswith('http') else f'https://jptt.tv{tid}'
try:
rsp = self.fetch(url)
root = etree.HTML(rsp.text)
title_elements = root.xpath('//h1[@class="h1_title"]/text()')
title = title_elements[0].strip() if title_elements else "未知标题"
pic_elements = root.xpath('//video/@poster')
pic = pic_elements[0] if pic_elements else ""
if pic and not pic.startswith('http'):
pic = 'https://jptt.tv' + pic
desc_elements = root.xpath('//div[contains(@class,"info_original")]//p/text()')
desc = desc_elements[0].strip() if desc_elements else title
play_url = self.extractVideoUrl(rsp.text)
vod = {
"vod_id": tid,
"vod_name": title,
"vod_pic": pic,
"vod_content": desc,
"vod_play_from": "屌戳插操",
"vod_play_url": "日你底下$" + play_url
}
return {'list': [vod]}
except Exception as e:
print(f"[detailContent error]: {e}")
return {'list': []}
def extractVideoUrl(self, html):
try:
source_match = re.search(r'<source\s+src="([^"]+)"', html)
if source_match:
video_url = source_match.group(1)
if video_url.startswith('//'):
video_url = 'https:' + video_url
return video_url
hls_patterns = [
r'//cdn-[^"\']+\.m3u8[^"\']*',
r'https?://[^"\']+\.m3u8[^"\']*',
r'/hlsredirect/[^"\']+\.m3u8'
]
for pattern in hls_patterns:
matches = re.findall(pattern, html)
if matches:
for match in matches:
if match.startswith('//'):
return 'https:' + match
elif match.startswith('http'):
return match
else:
return 'https://jptt.tv' + match
js_patterns = [
r'src\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
r'url\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
r'file\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']'
]
for pattern in js_patterns:
match = re.search(pattern, html)
if match:
video_url = match.group(1)
if video_url.startswith('//'):
return 'https:' + video_url
elif video_url.startswith('http'):
return video_url
else:
return 'https://jptt.tv' + video_url
all_m3u8 = re.findall(r'["\'](https?://[^"\']+\.m3u8[^"\']*)["\']', html)
if all_m3u8:
return all_m3u8[0]
except Exception as e:
print(f"[extractVideoUrl error]: {e}")
return "https://cdn-mso2.jptt1.cc/hlsredirect/EXBrcBO4G9RhgaUlZQhY1w/1760457600/hls/video/1/99-22-00164.3gp/index.m3u8"
def searchContent(self, key, quick, pg="1"):
result = {}
url = f'https://jptt.tv/search?kw={urllib.parse.quote(key)}'
try:
rsp = self.fetch(url)
root = etree.HTML(rsp.text)
videos = root.xpath('//div[contains(@class,"oneVideo")]')
vodList = []
for video in videos:
try:
name_elements = video.xpath('.//h3/text()')
if not name_elements:
continue
name = name_elements[0].strip()
img_elements = video.xpath('.//img/@src')
if not img_elements:
continue
img = img_elements[0]
if not img.startswith('http'):
img = 'https://jptt.tv' + img
desc_elements = video.xpath('.//p[contains(@class,"p_duration")]/text()')
desc = desc_elements[0].strip() if desc_elements else ''
link_elements = video.xpath('.//a/@href')
if not link_elements:
continue
link = link_elements[0]
vodList.append({
"vod_name": name,
"vod_pic": img,
"vod_remarks": desc,
"vod_id": link
})
except Exception as e:
print(f"[searchContent video parse error]: {e}")
continue
result['list'] = vodList
except Exception as e:
print(f"[searchContent fetch error]: {e}")
result['list'] = []
return result
def playerContent(self, flag, id, vipFlags):
result = {}
if flag == "屌戳插操":
try:
if id.startswith('http') and '.m3u8' in id:
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
else:
url = id if id.startswith('http') else f'https://jptt.tv{id}'
rsp = self.fetch(url)
play_url = self.extractVideoUrl(rsp.text)
result["parse"] = 0
result["playUrl"] = ''
result["url"] = play_url
except Exception as e:
print(f"[playerContent error]: {e}")
result["parse"] = 0
result["playUrl"] = ''
result["url"] = "https://cdn-mso2.jptt1.cc/hlsredirect/EXBrcBO4G9RhgaUlZQhY1w/1760457600/hls/video/1/99-22-00164.3gp/index.m3u8"
result["header"] = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
"Referer": "https://jptt.tv/",
"Origin": "https://jptt.tv"
}
return result
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def localProxy(self, param):
pass
+385
View File
@@ -0,0 +1,385 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import random
import base64
import html
import json
import time
import sys
import re
import os
sys.path.append('..')
headerz = {
'sec-ch-ua': '"Microsoft Edge";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Accept-Encoding': 'gzip, deflate'
}
xurl = "https://rb.huaduys.org"
response = requests.get(xurl, headers=headerz)
cookie_dict = {}
for cookie in response.cookies:
cookie_dict[cookie.name] = cookie.value
first_cookie_key = None
first_cookie_value = None
server_session_value = cookie_dict.get('server_name_session')
for key, value in cookie_dict.items():
if key != 'server_name_session':
first_cookie_key = key
first_cookie_value = value
break
headerx = {
"Host": "rb.huaduys.org",
"Connection": "keep-alive",
"sec-ch-ua": '"Microsoft Edge";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Dest": "document",
"Referer": "https://rb.huaduys.org/",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": f"{first_cookie_key}={first_cookie_value}; server_name_session={server_session_value}",
"Accept-Encoding": "gzip, deflate"
}
headers = {
'User-Agent': 'Linux; Android 12; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Mobile Safari/537.36'
}
class Spider(Spider):
global xurl
global headerx
global headers
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def parse_videos_from_doc(self, doc, xurl):
videos = []
skip_names = ["广告点赞"]
soups = doc.find_all('ul', class_="stui-vodlist clearfix")
for soup in soups:
vods = soup.find_all('li')
for vod in vods:
remarks = vod.find('a', class_="stui-vodlist__thumb picture w-thumb img-shadow")
remark = remarks.text.strip() + "点赞"
if remark in skip_names:
continue
names = vod.find('h4', class_="title text-overflow")
name = names.text.strip()
id = names.find('a')['href']
pic = vod.find('img')['data-original']
if 'http' not in pic:
pic = xurl + pic
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": '集多▶️' + remark
}
videos.append(video)
return videos
def homeContent(self, filter):
result = {"class": []}
detail = requests.get(url=xurl, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
doc = BeautifulSoup(res, "lxml")
soups = doc.find_all('ul', class_="stui-header__menu type-slide")
for soup in soups:
vods = soup.find_all('li')
for vod in vods:
name = vod.text.strip()
skip_names = ["首页", "发布页", "免费VPN下载"]
if name in skip_names:
continue
id1 = vod.find('a')['href']
fenge = id1.split(".html")
id = f"{fenge[0]}-----------.html"
id = id.replace('vodtype', 'vodshow')
result["class"].append({"type_id": id, "type_name": "集多🌠" + name})
return result
def homeVideoContent(self):
videos = []
detail = requests.get(url=xurl, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
doc = BeautifulSoup(res, "lxml")
videos = self.parse_videos_from_doc(doc, xurl)
result = {'list': videos}
return result
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
if pg:
page = int(pg)
else:
page = 1
fenge = cid.split("---.html")
url = f'{xurl}{fenge[0]}{str(page)}---.html'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
doc = BeautifulSoup(res, "lxml")
videos = self.parse_videos_from_doc(doc, xurl)
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
result = {}
videos = []
xianlu = ''
bofang = ''
if 'http' not in did:
did = xurl + did
res = requests.get(url=did, headers=headerx)
res.encoding = "utf-8"
res = res.text
res = html.unescape(res)
url = 'http://rihou.cc:88/je.json'
response = requests.get(url)
response.encoding = 'utf-8'
code = response.text
name = self.extract_middle_text(code, "s1='", "'", 0)
Jumps = self.extract_middle_text(code, "s2='", "'", 0)
content = '集多🎉为您介绍剧情📢' + self.extract_middle_text(res, '标题:', '</span>', 1, 'alt="(.*?)">')
director = self.extract_middle_text(res, '分类:', '</p>', 1, 'target=".*?">(.*?)</a>')
actor = self.extract_middle_text(res, '演员:', '</span>', 1, 'target=".*?">(.*?)</a>')
remarks = self.extract_middle_text(res, '类别:', '</li>', 1, 'target=".*?">(.*?)</a>')
year = self.extract_middle_text(res, '日期:', 'p>', 1, '</strong>(.*?)<')
area = self.extract_middle_text(res, '时长:', 'p>', 1, '</strong>(.*?)<')
if name not in content:
bofang = Jumps
xianlu = '1'
else:
id = self.extract_middle_text(res, 'class="btn btn-primary" href="', '"', 0)
if 'http' not in id:
id = xurl + id
name = "集多请您欣赏"
bofang = name + '$' + id
xianlu = '花都专线'
videos.append({
"vod_id": did,
"vod_director": director,
"vod_actor": actor,
"vod_remarks": remarks,
"vod_year": year,
"vod_area": area,
"vod_content": content,
"vod_play_from": xianlu,
"vod_play_url": bofang
})
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
detail = requests.get(url=id, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
url = self.extract_middle_text(res, '"","url":"', '"', 0).replace('\\', '')
base64_decoded_bytes = base64.b64decode(url)
base64_decoded_string = base64_decoded_bytes.decode('utf-8')
url = unquote(base64_decoded_string)
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = url
result["header"] = headers
return result
def searchContentPage(self, key, quick, pg):
result = {}
videos = []
url = f'{xurl}/vodsearch/-------------.html?wd={key}'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
res = detail.text
doc = BeautifulSoup(res, "lxml")
videos = self.parse_videos_from_doc(doc, xurl)
result['list'] = videos
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+371
View File
@@ -0,0 +1,371 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 丢丢喵 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import urlparse
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://eibdlw.hq123.icu"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
class Spider(Spider):
def getName(self):
return "丢丢喵"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def searchContentPage(self, key, quick, pg):
pass
def homeVideoContent(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def decrypt_data(self, token: str, key: str = "UC2FmMyG928hRZY4") -> dict:
padded_token = self.pad_token(token)
bin_data = self.base64_decode(padded_token)
k = self.encode_key(key)
payload = self.xor_decrypt(bin_data, k)
body = self.decompress_payload(payload)
return self.parse_json_body(body)
def pad_token(self, token):
return token + '=' * (4 - len(token) % 4)
def base64_decode(self, token):
return base64.urlsafe_b64decode(token)
def encode_key(self, key):
return key.encode('utf-8')
def xor_decrypt(self, bin_data, k):
return bytes(b ^ k[i % len(k)] for i, b in enumerate(bin_data))
def decompress_payload(self, payload):
if payload[0] == 1:
return zlib.decompress(payload[1:], -15)
return payload[1:]
def parse_json_body(self, body):
return json.loads(body.decode('utf-8'))
def homeContent(self, filter):
result = {"class": []}
res = self.get_home_page()
ress = self.extract_app_index(res)
data = self.decrypt_data(ress)
self.process_menu_items(data['menu'], result)
return result
def get_home_page(self):
detail = requests.get(url=xurl, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_app_index(self, res):
return self.extract_middle_text(res, "APP.Index('", "'", 0)
def process_menu_items(self, menu, result):
for vod in menu:
if not self.is_skipped_name(vod['name']):
self.append_class(result, vod)
def is_skipped_name(self, name):
skip_names = ["首頁", "AI脱衣", "小说", "影视剧", "涩漫", "抖淫", "黄游", "AI伴侣", "同城泻火"]
return name in skip_names
def append_class(self, result, vod):
result["class"].append({"type_id": vod['id'], "type_name": vod['name']})
def categoryContent(self, cid, pg, filter, ext):
videos = []
page = self.get_page_number(pg)
if '9' in cid:
url = self.build_gua_url(cid, page)
res = self.get_page_content(url)
ress = self.extract_app_index(res)
data = self.decrypt_data(ress)
self.process_gua_list(data['list'], videos)
else:
url = self.build_video_url(cid, page)
res = self.get_page_content(url)
ress = self.extract_app_index(res)
data = self.decrypt_data(ress)
self.process_video_list(data['list'], videos)
result = self.build_category_result(videos, pg)
return result
def get_page_number(self, pg):
return int(pg) if pg else 1
def build_gua_url(self, cid, page):
return f'{xurl}/category/{cid}/-{str(page)}-'
def build_video_url(self, cid, page):
return f'{xurl}/category/{cid}/---{str(page)}'
def get_page_content(self, url):
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_app_index(self, res):
return self.extract_middle_text(res, "APP.Index('", "'", 0)
def process_gua_list(self, vod_list, videos):
for vod in vod_list:
video = self.parse_gua_video(vod)
videos.append(video)
def parse_gua_video(self, vod):
return {
"vod_id": f"{vod.get('art_id')}@gua_details",
"vod_name": vod.get('art_name'),
"vod_pic": vod.get('art_pic')
}
def process_video_list(self, vod_list, videos):
for vod in vod_list:
video = self.parse_video(vod)
videos.append(video)
def parse_video(self, vod):
return {
"vod_id": f"{vod.get('vod_id')}@videoplay",
"vod_name": vod.get('vod_name'),
"vod_pic": vod.get('vod_pic')
}
def build_category_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def decode_video_url(self, encrypted_hex):
key = self.get_aes_key()
cipher_hex, iv_hex = self.split_hex_data(encrypted_hex)
ciphertext = self.hex_to_bytes(cipher_hex)
iv = self.hex_to_bytes(iv_hex)
cipher = self.create_aes_cipher(key, iv)
decrypted_bytes = self.decrypt_ciphertext(cipher, ciphertext)
decrypted_str = self.bytes_to_string(decrypted_bytes)
return self.parse_json(decrypted_str)
def get_aes_key(self):
return b"WB0nMZHXlxNndORe"
def split_hex_data(self, encrypted_hex):
n = len(encrypted_hex)
if n < 48:
cipher_hex = encrypted_hex[:n - 32]
iv_hex = encrypted_hex[n - 32:]
else:
cipher_hex = encrypted_hex[:16] + encrypted_hex[48:]
iv_hex = encrypted_hex[16:48]
return cipher_hex, iv_hex
def hex_to_bytes(self, hex_str):
return binascii.unhexlify(hex_str)
def create_aes_cipher(self, key, iv):
return AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128)
def decrypt_ciphertext(self, cipher, ciphertext):
return cipher.decrypt(ciphertext)
def bytes_to_string(self, decrypted_bytes):
return decrypted_bytes.decode('utf-8')
def parse_json(self, decrypted_str):
return json.loads(decrypted_str)
def detailContent(self, ids):
did = self.get_first_id(ids)
fenge = self.split_did(did)
if self.is_gua_details(fenge):
data = self.get_gua_details_data(fenge)
bofang = self.extract_gua_play_url(data)
else:
data = self.get_normal_details_data(fenge)
bofang = self.extract_normal_play_url(data)
videos = self.build_video_info(did, bofang)
result = self.build_detail_result(videos)
return result
def get_first_id(self, ids):
return ids[0]
def split_did(self, did):
return did.split("@")
def is_gua_details(self, fenge):
return 'gua_details' in fenge[1]
def get_gua_details_data(self, fenge):
url = self.build_gua_details_url(fenge)
res = self.get_page_content(url)
ress = self.extract_middle_text(res, "content:'", "'", 0)
return self.decode_video_url(ress)
def build_gua_details_url(self, fenge):
return f'{xurl}/{fenge[1]}/{fenge[0]}'
def get_page_content(self, url):
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
return detail.text
def extract_gua_play_url(self, data):
return data['0']['vod_play_url'][0]['list'][0]['h264']
def get_normal_details_data(self, fenge):
url = self.build_normal_details_url(fenge)
res = self.get_page_content(url)
ress = self.extract_middle_text(res, "APP.Index('", "'", 0)
return self.decrypt_data(ress)
def build_normal_details_url(self, fenge):
return f'{xurl}/{fenge[1]}/{fenge[0]}'
def extract_normal_play_url(self, data):
return data.get('info', {}).get('vod_play_url') or data.get('info', {}).get('vod_down_url')
def build_video_info(self, did, bofang):
return [{"vod_id": did,"vod_play_from": "黄区专线","vod_play_url": bofang}]
def build_detail_result(self, videos):
result = {}
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headerx
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+278
View File
@@ -0,0 +1,278 @@
# -*- coding: utf-8 -*-
import json,re,sys,base64,requests,threading,time,random,colorsys
from Crypto.Cipher import AES
from pyquery import PyQuery as pq
from urllib.parse import quote, unquote
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
SELECTORS=['.video-item','.video-list .item','.list-item','.post-item']
def getName(self):return"黑料不打烊"
def init(self,extend=""):pass
def homeContent(self,filter):
cateManual={"最新黑料":"hlcg","今日热瓜":"jrrs","每日TOP10":"mrrb","反差女友":"fczq","校园黑料":"xycg","网红黑料":"whhl","明星丑闻":"mxcw","原创社区":"ycsq","推特社区":"ttsq","社会新闻":"shxw","官场爆料":"gchl","影视短剧":"ysdj","全球奇闻":"qqqw","黑料课堂":"hlkt","每日大赛":"mrds","激情小说":"jqxs","桃图杂志":"ttzz","深夜综艺":"syzy","独家爆料":"djbl"}
return{'class':[{'type_name':k,'type_id':v}for k,v in cateManual.items()]}
def homeVideoContent(self):return{}
def categoryContent(self,tid,pg,filter,extend):
url=f'https://heiliao.com/{tid}/'if int(pg)==1 else f'https://heiliao.com/{tid}/page/{pg}/'
videos=self.get_list(url)
return{'list':videos,'page':pg,'pagecount':9999,'limit':90,'total':999999}
def fetch_and_decrypt_image(self,url):
try:
if url.startswith('//'):url='https:'+url
elif url.startswith('/'):url='https://heiliao.com'+url
r=requests.get(url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36','Referer':'https://heiliao.com/'},timeout=15,verify=False)
if r.status_code!=200:return b''
return AES.new(b'f5d965df75336270',AES.MODE_CBC,b'97b60394abc2fbe1').decrypt(r.content)
except: return b''
def _extract_img_from_onload(self,node):
try:
m=re.search(r"load(?:Share)?Img\s*\([^,]+,\s*['\"]([^'\"]+)['\"]",(node.attr('onload')or''))
return m.group(1)if m else''
except:return''
def _should_decrypt(self,url:str)->bool:
u=(url or'').lower();return any(x in u for x in['pic.gylhaa.cn','new.slfpld.cn','/upload_01/','/upload/'])
def _abs(self,u:str)->str:
if not u:return''
if u.startswith('//'):return'https:'+u
if u.startswith('/'):return'https://heiliao.com'+u
return u
def e64(self,s:str)->str:
try:return base64.b64encode((s or'').encode()).decode()
except:return''
def d64(self,s:str)->str:
try:return base64.b64decode((s or'').encode()).decode()
except:return''
def _img(self,img_node):
u=''if img_node is None else(img_node.attr('src')or img_node.attr('data-src')or'')
enc=''if img_node is None else self._extract_img_from_onload(img_node)
t=enc or u
return f"{self.getProxyUrl()}&url={self.e64(t)}&type=hlimg"if t and(enc or self._should_decrypt(t))else self._abs(t)
def _parse_items(self,root):
vids=[]
for sel in self.SELECTORS:
for it in root(sel).items():
title=it.find('.title, h3, h4, .video-title').text()
if not title:continue
link=it.find('a').attr('href')
if not link:continue
vids.append({'vod_id':self._abs(link),'vod_name':title,'vod_pic':self._img(it.find('img')),'vod_remarks':it.find('.date, .time, .remarks, .duration').text()or''})
if vids:break
return vids
def detailContent(self,array):
tid=array[0];url=tid if tid.startswith('http')else f'https://heiliao.com{tid}'
rsp=self.fetch(url)
if not rsp:return{'list':[]}
rsp.encoding='utf-8';html_text=rsp.text
try:root_text=pq(html_text)
except:root_text=None
try:root_content=pq(rsp.content)
except:root_content=None
title=(root_text('title').text()if root_text else'')or''
if' - 黑料网'in title:title=title.replace(' - 黑料网','')
pic=''
if root_text:
og=root_text('meta[property="og:image"]').attr('content')
if og and(og.endswith('.png')or og.endswith('.jpg')or og.endswith('.jpeg')):pic=og
else:pic=self._img(root_text('.video-item-img img'))
detail=''
if root_text:
detail=root_text('meta[name="description"]').attr('content')or''
if not detail:detail=root_text('.content').text()[:200]
play_from,play_url=[],[]
if root_content:
for i,p in enumerate(root_content('.dplayer').items()):
c=p.attr('config')
if not c:continue
try:s=(c.replace('&quot;','"').replace('&#34;','"').replace('&amp;','&').replace('&#38;','&').replace('&lt;','<').replace('&#60;','<').replace('&gt;','>').replace('&#62;','>'));u=(json.loads(s).get('video',{})or{}).get('url','')
except:m=re.search(r'"url"\s*:\s*"([^"]+)"',c);u=m.group(1)if m else''
if u:
u=u.replace('\\/','/');u=self._abs(u)
# Extract article ID for danmaku
article_id = self._extract_article_id(tid)
if article_id:
play_from.append(f'视频{i+1}');play_url.append(f"{article_id}_dm_{u}")
else:
play_from.append(f'视频{i+1}');play_url.append(u)
if not play_url:
for pat in[r'https://hls\.[^"\']+\.m3u8[^"\']*',r'https://[^"\']+\.m3u8\?auth_key=[^"\']+',r'//hls\.[^"\']+\.m3u8[^"\']*']:
for u in re.findall(pat,html_text):
u=self._abs(u)
article_id = self._extract_article_id(tid)
if article_id:
play_from.append(f'视频{len(play_from)+1}');play_url.append(f"{article_id}_dm_{u}")
else:
play_from.append(f'视频{len(play_from)+1}');play_url.append(u)
if len(play_url)>=3:break
if play_url:break
if not play_url:
js_patterns=[r'video[\s\S]{0,500}?url[\s"\'`:=]+([^"\'`\s]+)',r'videoUrl[\s"\'`:=]+([^"\'`\s]+)',r'src[\s"\'`:=]+([^"\'`\s]+\.m3u8[^"\'`\s]*)']
for pattern in js_patterns:
js_urls=re.findall(pattern,html_text)
for js_url in js_urls:
if'.m3u8'in js_url:
if js_url.startswith('//'):js_url='https:'+js_url
elif js_url.startswith('/'):js_url='https://heiliao.com'+js_url
elif not js_url.startswith('http'):js_url='https://'+js_url
article_id = self._extract_article_id(tid)
if article_id:
play_from.append(f'视频{len(play_from)+1}');play_url.append(f"{article_id}_dm_{js_url}")
else:
play_from.append(f'视频{len(play_from)+1}');play_url.append(js_url)
if len(play_url)>=3:break
if play_url:break
if not play_url:
article_id = self._extract_article_id(tid)
example_url = "https://hls.obmoti.cn/videos5/b9699667fbbffcd464f8874395b91c81/b9699667fbbffcd464f8874395b91c81.m3u8?auth_key=1760372539-68ed273b94e7a-0-3a53bc0df110c5f149b7d374122ef1ed&v=2"
if article_id:
play_from.append('示例视频');play_url.append(f"{article_id}_dm_{example_url}")
else:
play_from.append('示例视频');play_url.append(example_url)
return{'list':[{'vod_id':tid,'vod_name':title,'vod_pic':pic,'vod_content':detail,'vod_play_from':'$$$'.join(play_from),'vod_play_url':'$$$'.join(play_url)}]}
def searchContent(self,key,quick,pg="1"):
rsp=self.fetch(f'https://heiliao.com/index/search?word={key}')
if not rsp:return{'list':[]}
return{'list':self._parse_items(pq(rsp.text))}
def playerContent(self,flag,id,vipFlags):
# Check if this is a danmaku-enabled video
if '_dm_' in id:
aid, pid = id.split('_dm_', 1)
p = 0 if re.search(r'\.(m3u8|mp4|flv|ts|mkv|mov|avi|webm)', pid) else 1
if not p:
pid = f"{self.getProxyUrl()}&pdid={quote(id)}&type=m3u8"
return {'parse': p, 'url': pid, 'header': {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36","Referer":"https://heiliao.com/"}}
else:
return{"parse":0,"playUrl":"","url":id,"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36","Referer":"https://heiliao.com/"}}
def get_list(self,url):
rsp=self.fetch(url)
return[]if not rsp else self._parse_items(pq(rsp.text))
def fetch(self,url,params=None,cookies=None,headers=None,timeout=5,verify=True,stream=False,allow_redirects=True):
h=headers or{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36","Referer":"https://heiliao.com/"}
return super().fetch(url,params=params,cookies=cookies,headers=h,timeout=timeout,verify=verify,stream=stream,allow_redirects=allow_redirects)
def localProxy(self,param):
try:
xtype = param.get('type', '')
if xtype == 'hlimg':
url=self.d64(param.get('url'))
if url.startswith('//'):url='https:'+url
elif url.startswith('/'):url='https://heiliao.com'+url
r=requests.get(url,headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36","Referer":"https://heiliao.com/"},timeout=15,verify=False)
if r.status_code!=200:return[404,'text/plain','']
b=AES.new(b'f5d965df75336270',AES.MODE_CBC,b'97b60394abc2fbe1').decrypt(r.content)
ct='image/jpeg'
if b.startswith(b'\x89PNG'):ct='image/png'
elif b.startswith(b'GIF8'):ct='image/gif'
return[200,ct,b]
elif xtype == 'm3u8':
# Handle danmaku-enabled video
path, url = unquote(param['pdid']).split('_dm_', 1)
data = requests.get(url, headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36","Referer":"https://heiliao.com/"}, timeout=10).text
lines = data.strip().split('\n')
times = 0.0
for i in lines:
if i.startswith('#EXTINF:'):
times += float(i.split(':')[-1].replace(',', ''))
# Start background thread to refresh danmaku
thread = threading.Thread(target=self.some_background_task, args=(path, int(times)))
thread.start()
print('[INFO] 获取视频时长成功', times)
return [200, 'text/plain', data]
elif xtype == 'hlxdm':
# Return danmaku XML for heiliao comments
article_id = param.get('path', '')
times = int(param.get('times', 0))
comments = self._fetch_heiliao_comments(article_id)
return self._generate_danmaku_xml(comments, times)
except Exception as e:
print(f'[ERROR] localProxy: {e}')
return[404,'text/plain','']
def _extract_article_id(self, url):
"""Extract article ID from heiliao.com URL"""
try:
if '/archives/' in url:
match = re.search(r'/archives/(\d+)/?', url)
return match.group(1) if match else None
return None
except:
return None
def _fetch_heiliao_comments(self, article_id, max_pages=3):
"""Fetch comments from heiliao.com API"""
comments = []
try:
for page in range(1, max_pages + 1):
url = f"https://heiliao.com/comments/1/{article_id}/{page}.json"
resp = requests.get(url, headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.7049.96 Safari/537.36","Referer":"https://heiliao.com/"}, timeout=10)
if resp.status_code == 200:
data = resp.json()
if 'data' in data and 'list' in data['data'] and data['data']['list']:
for comment in data['data']['list']:
text = comment.get('content', '').strip()
if text and len(text) <= 100: # Filter out too long comments
comments.append(text)
# Also get replies from comments.list
if 'comments' in comment and 'list' in comment['comments'] and comment['comments']['list']:
for reply in comment['comments']['list']:
reply_text = reply.get('content', '').strip()
if reply_text and len(reply_text) <= 100:
comments.append(reply_text)
# Check if there are more pages
if not data['data'].get('next', False):
break
else:
break # No more comments
else:
break
except Exception as e:
print(f'[ERROR] _fetch_heiliao_comments: {e}')
return comments[:50] # Limit to 50 comments max
def _generate_danmaku_xml(self, comments, video_duration):
"""Generate danmaku XML from comments"""
try:
total_comments = len(comments)
tsrt = f'共有{total_comments}条弹幕来袭!!!'
danmu_xml = f'<?xml version="1.0" encoding="UTF-8"?>\n<i>\n\t<chatserver>chat.heiliao.com</chatserver>\n\t<chatid>88888888</chatid>\n\t<mission>0</mission>\n\t<maxlimit>99999</maxlimit>\n\t<state>0</state>\n\t<real_name>0</real_name>\n\t<source>heiliao</source>\n'
danmu_xml += f'\t<d p="0,5,25,16711680,0">{tsrt}</d>\n'
for i, comment in enumerate(comments):
# Distribute comments across video duration
base_time = (i / total_comments) * video_duration if total_comments > 0 else 0
dm_time = base_time + random.uniform(-3, 3)
dm_time = round(max(0, min(dm_time, video_duration)), 1)
dm_color = self._get_danmaku_color()
# Clean comment text
dm_text = re.sub(r'[<>&\u0000\b]', '', comment)
danmu_xml += f'\t<d p="{dm_time},1,25,{dm_color},0">{dm_text}</d>\n'
danmu_xml += '</i>'
return [200, "text/xml", danmu_xml]
except Exception as e:
print(f'[ERROR] _generate_danmaku_xml: {e}')
return [500, 'text/html', '']
def _get_danmaku_color(self):
"""Get danmaku color (90% white, 10% random)"""
if random.random() < 0.1:
h = random.random()
s = random.uniform(0.7, 1.0)
v = random.uniform(0.8, 1.0)
r, g, b = colorsys.hsv_to_rgb(h, s, v)
r = int(r * 255)
g = int(g * 255)
b = int(b * 255)
return str((r << 16) + (g << 8) + b)
else:
return '16777215' # White
def some_background_task(self, article_id, video_duration):
"""Background task to refresh danmaku in FongMi"""
try:
time.sleep(1)
danmaku_url = f"{self.getProxyUrl()}&path={quote(article_id)}&times={video_duration}&type=hlxdm"
self.fetch(f"http://127.0.0.1:9978/action?do=refresh&type=danmaku&path={quote(danmaku_url)}")
print(f'[INFO] 弹幕刷新成功: {article_id}')
except Exception as e:
print(f'[ERROR] some_background_task: {e}')