-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #131 from badasr/main
add caiji r18
- Loading branch information
Showing
1 changed file
with
376 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,376 @@ | ||
// ==MiruExtension== | ||
// @name 18影视集合 | ||
// @version v0.0.1 | ||
// @author Bad | ||
// @lang zh-cn | ||
// @license MIT | ||
// @package vod.api.json.collection18 | ||
// @type bangumi | ||
// @webSite https:// | ||
// @nsfw true | ||
// ==/MiruExtension== | ||
|
||
export default class extends Extension { | ||
apis = { | ||
// '91av': 'https://91av.cyou/api.php/provide/vod/at/json', | ||
'91md': 'https://91md.me/api.php/provide/vod/at/json', | ||
'madou': 'http://wuxu.cc/api.php/provide/vod/from/mdm3u8/', | ||
'tianmei': 'http://www.tianmei.pw/api.php/provide/vod/from/m3u8/', | ||
'madouse': 'http://madouse.cc/api.php/provide/vod/at/json', | ||
'chujia': 'http://chujia.cc/api.php/provide/vod/from/m3u8/', | ||
'qqcm': 'https://qqcm.sbs/api.php/provide/vod/at/json', | ||
'shayuapi': 'https://shayuapi.com/api.php/provide/vod/', | ||
'apilyzy': 'https://api.apilyzy.com/api.php/provide/vod/', | ||
'sewozy': 'https://sewozyapi.com/api.php/provide/vod', | ||
'lajiao': 'https://apilj.com/api.php/provide/vod/at/json/', | ||
'xiaocao': 'https://www.caoliuzyw.com/api.php/provide/vod', | ||
'apiyikanapi': 'https://api.yikanapi.com/api.php/provide/vod/at/json', | ||
'zy018': 'https://www.zy018.com/api.php/provide/vod/at/json', | ||
// '9szy': 'https://9szy.net/api.php/provide/vod/at/json', | ||
// 'apibukazyw': 'https://api.bukazyw.fun/api.php/provide/vod/at/json', | ||
'kuaiavzy': 'http://kuaiavzy.com/api.php/provide/vod/at/json', | ||
// 'apivodkok2': 'https://api.vodkok2.com/api.php/provide/vod/at/json', | ||
'maozyapi': 'https://api.maozyapi.com/inc/apijson_vod.php', | ||
'siwazywtw': 'https://www.siwazyw.tv/api.php/provide/vod/at/json', | ||
'semao': 'https://caiji.semaozy.net/inc/apijson_vod.php', | ||
'putaozyw': 'https://caiji.putaozyw.net/inc/apijson_vod.php', | ||
'apiddapi': 'https://api.ddapi.cc/api.php/provide/vod/at/json', | ||
'apixbapi': 'https://api.xbapi.cc/api.php/provide/vod/at/json', | ||
'apiyirenziyuan': 'https://api.yirenziyuan.com/api.php/provide/vod/at/json', | ||
'seyavod': 'https://api.seyavod.com/api.php/provide/vod/at/json', | ||
// 'jizhiapi': 'https://jizhiapi.com/api.php/provide/vod/at/json', | ||
'timizy10': 'https://timizy10.cc/api.php/provide/vod/at/json', | ||
'naixxzy': 'https://naixxzy.com/api.php/provide/vod/at/json', | ||
'aosikazy': 'https://aosikazy.com/api.php/provide/vod/?ac=list', | ||
'sexnguon': 'https://api.sexnguon.com/api.php/provide/vod/at/json', | ||
'156249': 'http://156.249.29.8/inc/apijson_vod.php', | ||
'kkzy': 'https://kkzy.me/api.php/provide/vod/at/json', | ||
// 'apig14o': 'https://api.g14o.cc/api.php/provide/vod/at/json', | ||
'bominzy': 'https://www.bominzy.com/api.php/provide/vod/at/json', | ||
// '155api': 'https://155api.com/api.php/provide/vod/?ac=list', | ||
// 'avre00': 'https://www.avre00.com/api.php/provide/vod/?ac=list', | ||
'mtav': 'https://mtav.art/api.php/provide/vod/at/json', | ||
'888dav': 'https://www.888dav.com/api.php/provide/vod/', | ||
'apittzy': 'https://apittzy.com/api.php/provide/vod/at/json', | ||
'8day': 'https://8day.icu/api.php/provide/vod/at/json', | ||
'5bo1': 'https://5bo1.xyz/api.php/provide/vod/at/json', | ||
'92free': 'http://92free.icu/api.php/provide/vod/at/json', | ||
'tmav': 'https://tmav.art/api.php/provide/vod/at/json', | ||
'sezy': 'https://sezy.website/api.php/provide/vod/at/json', | ||
'xxavs': 'https://xxavs.com/api.php/provide/vod/at/json', | ||
'auezy': 'https://a.uezy.pw/api.php/provide/vod/at/json', | ||
'hongxiuzy': 'https://hongxiuzy.com/api.php/provide/vod/', | ||
'apilsbzy1': 'https://apilsbzy1.com/api.php/provide/vod/at/json', | ||
// 'toto': 'https://to.to-long.com/api.php/provide/vod/at/json', | ||
'huangpian': 'https://www.pgxdy.com/api/json.php', | ||
'apittzy': 'https://apittzy.com/api.php/provide/vod/at/json', | ||
// 'huakuiapi': 'https://caiji.huakuiapi.com/inc/apijson_vod.php', | ||
'hghhh': 'https://hghhh.com/api.php/provide/vod/at/json', | ||
// 'slapibf': 'https://slapibf.com/api.php/provide/vod/at/json', | ||
'apiyutu': 'https://apiyutu.com/api.php/provide/vod/at/json', | ||
// 'hszy': 'http://hszy.me/api.php/provide/vod/at/json', | ||
'haopianapi': 'https://haopianapi.com/api.php/provide/vod/at/json', | ||
'ziyuan': 'https://ziyuan.skm3u8.com/api.php/provide/vod/at/json', | ||
'jkunzyapi': 'https://jkunzyapi.com/api.php/provide/vod/at/json', | ||
'888dav': 'https://www.888dav.com/api.php/provide/vod/at/json', | ||
'msnii': 'https://www.msnii.com/api/json.php', | ||
'xrbsp': 'https://www.xrbsp.com/api/json.php', | ||
'gdlsp': 'https://www.gdlsp.com/api/json.php', | ||
'kxgav': 'https://www.kxgav.com/api/json.php', | ||
'afasu': 'https://www.afasu.com/api/json.php', | ||
// 'lbapi9': 'https://lbapi9.com/api.php/provide/vod/at/json', | ||
'fhapi9': 'http://fhapi9.com/api.php/provide/vod/at/json' | ||
} | ||
|
||
api = 'https://91md.me/api.php/provide/vod/at/json' | ||
headers = { | ||
'User-Agent': | ||
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36' | ||
} | ||
videoInfoCache = {} | ||
apiCategoryCache = {} | ||
defaultApiKey = null | ||
|
||
async latest(page) { | ||
let res | ||
if (!this.defaultApiKey) { | ||
for (const key of Object.keys(this.apis)) { | ||
this.api = this.apis[key] | ||
this.defaultApiKey = key | ||
try { | ||
res = await this.callApi({ page, action: 'videolist' }) | ||
break | ||
} catch (error) { | ||
console.log(`源 ${key} 加载失败,自动切换到下一个源`) | ||
} | ||
} | ||
} else { | ||
res = await this.callApi({ page, action: 'videolist' }) | ||
} | ||
res.list.forEach(item => { | ||
this.videoInfoCache[item.vod_id] = item | ||
}) | ||
return res.list.map(item => ({ | ||
title: item.vod_name, | ||
url: `${this.api}|${item.vod_id}`, | ||
cover: item.vod_pic, | ||
//desc: item.vod_content, | ||
update: item.vod_remarks | ||
})) | ||
} | ||
|
||
async createFilter(filter) { | ||
const api = { | ||
title: '源', | ||
min: 1, | ||
max: 1, | ||
default: this.defaultApiKey || '91av', | ||
options: { | ||
// '91av': '91视频', | ||
'91md': '九麻', | ||
'madou': '麻豆', | ||
'tianmei': '天美', | ||
'madouse': '果冻', | ||
'chujia': '精东', | ||
'qqcm': '传媒', | ||
'shayuapi': '鲨鱼', | ||
'apilyzy': '老鸭', | ||
'sewozy': '涩窝', | ||
'lajiao': '辣椒', | ||
'xiaocao': '小草', | ||
'apiyikanapi': '易看', | ||
'zy018': '十八', | ||
// '9szy': '九色', | ||
// 'apibukazyw': '不卡', | ||
'kuaiavzy': '快爱', | ||
// 'apivodkok2': 'KOK2', | ||
'maozyapi': '猫咪', | ||
'siwazywtw': '袜丝', | ||
'semao': '色猫', | ||
'putaozyw': '葡萄', | ||
'apiddapi': '滴滴', | ||
'apixbapi': '雪豹', | ||
'apiyirenziyuan': '依人', | ||
'seyavod': '色鸭', | ||
// 'jizhiapi': '极致', | ||
'timizy10': '甜蜜', | ||
'naixxzy': '奶香', | ||
'aosikazy': '奥斯', | ||
'sexnguon': '色南', | ||
'156249': '蛋蛋', | ||
'kkzy': '写真', | ||
// 'apig14o': '萝莉', | ||
'bominzy': '博民', | ||
// '155api': '十五', | ||
// 'avre00': '黄瓜', | ||
'mtav': '桃桃', | ||
'888dav': '八八', | ||
'apittzy': '天天', | ||
'8day': '八天', | ||
'5bo1': '五播', | ||
'92free': '久爱', | ||
'tmav': '美亚', | ||
'sezy': '色网', | ||
'xxavs': '湿园', | ||
'auezy': '优异', | ||
'hongxiuzy': '红袖', | ||
'apilsbzy1': '色逼', | ||
// 'toto': '橘猫', | ||
'huangpian': '黄片', | ||
'apittzy': '探探', | ||
// 'huakuiapi': '花魁', | ||
'hghhh': '皇冠', | ||
// 'slapibf': '森林', | ||
'apiyutu': '玉兔', | ||
// 'hszy': '黄色', | ||
'haopianapi': '好片', | ||
'ziyuan': '湿裤', | ||
'jkunzyapi': '鸡坤', | ||
'888dav': '抖阴', | ||
'msnii': '美女', | ||
'xrbsp': '淫水', | ||
'gdlsp': '香奶', | ||
'kxgav': '白嫖', | ||
'afasu': '湿妹', | ||
// 'lbapi9': '乐播', | ||
'fhapi9': '番号' | ||
} | ||
} | ||
|
||
let apiKey = this.defaultApiKey || 'baidu' | ||
|
||
// 选择后切换 api | ||
if (filter && filter.api) { | ||
this.api = this.apis[filter.api[0]] | ||
apiKey = filter.api[0] | ||
} | ||
|
||
return { | ||
api, | ||
category: { | ||
title: '分类', | ||
max: 1, | ||
min: 1, | ||
default: 'all', | ||
options: { | ||
all: '全部', | ||
...(await this.category(apiKey)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
async search(kw, page, filter) { | ||
const apiKey = filter?.api?.[0] | ||
const category = filter?.category?.[0] | ||
if (kw === '' && (!category || category === 'all')) { | ||
return this.latest(page) | ||
} | ||
let res | ||
if (kw && apiKey === '91av') { | ||
res = await this.callApi({ query: kw, page }) | ||
kw = res.list.map(item => item.vod_id).join() | ||
} | ||
if (kw.split(',').every(s => isNumeric(s))) { | ||
res = await this.callApi({ ids: kw, action: 'videolist' }) | ||
} else if (category && category !== 'all') { | ||
res = await this.callApi({ | ||
type: category, | ||
page, | ||
action: 'videolist' | ||
}) | ||
} else { | ||
res = await this.callApi({ query: kw, page, action: 'videolist' }) | ||
} | ||
res.list.forEach(item => { | ||
this.videoInfoCache[item.vod_id] = item | ||
}) | ||
return res.list.map(item => ({ | ||
title: item.vod_name, | ||
url: `${this.api}|${item.vod_id}`, | ||
cover: item.vod_pic, | ||
//desc: item.vod_content, | ||
update: item.vod_remarks | ||
})) | ||
} | ||
|
||
async detail(url) { | ||
var [api, url] = url.split('|') | ||
const item = | ||
this.videoInfoCache[url] || | ||
(await this.callApi({ ids: url, action: 'videolist', api })).list[0] | ||
const servers = item.vod_play_from.split('$$$') | ||
const episodes = [] | ||
loop: for (const [index, playlist] of Object.entries( | ||
item.vod_play_url.split('$$$') | ||
)) { | ||
const urls = [] | ||
var episodeName, playUrl; | ||
for (const info of rtrim(playlist, '#').split('#')) { | ||
if (info.includes("$")) { | ||
[episodeName, playUrl] = info.split('$') | ||
}else if (info.includes("=")) { | ||
[, playUrl] = info.split('=') | ||
episodeName='play' | ||
}else{ | ||
playUrl=info | ||
episodeName='play' | ||
} | ||
if (!playUrl.endsWith('.m3u8')) { | ||
continue loop | ||
} | ||
urls.push({ | ||
name: episodeName, | ||
url: playUrl | ||
}) | ||
} | ||
episodes.push({ | ||
title: servers[index], | ||
urls | ||
}) | ||
} | ||
var desc = item.vod_blurb | ||
if (!desc) { | ||
desc = item.vod_content | ||
} | ||
|
||
return { | ||
title: item.vod_name, | ||
cover: item.vod_pic, | ||
desc, | ||
episodes | ||
} | ||
} | ||
|
||
async watch(url) { | ||
return { | ||
type: 'hls', | ||
url | ||
} | ||
} | ||
|
||
async category(apiKey) { | ||
if (this.apiCategoryCache[apiKey]) { | ||
return this.apiCategoryCache[apiKey] | ||
} | ||
try { | ||
const res = await this.callApi() | ||
const options = Object.fromEntries( | ||
res.class.map(item => [item.type_id, item.type_name]) | ||
) | ||
this.apiCategoryCache[apiKey] = options | ||
return options | ||
} catch (error) { | ||
return {} | ||
} | ||
} | ||
|
||
async callApi({ | ||
query, | ||
page = 0, | ||
ids, | ||
type, | ||
pageSize, | ||
action = 'list', | ||
api = this.api | ||
} = {}) { | ||
var params = `?ac=${action}` | ||
if (query) { | ||
params += `&wd=${encodeURIComponent(query)}` | ||
} else if (ids) { | ||
params += `&ids=${ids}` | ||
} | ||
if (page > 0) { | ||
params += `&pg=${page}` | ||
} | ||
if (type) { | ||
params += `&t=${type}` | ||
} | ||
if (pageSize) { | ||
params += `&pagesize=${pageSize}` | ||
} | ||
const options = { | ||
headers: { | ||
...this.headers, | ||
Referer: api + params, | ||
'Miru-Url': api | ||
}, | ||
method: 'get' | ||
} | ||
return await this.request(params, options) | ||
} | ||
} | ||
|
||
function rtrim(str, ch) { | ||
let i = str.length | ||
while (i-- && str.charAt(i) === ch); | ||
return str.substring(0, i + 1) | ||
} | ||
|
||
function isNumeric(str) { | ||
if (typeof str != 'string') return false // we only process strings! | ||
return ( | ||
!isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)... | ||
!isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail | ||
) | ||
} |