# -*- coding: utf-8 -*- # by @Qist """ LibVio (libvio.run) - 免费在线观影 """ import re import json import requests from urllib.parse import quote from base.spider import Spider class Spider(Spider): def getName(self): return "LibVio" def init(self, extend=""): self.host = 'https://www.libvio.run' self.header = { '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': self.host + '/', 'Origin': self.host, } self.timeout = 20 self.session = requests.Session() self.session.headers.update(self.header) def _safe_get(self, url, max_retries=3): for attempt in range(max_retries): try: resp = self.session.get(url, timeout=self.timeout, verify=False) resp.encoding = 'utf-8' return resp except: import time time.sleep(1) return None def _parse_videos(self, html): videos = [] seen = set() pattern = re.compile( r'href="/detail/(\d+)\.html"[^>]+title="([^"]*)"[^>]+data-original="([^"]*)"[^>]*>.*?' r'([^<]*).*?' r'([^<]*)', re.DOTALL ) for m in pattern.finditer(html): vid = m.group(1) if vid in seen: continue seen.add(vid) vod_name = m.group(2) vod_pic = m.group(3) vod_remarks = m.group(4).strip() score = m.group(5).strip() if score and score != '0.0': vod_remarks = vod_remarks + '/' + score if vod_remarks else score videos.append({ 'vod_id': vid, 'vod_name': vod_name, 'vod_pic': vod_pic, 'vod_remarks': vod_remarks, }) return videos def isVideoFormat(self, url): pass def manualVideoCheck(self): pass def destroy(self): pass def homeContent(self, filter): try: result = {'class': [], 'list': []} classes = [ {'type_name': '电影', 'type_id': '1'}, {'type_name': '剧集', 'type_id': '2'}, {'type_name': '国剧', 'type_id': '13'}, {'type_name': '动作片', 'type_id': '6'}, {'type_name': '喜剧片', 'type_id': '7'}, {'type_name': '爱情片', 'type_id': '8'}, {'type_name': '科幻片', 'type_id': '9'}, {'type_name': '恐怖片', 'type_id': '10'}, {'type_name': '剧情片', 'type_id': '11'}, {'type_name': '战争片', 'type_id': '12'}, {'type_name': '动画片', 'type_id': '23'}, {'type_name': '纪录片', 'type_id': '21'}, {'type_name': '港台剧', 'type_id': '14'}, {'type_name': '日韩剧', 'type_id': '15'}, {'type_name': '欧美剧', 'type_id': '16'}, {'type_name': '泰国剧', 'type_id': '24'}, {'type_name': '综艺', 'type_id': '3'}, {'type_name': '番剧', 'type_id': '4'}, ] result['class'] = classes # 获取首页推荐视频 resp = self._safe_get(self.host + '/') if resp and resp.status_code == 200: result['list'] = self._parse_videos(resp.text)[:10] filter_cfg = { '1': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "泰国", "v": "泰国"}, {"n": "英国", "v": "英国"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '6': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '7': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '8': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '9': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '10': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '11': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '12': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '23': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '21': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "美国", "v": "美国"}, {"n": "日本", "v": "日本"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '2': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国台湾", "v": "中国台湾"}, {"n": "中国香港", "v": "中国香港"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "美国", "v": "美国"}, {"n": "泰国", "v": "泰国"}, {"n": "英国", "v": "英国"}, {"n": "新加坡", "v": "新加坡"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '13': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '14': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国香港", "v": "中国香港"}, {"n": "中国台湾", "v": "中国台湾"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '15': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '16': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "美国", "v": "美国"}, {"n": "英国", "v": "英国"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '24': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "泰国", "v": "泰国"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '3': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "中国香港"}, {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "美国", "v": "美国"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], '4': [ {"key": "area", "name": "地区", "value": [ {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "日本", "v": "日本"}, {"n": "美国", "v": "美国"}, {"n": "其他", "v": "其他"}, ]}, {"key": "by", "name": "排序", "value": [ {"n": "最新", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}, ]}, ], } result['filters'] = filter_cfg return result except Exception as e: print(f"Error in homeContent: {str(e)}") return {"class": [], "list": []} def homeVideoContent(self): try: resp = self._safe_get(self.host + '/') if not resp or resp.status_code != 200: return {} videos = self._parse_videos(resp.text) return {'list': videos[:30]} except Exception as e: print(f"Error in homeVideoContent: {str(e)}") return {} def categoryContent(self, tid, pg, filter, extend): try: url = f"{self.host}/show/{tid}--------{pg}---.html" resp = self._safe_get(url) if not resp or resp.status_code != 200: return {'list': [], 'page': int(pg), 'pagecount': 1, 'limit': 0, 'total': 0} html = resp.text videos = self._parse_videos(html) pagecount_match = re.search(r'(\d+)/(\d+)', html) pagecount = int(pagecount_match.group(2)) if pagecount_match else 999 last_page_match = re.search(r'class="page-link"[^>]*href="[^"]*-(\d+)---\.html"[^>]*>尾页', html) if last_page_match: pagecount = int(last_page_match.group(1)) return { 'list': videos, 'page': int(pg), 'pagecount': pagecount, 'limit': len(videos), 'total': pagecount * len(videos) if videos else 0, } except Exception as e: print(f"Error in categoryContent: {str(e)}") return {'list': [], 'page': int(pg), 'pagecount': 1, 'limit': 0, 'total': 0} def detailContent(self, ids): try: vid = ids[0] url = f"{self.host}/detail/{vid}.html" resp = self._safe_get(url) if not resp or resp.status_code != 200: return {'list': []} html = resp.text vod_name = '' h1_match = re.search(r'