1+ /*
2+ title: '嗷呜动漫', author: '小可乐/v6.1.1'
3+ 说明:可以不写ext,也可以写ext,ext支持的参数和格式参数如下
4+ "ext": {
5+ "host": "xxxx", //站点网址
6+ "timeout": 6000 //请求超时,单位毫秒
7+ }
8+ */
9+ import { Crypto } from 'assets://js/lib/cat.js' ;
10+
11+ const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36' ;
12+ const DefHeader = { 'User-Agent' : MOBILE_UA } ;
13+ var HOST ;
14+ var KParams = {
15+ headers : { 'User-Agent' : MOBILE_UA } ,
16+ timeout : 5000
17+ } ;
18+
19+ async function init ( cfg ) {
20+ try {
21+ HOST = ( cfg . ext ?. host ?. trim ( ) || 'https://www.aowu.tv' ) . replace ( / \/ $ / , '' ) ;
22+ KParams . headers [ 'Referer' ] = HOST ;
23+ let parseTimeout = parseInt ( cfg . ext ?. timeout ?. trim ( ) , 10 ) ;
24+ KParams . timeout = parseTimeout > 0 ? parseTimeout : 5000 ;
25+ } catch ( e ) {
26+ console . error ( '初始化参数失败:' , e . message ) ;
27+ }
28+ }
29+
30+ async function home ( filter ) {
31+ try {
32+ let kclassName = '新番$20&番剧$21&剧场$22' ;
33+ let classes = kclassName . split ( '&' ) . map ( item => {
34+ let [ cName , cId ] = item . split ( '$' ) ;
35+ return { type_name : cName , type_id : cId } ;
36+ } ) ;
37+ let filters = { } ;
38+ try {
39+ const nameObj = { class : 'class,剧情' , year : 'year,年份' , by : 'by,排序' } ;
40+ const flValues = { class : [ '搞笑' , '恋爱' , '校园' , '后宫' , '治愈' , '日常' , '原创' , '战斗' , '百合' , 'BL' , '卖肉' , '漫画改' , '游戏改' , '异世界' , '泡面番' , '轻小说改' , 'OVA' , 'OAD' , '京阿尼' , '芳文社' , 'A-1Pictures' , 'CloverWorks' , 'J.C.STAFF' , '动画工房' , 'SUNRISE' , 'Production.I.G' , 'MADHouse' , 'BONES' , 'P.A.WORKS' , 'SHAFT' , 'MAPPA' , 'ufotable' , 'TRIGGER' , 'WITSTUDIO' ] , year : [ '2026' , '2025' , '2024' , '2023' , '2022' , '2021' , '2020' , '2019' , '2018' , '2017' , '2016' , '2015' , '2014' , '2013' , '2012' , '2011' , '2010' , '2009' , '2008' , '2007' , '2006' , '2005' , '2004' , '2003' , '2002' , '2001' , '2000' , '1999' , '1998' , '1997' , '1996' , '1995' , '1994' , '1993' , '1992' , '1991' , '1990' ] , by : [ '按最新,time' , '按最热,hits' , '按评分,score' ] } ;
41+ for ( let item of classes ) {
42+ filters [ item . type_id ] = Object . entries ( nameObj ) . map ( ( [ nObjk , nObjv ] ) => {
43+ let [ kkey , kname ] = nObjv . split ( ',' ) ;
44+ let fvalue = flValues [ nObjk ] || [ ] ;
45+ if ( item . type_id === '20' && nObjk === 'year' ) { fvalue = fvalue . slice ( 0 , 2 ) ; }
46+ let kvalue = fvalue . map ( it => {
47+ let [ n , v ] = [ it , it ] ;
48+ if ( nObjk === 'by' ) { [ n , v ] = it . split ( ',' ) ; }
49+ return { n : n , v : v } ;
50+ } ) ;
51+ if ( nObjk !== 'by' ) { kvalue . unshift ( { n : '全部' , v : '' } ) ; }
52+ return { key : kkey , name : kname , value : kvalue } ;
53+ } ) . filter ( flt => flt . key && flt . value . length > 1 ) ;
54+ }
55+ } catch ( e ) {
56+ filters = { } ;
57+ }
58+ return JSON . stringify ( { class : classes , filters : filters } ) ;
59+ } catch ( e ) {
60+ console . error ( '获取分类失败:' , e . message ) ;
61+ return JSON . stringify ( { class : [ ] , filters : { } } ) ;
62+ }
63+ }
64+
65+ async function homeVod ( ) {
66+ try {
67+ let homeUrl = HOST ;
68+ let resHtml = await request ( homeUrl ) ;
69+ let VODS = getVodList ( resHtml , true ) ;
70+ return JSON . stringify ( { list : VODS } ) ;
71+ } catch ( e ) {
72+ console . error ( '推荐页获取失败:' , e . message ) ;
73+ return JSON . stringify ( { list : [ ] } ) ;
74+ }
75+ }
76+
77+ async function category ( tid , pg , filter , extend ) {
78+ try {
79+ pg = parseInt ( pg , 10 ) ;
80+ pg = pg > 0 ? pg : 1 ;
81+ let cateBody = `type=${ tid } &class=${ extend ?. class ?? '' } &year=${ extend ?. year ?? '' } &by=${ extend ?. by ?? '' } &page=${ pg } ` ;
82+ let cateUrl = `${ HOST } /index.php/ds_api/vod` ;
83+ let resObj = safeParseJSON ( await request ( cateUrl , {
84+ headers : { ...KParams . headers , 'Content-Type' : 'application/x-www-form-urlencoded; charset=utf-8' } ,
85+ method : 'POST' ,
86+ body : cateBody
87+ } ) ) ;
88+ if ( ! resObj ) { throw new Error ( '源码对象为空' ) ; }
89+ let VODS = [ ] ;
90+ let listArr = Array . isArray ( resObj . list ) ? resObj . list : [ ] ;
91+ for ( let it of listArr ) {
92+ let kname = it . vod_name || '名称' ;
93+ let kpic = it . vod_pic || '图片' ;
94+ let kremarks = `${ it . vod_remarks || '状态' } |${ it . vod_douban_score || '无评分' } ` ;
95+ let kyear = extend ?. year || '' ;
96+ let kid = it . url ?? 'Id' ;
97+ VODS . push ( {
98+ vod_name : kname ,
99+ vod_pic : kpic ,
100+ vod_remarks : kremarks ,
101+ vod_year : kyear ,
102+ vod_id : `${ kid } @${ kname } @${ kpic } @${ kremarks } `
103+ } ) ;
104+ }
105+ let { pagecount= 1000 , limit= 30 , total= 30000 } = resObj ;
106+ return JSON . stringify ( { list : VODS , page : pg , pagecount : pagecount , limit : 30 , total : total } ) ;
107+ } catch ( e ) {
108+ console . error ( '类别页获取失败:' , e . message ) ;
109+ return JSON . stringify ( { list : [ ] , page : 1 , pagecount : 0 , limit : 30 , total : 0 } ) ;
110+ }
111+ }
112+
113+ async function search ( wd , quick , pg ) {
114+ try {
115+ pg = parseInt ( pg , 10 ) ;
116+ pg = pg > 0 ? pg : 1 ;
117+ let searchUrl = `${ HOST } /search/${ wd } ----------${ pg } ---.html` ;
118+ let resHtml = await request ( searchUrl ) ;
119+ let VODS = getVodList ( resHtml ) ;
120+ return JSON . stringify ( { list : VODS , page : pg , pagecount : 10 , limit : 30 , total : 300 } ) ;
121+ } catch ( e ) {
122+ console . error ( '搜索页获取失败:' , e . message ) ;
123+ return JSON . stringify ( { list : [ ] , page : 1 , pagecount : 0 , limit : 30 , total : 0 } ) ;
124+ }
125+ }
126+
127+ function getVodList ( khtml , rec = false ) {
128+ try {
129+ if ( ! khtml ) { throw new Error ( '源码为空' ) ; }
130+ let kvods = [ ] ;
131+ let selector = rec ? '.public-list-box' : '.search-list' ;
132+ let listArr = pdfa ( khtml , selector ) ;
133+ for ( let it of listArr ) {
134+ let kname = cutStr ( it , 'alt="' , '"' , '名称' ) ;
135+ let kpic = cutStr ( it , 'data-src="' , '"' , '图片' ) ;
136+ let kremarks = rec ? `${ cutStr ( it , 'public-prt£>' , '<' , '类型' ) } |${ cutStr ( it , 'ft2">' , '<' , '状态' ) } ` : cutStr ( it , 'this-wap">' , '</div>' , '状态' ) ;
137+ let kid = cutStr ( it , 'href="' , '"' , 'Id' ) ;
138+ kvods . push ( {
139+ vod_name : kname ,
140+ vod_pic : kpic ,
141+ vod_remarks : kremarks ,
142+ vod_id : `${ kid } @${ kname } @${ kpic } @${ kremarks } `
143+ } ) ;
144+ }
145+ return kvods ;
146+ } catch ( e ) {
147+ console . error ( `生成视频列表失败:` , e . message ) ;
148+ return [ ] ;
149+ }
150+ }
151+
152+ async function detail ( ids ) {
153+ try {
154+ let [ id , kname , kpic , kremarks ] = ids . split ( '@' ) ;
155+ let detailUrl = ! / ^ h t t p / . test ( id ) ? `${ HOST } ${ id } ` : id ;
156+ let resHtml = await request ( detailUrl ) ;
157+ if ( ! resHtml ) { throw new Error ( '源码为空' ) ; }
158+ let intros = cutStr ( resHtml , 'search-show' , '</ul>' , '' , false ) ;
159+ let ktabs = pdfa ( resHtml , '.anthology-tab&&a' ) . map ( ( it , idx ) => cutStr ( it , '</i>' , '<' , `线路${ idx + 1 } ` ) ) ;
160+ let kurls = pdfa ( resHtml , '.anthology-list-play' ) . map ( item => {
161+ return pdfa ( item , 'a' ) . map ( it => { return `${ cutStr ( it , '>' , '<' , 'noEpi' ) } $${ cutStr ( it , 'href="' , '"' , 'noUrl' ) } ` } ) . join ( '#' ) ;
162+ } ) ;
163+ let VOD = {
164+ vod_id : detailUrl ,
165+ vod_name : kname ,
166+ vod_pic : kpic ,
167+ type_name : cutStr ( intros , '类型:' , '</li>' , '类型' ) ,
168+ vod_remarks : `${ cutStr ( intros , '状态:' , '</li>' , '状态' ) } |${ cutStr ( intros , '更新:' , '</li>' , '更新' ) } ` ,
169+ vod_year : cutStr ( intros , '年份:' , '</li>' , '1000' ) ,
170+ vod_area : cutStr ( intros , '地区:' , '</li>' , '地区' ) ,
171+ vod_lang : cutStr ( intros , '语言:' , '</li>' , '语言' ) ,
172+ vod_director : cutStr ( intros , '导演:' , '</li>' , '' ) . replace ( / , $ / , '' ) || '导演' ,
173+ vod_actor : cutStr ( intros , '主演:' , '</li>' , '' ) . replace ( / , $ / , '' ) || '主演' ,
174+ vod_content : cutStr ( intros , '简介:' , '</li>' , '' ) || kname ,
175+ vod_play_from : ktabs . join ( '$$$' ) ,
176+ vod_play_url : kurls . join ( '$$$' )
177+ } ;
178+ return JSON . stringify ( { list : [ VOD ] } ) ;
179+ } catch ( e ) {
180+ console . error ( '详情页获取失败:' , e . message ) ;
181+ return JSON . stringify ( { list : [ ] } ) ;
182+ }
183+ }
184+
185+ async function play ( flag , ids , flags ) {
186+ try {
187+ let playUrl = ! / ^ h t t p / . test ( ids ) ? `${ HOST } ${ ids } ` : ids ;
188+ let kp = 0 , kurl = '' ;
189+ let resHtml = await request ( playUrl ) ;
190+ let codeObj = safeParseJSON ( cutStr ( resHtml , 'var player_£=' , '<' , '' , false ) ) ;
191+ let jurl = codeObj ?. url ?? '' ;
192+ jurl = safeUrlDecode ( safeB64Decode ( jurl ) ) ;
193+ if ( jurl ) {
194+ jurl = `${ HOST } /player/?url=${ jurl } &next=` ;
195+ resHtml = await request ( jurl ) ;
196+ let encryptedUrl = cutStr ( resHtml , 'const encryptedUrl = "' , '"' , '' ) ;
197+ let sessionKey = cutStr ( resHtml , 'const sessionKey = "' , '"' , '' ) ;
198+ kurl = urlAesDecrypt ( encryptedUrl , sessionKey ) ;
199+ }
200+ if ( ! / ^ h t t p / . test ( kurl ) ) {
201+ kurl = playUrl ;
202+ kp = 1 ;
203+ }
204+ return JSON . stringify ( { jx : 0 , parse : kp , url : kurl , header : DefHeader } ) ;
205+ } catch ( e ) {
206+ console . error ( '播放失败:' , e . message ) ;
207+ return JSON . stringify ( { jx : 0 , parse : 0 , url : '' , header : { } } ) ;
208+ }
209+ }
210+
211+ function urlAesDecrypt ( ciphertext , key ) {
212+ try {
213+ const rawData = Crypto . enc . Base64 . parse ( ciphertext ) ;
214+ const keyWordArr = Crypto . enc . Utf8 . parse ( key ) ;
215+ const ivWordArr = Crypto . lib . WordArray . create ( rawData . words . slice ( 0 , 4 ) ) ;
216+ const encrypted = Crypto . lib . WordArray . create ( rawData . words . slice ( 4 ) ) ;
217+ const decrypted = Crypto . AES . decrypt ( { ciphertext : encrypted } , keyWordArr ,
218+ {
219+ iv : ivWordArr ,
220+ mode : Crypto . mode . CBC ,
221+ padding : Crypto . pad . Pkcs7
222+ }
223+ ) ;
224+ return decrypted . toString ( Crypto . enc . Utf8 ) ;
225+ } catch ( e ) {
226+ return '' ;
227+ }
228+ }
229+
230+ function safeB64Decode ( b64Str ) {
231+ try { return Crypto . enc . Utf8 . stringify ( Crypto . enc . Base64 . parse ( b64Str ) ) ; } catch ( e ) { return '' ; }
232+ }
233+
234+ function safeUrlDecode ( urlStr ) {
235+ try { return decodeURIComponent ( urlStr ) ; } catch ( e ) { return '' ; }
236+ }
237+
238+ function safeParseJSON ( jStr ) {
239+ try { return JSON . parse ( jStr ) ; } catch ( e ) { return null ; }
240+ }
241+
242+ function cutStr ( str , prefix = '' , suffix = '' , defaultVal = 'cutFaile' , clean = true , i = 1 , all = false ) {
243+ try {
244+ if ( typeof str !== 'string' || ! str ) { throw new Error ( '被截取对象需为非空字符串' ) ; }
245+ const cleanStr = cs => String ( cs ) . replace ( / < [ ^ > ] * ?> / g, ' ' ) . replace ( / ( & n b s p ; | \u00A0 | \s ) + / g, ' ' ) . trim ( ) . replace ( / \s + / g, ' ' ) ;
246+ const esc = s => String ( s ) . replace ( / [ . * + ? $ { } ( ) | [ \] \\ / ^ ] / g, '\\$&' ) ;
247+ let pre = esc ( prefix ) . replace ( / £ / g, '[^]*?' ) , end = esc ( suffix ) ;
248+ let regex = new RegExp ( `${ pre ? pre : '^' } ([^]*?)${ end ? end : '$' } ` , 'g' ) ;
249+ let matchIterator = str . matchAll ( regex ) ;
250+ if ( all ) {
251+ let matchArr = [ ...matchIterator ] ;
252+ return matchArr . length ? matchArr . map ( it => {
253+ const val = it [ 1 ] ?? defaultVal ;
254+ return clean && val !== defaultVal ? cleanStr ( val ) : val ;
255+ } ) : [ defaultVal ] ;
256+ }
257+ i = parseInt ( i , 10 ) ;
258+ if ( isNaN ( i ) || i < 1 ) { throw new Error ( '序号必须为正整数' ) ; }
259+ let tgIdx = i - 1 , matchIdx = 0 ;
260+ for ( const match of matchIterator ) {
261+ if ( matchIdx ++ === tgIdx ) {
262+ const result = match [ 1 ] ?? defaultVal ;
263+ return clean && result !== defaultVal ? cleanStr ( result ) : result ;
264+ }
265+ }
266+ return defaultVal ;
267+ } catch ( e ) {
268+ console . error ( `字符串截取失败:` , e . message ) ;
269+ return all ? [ 'cutErr' ] : 'cutErr' ;
270+ }
271+ }
272+
273+ async function request ( reqUrl , options = { } ) {
274+ try {
275+ if ( typeof reqUrl !== 'string' || ! reqUrl . trim ( ) ) { throw new Error ( 'reqUrl需为字符串且非空' ) ; }
276+ if ( typeof options !== 'object' || Array . isArray ( options ) || options === null ) { throw new Error ( 'options类型需为非null对象' ) ; }
277+ options . method = options . method ?. toUpperCase ( ) || 'GET' ;
278+ if ( [ 'GET' , 'HEAD' ] . includes ( options . method ) ) {
279+ delete options . body ;
280+ delete options . data ;
281+ delete options . postType ;
282+ }
283+ let { headers, timeout, buffer, ...restOpts } = options ;
284+ const optObj = {
285+ headers : ( typeof headers === 'object' && ! Array . isArray ( headers ) && headers ) ? headers : KParams . headers ,
286+ timeout : parseInt ( timeout , 10 ) > 0 ? parseInt ( timeout , 10 ) : KParams . timeout ,
287+ buffer : buffer ?? 0 ,
288+ ...restOpts
289+ } ;
290+ const res = await req ( reqUrl , optObj ) ;
291+ if ( options . withHeaders ) {
292+ const resHeaders = typeof res . headers === 'object' && ! Array . isArray ( res . headers ) && res . headers ? res . headers : { } ;
293+ const resWithHeaders = { ...resHeaders , body : res ?. content ?? '' } ;
294+ return JSON . stringify ( resWithHeaders ) ;
295+ }
296+ return res ?. content ?? '' ;
297+ } catch ( e ) {
298+ console . error ( `${ reqUrl } →请求失败:` , e . message ) ;
299+ return options ?. withHeaders ? JSON . stringify ( { body : '' } ) : '' ;
300+ }
301+ }
302+
303+ export function __jsEvalReturn ( ) {
304+ return {
305+ init,
306+ home,
307+ homeVod,
308+ category,
309+ search,
310+ detail,
311+ play,
312+ proxy : null
313+ } ;
314+ }
0 commit comments