Vision TV+
https://www.visiontv.club/
Simwoodone (12535)06/08 15:26
该用户很懒,什么介绍也没有写!
{
"bookSourceUrl": "https:\/\/www.visiontv.club\/",
"bookSourceName": "Vision TV+",
"enabledExplore": true,
"enabled": true,
"bookSourceGroup": "有声漫画",
"author": "",
"help": true,
"html": "<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Vision TV+ - 轻悦时光书源<\/title>\n<\/head>\n<body>\n<\/body>\n\n<script src=\"https:\/\/vc.jd.com\/web\/js\/jquery-3.1.1.min.js\"><\/script>\n<script>\n var isCookieJar = true;\n class FlutterJSBridge {\n constructor() { this.init(); }\n init() {\n if (window.flutter_inappwebview) { this.isReady = true; this.CookieJar(); }\n else { window.addEventListener('flutterInAppWebViewPlatformReady', () => { this.isReady = true; this.CookieJar(); }); }\n }\n async CookieJar() { try { await window.flutter_inappwebview.callHandler('CookieJar', isCookieJar); } catch (e) {} }\n async getDeviceid() { try { return await window.flutter_inappwebview.callHandler('id'); } catch (e) { return \"\"; } }\n async log(str) { try { return await window.flutter_inappwebview.callHandler('log', str); } catch (e) { return false; } }\n async showToast(str) { try { return await window.flutter_inappwebview.callHandler('showToast', str); } catch (e) { return false; } }\n async showLongToast(str) { try { return await window.flutter_inappwebview.callHandler('showLongToast', str); } catch (e) { return false; } }\n async openurl(url) { try { return await window.flutter_inappwebview.callHandler('openurl', url, \"\"); } catch (e) { return false; } }\n async startBrowser(url, title, header) { try { return await window.flutter_inappwebview.callHandler('startBrowser', url, title, header); } catch (e) { return \"\"; } }\n async showvideoplay(url, title, key) { try { return await window.flutter_inappwebview.callHandler('showvideoplay', url, title, key); } catch (e) { return \"\"; } }\n async webview(url, js, html, body, header) {\n try { return await window.flutter_inappwebview.callHandler('webview', url, js, html, body, header, \"\", \"\"); }\n catch (e) { return \"\"; }\n }\n }\n\n class Http {\n constructor() { this.open = false; this.requestTimestamps = []; this.rateLimit = 5; this.rateLimitWindow = 1000; }\n async checkRateLimit() {\n if (!this.open) return;\n const now = Date.now();\n this.requestTimestamps = this.requestTimestamps.filter(t => now - t < this.rateLimitWindow);\n if (this.requestTimestamps.length >= this.rateLimit) {\n await new Promise(r => setTimeout(r, this.rateLimitWindow - (now - this.requestTimestamps[0])));\n return this.checkRateLimit();\n }\n this.requestTimestamps.push(now);\n }\n async Get(url, headers, followRedirects) {\n try { await this.checkRateLimit(); return await window.flutter_inappwebview.callHandler('http', \"get\", url, \"\", JSON.stringify(headers), followRedirects, \"\"); }\n catch (e) { return null; }\n }\n }\n\n class Cache {\n constructor() {}\n async get(key) { try { return await window.flutter_inappwebview.callHandler('cache.get', key); } catch (e) { return null; } }\n async set(key, value) { try { return await window.flutter_inappwebview.callHandler('cache.set', key, value); } catch (e) { return null; } }\n async getLoginInfo() { return await this.get(\"LoginInfo\"); }\n }\n\n const flutterBridge = new FlutterJSBridge();\n const cache = new Cache();\n const http = new Http();\n\n const BASE_URL = 'https:\/\/www.visiontv.club';\n const UA = { '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' };\n\n function pick() {\n for (let i = 0; i < arguments.length; i++) {\n const v = arguments[i];\n if (v !== undefined && v !== null && String(v) !== '' && String(v) !== 'null') return v;\n }\n return '';\n }\n\n function asText(res) {\n if (!res) return '';\n if (typeof res === 'string') return res;\n if (typeof res.data === 'string') return res.data;\n if (typeof res.body === 'string') return res.body;\n try { return JSON.stringify(res.data); } catch (e) { return ''; }\n }\n\n function safeJson(text, fallback) { try { return JSON.parse(text); } catch (e) { return fallback; } }\n\n async function fetchHtml(url) {\n var res = await http.Get(url, UA, true);\n return asText(res);\n }\n\n \/\/ All pages need WebView to pass Cloudflare. Use webview() for all data extraction.\n\n function encodeUrl(type, url) {\n return 'vistv:\/\/' + type + '?url=' + encodeURIComponent(url);\n }\n function decodeUrl(s) {\n var m = String(s || '').indexOf('url=');\n if (m < 0) return s;\n return decodeURIComponent(String(s).slice(m + 4));\n }\n\n \/\/ ==================== 搜索 ====================\n async function search(key, page) {\n page = page || 1;\n var keyword = String(key || '').trim();\n if (!keyword || page > 1) return '[]';\n\n var url = BASE_URL + '\/index.php\/vod\/search.html?wd=' + encodeURIComponent(keyword);\n var js = \"(function(){\" +\n \"var items=document.querySelectorAll('[class*=public-list-box]');\" +\n \"var r=[];\" +\n \"for(var i=0;i<items.length;i++){\" +\n \"var box=items[i];\" +\n \"var a=box.querySelector('a[href*=\\\"\/detail\/\\\"]');\" +\n \"var img=box.querySelector('img');\" +\n \"var btn=box.querySelector('[class*=button]');\" +\n \"if(a){r.push({\" +\n \"\\\"href\\\":a.getAttribute('href')||'',\" +\n \"\\\"title\\\":a.getAttribute('title')||a.textContent.trim().slice(0,30),\" +\n \"\\\"img\\\":img?(img.getAttribute('src')||''):'',\" +\n \"\\\"status\\\":btn?btn.textContent.trim():''\" +\n \"});}\" +\n \"}\" +\n \"return JSON.stringify(r);\" +\n \"})()\";\n\n var data = await flutterBridge.webview(url, js, '', '', '{}');\n var list = safeJson(data, []);\n var result = [];\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n if (!item.href || !item.title) continue;\n result.push({\n bookUrl: encodeUrl('detail', item.href),\n name: item.title,\n author: '',\n kind: '',\n coverUrl: item.img.indexOf('http') === 0 ? item.img : '',\n intro: '',\n tocUrl: encodeUrl('detail', item.href),\n wordCount: '',\n type: 0,\n latestChapterTitle: item.status\n });\n }\n return JSON.stringify(result);\n }\n\n \/\/ ==================== 详情 ====================\n async function info(bookurl) {\n var detailUrl = decodeUrl(bookurl);\n if (detailUrl.indexOf('http') !== 0) detailUrl = BASE_URL + detailUrl;\n\n var js = \"(function(){\" +\n \"var t=document.title||'';t=t.replace(\/[-_].*Vision.*$\/i,'').trim();\" +\n \"var c=document.querySelector('img[src*=\\\".jpg\\\"],img[src*=\\\".png\\\"],img[src*=\\\".webp\\\"]');\" +\n \"var cover=c?c.getAttribute('src'):'';\" +\n \"var ogd=document.querySelector('meta[property=\\\"og:description\\\"]');\" +\n \"var intro=ogd?ogd.getAttribute('content'):'';\" +\n \"var ogt=document.querySelector('meta[property=\\\"og:title\\\"]');\" +\n \"if(ogt)t=ogt.getAttribute('content');\" +\n \"return JSON.stringify({name:t,cover:cover,intro:intro});\" +\n \"})()\";\n\n var data = await flutterBridge.webview(detailUrl, js, '', '', '{}');\n var info = safeJson(data, {});\n return JSON.stringify({\n bookUrl: bookurl,\n name: pick(info.name, '未知'),\n author: '',\n kind: '',\n coverUrl: pick(info.cover, ''),\n intro: pick(info.intro, ''),\n tocUrl: encodeUrl('detail', detailUrl),\n wordCount: '',\n type: 0,\n latestChapterTitle: ''\n });\n }\n\n \/\/ ==================== 目录 ====================\n async function chapter(tocUrl, bookurl) {\n var detailUrl = decodeUrl(tocUrl || bookurl);\n if (detailUrl.indexOf('http') !== 0) detailUrl = BASE_URL + detailUrl;\n\n \/\/ Extract play lines and episodes grouped by line\n var js = \"(function(){\" +\n \/\/ First, try to find line\/source tabs and their names\n \"var lineNames={};\" +\n \"var sourceTabs=document.querySelectorAll('[class*=source] a,[class*=play-source] a,[class*=tab] a,.source-list a,.play-source-tab a');\" +\n \"for(var i=0;i<sourceTabs.length;i++){\" +\n \"var tab=sourceTabs[i];\" +\n \"var sid=tab.getAttribute('data-sid')||tab.getAttribute('data-id')||tab.getAttribute('href');\" +\n \"if(sid&&sid.match(\/sid[=\/](\\\\d+)\/)){sid=sid.match(\/sid[=\/](\\\\d+)\/)[1];}\" +\n \"if(!sid)continue;\" +\n \"var name=tab.textContent.trim();\" +\n \"if(name&&name.length<20)lineNames[sid]=name;}\" +\n \/\/ If no tabs found, try to extract sid from episode URLs\n \"var allLinks=document.querySelectorAll('a[href*=\\\"\/play\/\\\"]');\" +\n \"var lines={};var allSids={};\" +\n \"for(var j=0;j<allLinks.length;j++){\" +\n \"var a=allLinks[j];\" +\n \"var t=a.textContent.trim();\" +\n \"var h=a.getAttribute('href');\" +\n \"if(!h||!t||t.length>50)continue;\" +\n \"var sidMatch=h.match(\/sid[=\/](\\\\d+)\/);\" +\n \"var sid=sidMatch?sidMatch[1]:'1';\" +\n \"allSids[sid]=1;\" +\n \"if(!lines[sid])lines[sid]=[];\" +\n \"lines[sid].push({name:t,url:h});}\" +\n \/\/ Build result with line names as volumes\n \"var result=[];var sids=Object.keys(allSids).sort();\" +\n \"for(var k=0;k<sids.length;k++){\" +\n \"var sid=sids[k];\" +\n \"var lineName=lineNames[sid]||('播放线路'+sid);\" +\n \"result.push({name:lineName,url:'',isVolume:true});\" +\n \"var eps=lines[sid]||[];\" +\n \"for(var e=0;e<eps.length;e++){\" +\n \"result.push({name:eps[e].name,url:eps[e].url,isVolume:false});}\" +\n \"}\" +\n \"return JSON.stringify(result);\" +\n \"})()\";\n\n var data = await flutterBridge.webview(detailUrl, js, '', '', '{}');\n var eps = safeJson(data, []);\n var result = [];\n for (var i = 0; i < eps.length; i++) {\n if (eps[i].isVolume) {\n result.push({\n name: eps[i].name,\n chapterId: '',\n index: i,\n isPay: false, isVip: false, isVolume: true, tag: ''\n });\n } else {\n result.push({\n name: eps[i].name || ('第' + (i + 1) + '集'),\n chapterId: encodeUrl('play', eps[i].url),\n index: i,\n isPay: false, isVip: false, isVolume: false, tag: ''\n });\n }\n }\n return JSON.stringify(result);\n }\n\n \/\/ ==================== 正文 ====================\n async function content(url, bookurl) {\n var playUrl = decodeUrl(url);\n if (playUrl.indexOf('http') !== 0) playUrl = BASE_URL + playUrl;\n\n var js = \"(function(){\" +\n \"var scripts=document.querySelectorAll('script');\" +\n \"for(var i=0;i<scripts.length;i++){\" +\n \"var t=scripts[i].textContent||'';\" +\n \"var m=t.match(\/player_\\\\w+\\\\s*=\\\\s*(\\\\{[^;]+\\\\});?\/);\" +\n \"if(m){\" +\n \"try{var d=JSON.parse(m[1]);if(d.url)return d.url.replace(\/\\\\\\\\\\\\\/\/g,'\/');}catch(e){}\" +\n \"}\" +\n \"m=t.match(\/thisUrl\\\\s*=\\\\s*\\\"([^\\\"]+?)\\\"\/);\" +\n \"if(m)return m[1];\" +\n \"}\" +\n \"var iframes=document.querySelectorAll('iframe');\" +\n \"for(var j=0;j<iframes.length;j++){\" +\n \"var s=iframes[j].getAttribute('src');\" +\n \"if(s&&(s.indexOf('m3u8')>-1||s.indexOf('.mp4')>-1))return s;}\" +\n \"return '';\" +\n \"})()\";\n\n try {\n var videoUrl = await flutterBridge.webview(playUrl, js, '', '', '{}');\n videoUrl = (videoUrl || '').trim();\n if (videoUrl) {\n return '<img src=\"data:image\/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSIwIDAgMjMgMjgiPjxkZWZzPjxmaWx0ZXIgaWQ9InMiPjxmZURyb3BTaGFkb3cgZHg9IjAiIGR5PSIwLjYiIHN0ZERldmlhdGlvbj0iMC43IiBmbG9vZC1jb2xvcj0iIzAwMCIgZmxvb2Qtb3BhY2l0eT0iMC4zNSIvPjwvZmlsdGVyPjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjUsNCkiPjxwYXRoIGQ9Ik0wIDBoMTR2MTRIMHoiIGZpbGw9Im5vbmUiLz48ZyBmaWxsPSJub25lIj48cGF0aCBmaWxsPSIjMjg1OWM1IiBkPSJNNC41IDguODgyVjUuMTE4YTEgMSAwIDAgMSAxLjQ0Ny0uODk0bDMuNzY0IDEuODgyYTEgMSAwIDAgMSAwIDEuNzg4TDUuOTQ3IDkuNzc2QTEgMSAwIDAgMSA0LjUgOC44ODIiLz48cGF0aCBmaWxsPSIjOGZiZmZhIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjA5LjA4NGEuNzUuNzUgMCAwIDAtLjIzMiAxLjQ4MkE1LjUwMiA1LjUwMiAwIDAgMSA3IDEyLjVhNS41IDUuNSAwIDAgMS01LjExNi0zLjQ3NWEuNzUuNzUgMCAwIDAtMS4zOTQuNTUyQTcuMDAyIDcuMDAyIDAgMCAwIDE0IDdBNyA3IDAgMCAwIDguMDkuMDg0bS0yLjQ2NiAxLjQ5QS43NS43NSAwIDAgMCA1LjI1MS4xMjNhNyA3IDAgMCAwLS45MzguMzE0YS43NS43NSAwIDAgMCAuNTc2IDEuMzg1cS4zNTYtLjE0OC43MzUtLjI0Nk0zLjE3IDIuOTU1YS43NS43NSAwIDAgMC0xLjA0NC0xLjA3NkE3IDcgMCAwIDAgLjkxNyAzLjQzNWEuNzUuNzUgMCAxIDAgMS4zMDMuNzQ0Yy4yNTgtLjQ1Mi41NzgtLjg2NC45NS0xLjIyNE0xLjU1NCA2LjEyN2EuNzUuNzUgMCAxIDAtMS40ODUtLjIxQTcgNyAwIDAgMCAwIDYuOTAzYS43NS43NSAwIDEgMCAxLjUgMHEwLS4zOTYuMDU0LS43NzYiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvZz48L2c+PHRleHQgeD0iMTEuNSIgeT0iMjUuNSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxLjMiIGZvbnQtZmFtaWx5PSJzeXN0ZW0tdWksLWFwcGxlLXN5c3RlbSxQaW5nRmFuZyBTQyxNaWNyb3NvZnQgWWFIZWksc2Fucy1zZXJpZiIgZmlsbD0iIzI4NTljNSIgZm9udC13ZWlnaHQ9ImJvbGQiIGZpbHRlcj0idXJsKCNzKSI+54K55Ye75pKt5pS+PC90ZXh0Pjwvc3ZnPg==,{"js":"flutterBridge.showvideoplay(\\''+videoUrl+'\\',\\'Vision TV+\\',\\''+videoUrl+'\\')"}\"\/>';\n }\n return '未找到视频地址\\n' + playUrl;\n } catch (e) {\n return '加载失败: ' + e.message;\n }\n }\n\n \/\/ ==================== 发现 ====================\n async function getfinds() {\n var finds = [];\n\n function addSection(title) {\n finds.push({ title: title, url: '', width: 3 });\n }\n function addItem(title, url) {\n finds.push({ title: title, url: url, type: 0, width: 0 });\n }\n\n \/\/ === 频道 ===\n addSection('📺 频道');\n var channels = [\n ['电影', '\/index.php\/vod\/type\/id\/1.html'],\n ['电视剧', '\/index.php\/vod\/type\/id\/2.html'],\n ['综艺', '\/index.php\/vod\/type\/id\/3.html'],\n ['动漫', '\/index.php\/vod\/type\/id\/4.html'],\n ['纪录片', '\/index.php\/vod\/type\/id\/5.html'],\n ['短剧', '\/index.php\/vod\/type\/id\/6.html'],\n ];\n for (var i = 0; i < channels.length; i++) {\n addItem(channels[i][0], BASE_URL + channels[i][1] + '?page={{page}}');\n }\n\n \/\/ === 类型 ===\n addSection('🏷️ 类型');\n var types = ['动作', '喜剧', '爱情', '科幻', '恐怖', '剧情', '战争', '动画', '纪录', '犯罪', '悬疑', '奇幻', '冒险'];\n for (var j = 0; j < types.length; j++) {\n addItem(types[j], BASE_URL + '\/index.php\/vod\/search.html?class=' + encodeURIComponent(types[j]) + '&page={{page}}');\n }\n\n \/\/ === 地区 ===\n addSection('🌍 地区');\n var areas = ['中国大陆', '中国香港', '中国台湾', '日本', '韩国', '美国', '英国', '法国', '德国', '泰国', '印度', '其他'];\n for (var k = 0; k < areas.length; k++) {\n addItem(areas[k], BASE_URL + '\/index.php\/vod\/search.html?area=' + encodeURIComponent(areas[k]) + '&page={{page}}');\n }\n\n \/\/ === 年份 ===\n addSection('📅 年份');\n var years = ['2026', '2025', '2024', '2023', '2022', '2021', '2020', '2019', '2018', '2017', '2016', '2015', '更早'];\n for (var y = 0; y < years.length; y++) {\n addItem(years[y], BASE_URL + '\/index.php\/vod\/search.html?year=' + years[y] + '&page={{page}}');\n }\n\n \/\/ === 语言 ===\n addSection('🔊 语言');\n var langs = ['国语', '英语', '日语', '韩语', '粤语', '法语', '德语', '泰语', '西班牙语', '其他'];\n for (var l = 0; l < langs.length; l++) {\n addItem(langs[l], BASE_URL + '\/index.php\/vod\/search.html?lang=' + encodeURIComponent(langs[l]) + '&page={{page}}');\n }\n\n \/\/ === 字母 ===\n addSection('🔤 字母');\n var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');\n for (var a = 0; a < letters.length; a++) {\n addItem(letters[a], BASE_URL + '\/index.php\/vod\/search.html?letter=' + letters[a] + '&page={{page}}');\n }\n\n finds.push({ title: '', url: '', width: 0 });\n finds.push({ title: '🔍 首次使用请先登录过CF', url: '', width: 3 });\n\n return JSON.stringify(finds);\n }\n\n \/\/ ==================== 发现搜索 ====================\n async function find(url, page) {\n page = page || 1;\n var realUrl = String(url || '').replace(\/\\{\\{page\\}\\}\/g, String(page));\n if (!realUrl) return '[]';\n\n var js = \"(function(){\" +\n \"var items=document.querySelectorAll('[class*=public-list-box]');\" +\n \"var r=[];\" +\n \"for(var i=0;i<items.length;i++){\" +\n \"var box=items[i];\" +\n \"var a=box.querySelector('a[href*=\\\"\/detail\/\\\"]');\" +\n \"var img=box.querySelector('img');\" +\n \"var btn=box.querySelector('[class*=button]');\" +\n \"if(a){r.push({\" +\n \"\\\"href\\\":a.getAttribute('href')||'',\" +\n \"\\\"title\\\":a.getAttribute('title')||a.textContent.trim().slice(0,30),\" +\n \"\\\"img\\\":img?(img.getAttribute('src')||''):'',\" +\n \"\\\"status\\\":btn?btn.textContent.trim():''\" +\n \"});}\" +\n \"}\" +\n \"return JSON.stringify(r);\" +\n \"})()\";\n\n var data = await flutterBridge.webview(realUrl, js, '', '', '{}');\n var list = safeJson(data, []);\n var result = [];\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n if (!item.href || !item.title) continue;\n result.push({\n bookUrl: encodeUrl('detail', item.href),\n name: item.title, author: '', kind: '',\n coverUrl: item.img.indexOf('http') === 0 ? item.img : '',\n intro: '', tocUrl: encodeUrl('detail', item.href),\n wordCount: '', type: 0, latestChapterTitle: item.status\n });\n }\n return JSON.stringify(result);\n }\n\n async function getloginurl() {\n \/\/ 通过搜索页过CF验证(默认搜索词\"我的\")\n return BASE_URL + '\/index.php\/vod\/search.html?wd=' + encodeURIComponent('我的');\n }\n async function login() {\n await flutterBridge.showToast('CF 验证完成,可以正常使用了');\n }\n async function pay(bookurl, url) {}\n async function imagedecrypt(url, image) { return []; }\n async function shouldOverrideUrlLoading(url) { return true; }\n async function gethelp() {\n return 'Vision TV+ v1.2 轻悦时光版(内置播放器)\\n\\n所有页面通过WebView过CF\\n正文调用内置播放器播放\\n搜索\/发现\/正文全覆盖';\n }\n<\/script>\n<\/html>\n",
"login": true,
"lastUpdateTime": "1780903584517"
}