diff --git a/开心影院.py b/开心影院.py new file mode 100644 index 0000000..570ae74 --- /dev/null +++ b/开心影院.py @@ -0,0 +1,521 @@ +# -*- coding: utf-8 -*- +# 开心影院 - TVbox Python 正式版 + +import re +import sys +import json +import urllib.parse +from pyquery import PyQuery as pq + +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.extend = extend + + def getName(self): + return "开心影院" + + def isVideoFormat(self, url): + return False + + def manualVideoCheck(self): + return False + + def destroy(self): + pass + + host = 'https://www.kxyy2.cc' + + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; Mobile) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/114.0.0.0 Mobile Safari/537.36', + 'Referer': host, + } + + # 分类配置 + classes_config = [ + ("电影", "1"), + ("电视剧", "2"), + ("综艺", "3"), + ("动漫", "4"), + ("短剧", "26"), + ("纪录片", "24"), + ] + + # ==================== 首页 ==================== + def homeContent(self, filter): + result = {} + classes = [] + vlist = [] + + for name, tid in self.classes_config: + classes.append({ + "type_name": name, + "type_id": tid + }) + + # ====== 筛选配置 ====== + filters = {} + + # 电影 (1) + filters["1"] = [ + {"key": "class", "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": "动画"}, + ]}, + {"key": "area", "name": "地区", "value": [ + {"n": "全部", "v": ""}, + {"n": "中国大陆", "v": "中国大陆"}, + {"n": "中国香港", "v": "中国香港"}, + {"n": "美国", "v": "美国"}, + {"n": "日本", "v": "日本"}, + {"n": "韩国", "v": "韩国"}, + {"n": "英国", "v": "英国"}, + ]}, + {"key": "year", "name": "年份", "value": [ + {"n": "全部", "v": ""}, + {"n": "2026", "v": "2026"}, + {"n": "2025", "v": "2025"}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + ]}, + ] + + # 电视剧 (2) + filters["2"] = [ + {"key": "class", "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": "穿越"}, + ]}, + {"key": "area", "name": "地区", "value": [ + {"n": "全部", "v": ""}, + {"n": "中国大陆", "v": "中国大陆"}, + {"n": "中国香港", "v": "中国香港"}, + {"n": "中国台湾", "v": "中国台湾"}, + {"n": "美国", "v": "美国"}, + {"n": "日本", "v": "日本"}, + {"n": "韩国", "v": "韩国"}, + {"n": "泰国", "v": "泰国"}, + {"n": "英国", "v": "英国"}, + ]}, + {"key": "year", "name": "年份", "value": [ + {"n": "全部", "v": ""}, + {"n": "2026", "v": "2026"}, + {"n": "2025", "v": "2025"}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + ]}, + ] + + # 综艺 (3) + filters["3"] = [ + {"key": "class", "name": "类型", "value": [ + {"n": "全部", "v": ""}, + {"n": "大陆综艺", "v": "大陆综艺"}, + {"n": "港台综艺", "v": "港台综艺"}, + {"n": "日韩综艺", "v": "日韩综艺"}, + {"n": "欧美综艺", "v": "欧美综艺"}, + ]}, + {"key": "year", "name": "年份", "value": [ + {"n": "全部", "v": ""}, + {"n": "2026", "v": "2026"}, + {"n": "2025", "v": "2025"}, + {"n": "2024", "v": "2024"}, + ]}, + ] + + # 动漫 (4) + filters["4"] = [ + {"key": "class", "name": "类型", "value": [ + {"n": "全部", "v": ""}, + {"n": "国产动漫", "v": "国产动漫"}, + {"n": "日本动漫", "v": "日本动漫"}, + {"n": "欧美动漫", "v": "欧美动漫"}, + ]}, + {"key": "year", "name": "年份", "value": [ + {"n": "全部", "v": ""}, + {"n": "2026", "v": "2026"}, + {"n": "2025", "v": "2025"}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + ]}, + ] + + # 短剧 (26) + filters["26"] = [ + {"key": "year", "name": "年份", "value": [ + {"n": "全部", "v": ""}, + {"n": "2026", "v": "2026"}, + {"n": "2025", "v": "2025"}, + {"n": "2024", "v": "2024"}, + ]}, + ] + + result['filters'] = filters + + # 获取首页 + rsp = self.fetch(self.host, headers=self.headers) + data = pq(rsp.text) + + vlist.extend(self.getlist(data('.col-4.rows-md-7 .card-sm'))) + + result['class'] = classes + result['list'] = vlist[:40] + return result + + def homeVideoContent(self): + return {} + + # ==================== 分类 ==================== + def categoryContent(self, tid, pg, filter, extend): + page = int(pg) if pg else 1 + + area = extend.get('area', '') if extend else '' + + if area: + url = f'{self.host}/vodshow/{tid}-{area}-------{page}---.html' + else: + if page > 1: + url = f'{self.host}/vodshow/{tid}-----------{page}---.html' + else: + url = f'{self.host}/vodshow/{tid}-----------.html' + + rsp = self.fetch(url, headers=self.headers) + data = pq(rsp.text) + + videos = self.getlist(data('.col-lg-8.col-4 .card-sm')) + if not videos: + videos = self.getlist(data('.card-sm')) + + result = { + 'list': videos, + 'page': page, + 'pagecount': 9999, + 'limit': 90, + 'total': 999999 + } + return result + + # ==================== 详情 ==================== + def detailContent(self, ids): + vid = ids[0] + if not vid.startswith('http'): + if vid.startswith('/'): + url = self.host.rstrip('/') + vid + else: + url = self.host.rstrip('/') + '/' + vid + else: + url = vid + + rsp = self.fetch(url, headers=self.headers) + if not rsp: + return {'list': []} + + html = rsp.text + data = pq(html) + + # ===== 标题 ===== + name = data('h1').text().strip() + if not name: + name = data('h2.d-none').text().strip() + if not name: + name = data('h2').text().strip() + + # ===== 封面 ===== + pic = data('.cover-lg-max-25 img').attr('src') or '' + if not pic: + pic = data('.col-md-auto img').attr('src') or '' + if pic and ('logo' in pic.lower() or 'favicon' in pic.lower()): + pic = '' + if pic and not pic.startswith('http'): + if pic.startswith('/'): + pic = self.host.rstrip('/') + pic + else: + pic = self.host.rstrip('/') + '/' + pic + + # ===== 简介 ===== + content = data('#synopsis .card-body p').text().strip() + if not content: + content = data('.card-body .text-secondary p').text().strip() + + # ===== 信息提取 ===== + director = '' + actor = '' + year = '' + area = '' + type_name = '' + + for info in data('.text-truncate-sm').items(): + text = info.text().strip() + if '导演' in text: + directors = [a.text().strip() for a in info('a').items()] + director = ','.join(directors) if directors else '' + elif '主演' in text: + actors = [a.text().strip() for a in info('a').items()] + actor = ','.join(actors) if actors else '' + elif '类型' in text: + types = [a.text().strip() for a in info('a').items()] + type_name = ','.join(types) if types else '' + elif '制片国家/地区' in text: + area_match = re.search(r'\[([^\]]+)\]', text) + if area_match: + area = area_match.group(1) + elif '首播' in text: + year_match = re.search(r'(\d{4})', text) + if year_match: + year = year_match.group(1) + + if not year: + year_match = re.search(r'(\d{4})', name) + if year_match: + year = year_match.group(1) + + # ===== 备注 ===== + remarks = data('.text-orange').text().strip() + if not remarks: + remarks = data('.badge.bg-pink').text().strip() + if not remarks: + remarks = '更新中' + + vod = { + 'vod_id': vid, + 'vod_name': name, + 'vod_pic': pic, + 'type_name': type_name, + 'vod_year': year, + 'vod_area': area, + 'vod_remarks': remarks, + 'vod_actor': actor, + 'vod_director': director, + 'vod_content': content, + 'vod_play_from': '', + 'vod_play_url': '' + } + + # ====== 播放列表 ====== + # 线路名称 (正则提取, 最稳定) + tabs = [] + nav_match = re.search(r'