GithubHelp home page GithubHelp logo

Comments (11)

zzz6839 avatar zzz6839 commented on June 2, 2024 1

感谢你的耐心回复,你的脚本已经有效的帮我解决了问题,本问题将关闭,期待你放出正式版和增加首页关于搜索指令的简要说明。
sticker

from dandanplay-resource-service.

LussacZheng avatar LussacZheng commented on June 2, 2024

你使用的是 golang 还是 cf-worker 版本?

目前 golang 版本和 cf-worker 版本都没有缓存机制,二者都是在监听到 弹弹play 客户端发起的 API 请求后,实时访问 dmhy 的相关搜索页面,从 HTML 中解析提取信息并返回。

按道理 "API 返回的信息" 应该和 "在浏览器中的搜索结果" 是一致的。


按照你的描述进行测试,当前 dmhy 主页的第一项为 "恶魔恋歌" ,为 2021-08-14_17:00 发布,

主页

而在 2021-08-14_17:28 时进行的 搜索结果 中并未出现。

搜索页

说明此时 dmhy 可能尚未将此条资源添加到索引中,即 "延迟" 可能是由 dmhy 本身引起的。

from dandanplay-resource-service.

zzz6839 avatar zzz6839 commented on June 2, 2024

了解了,3Q

from dandanplay-resource-service.

LussacZheng avatar LussacZheng commented on June 2, 2024

我用 cf-worker 版本简单实现了一个 "搜索指令/选项" 的功能:
在原来搜索的关键词后添加 $realtime ,即可要求 API 额外访问一次 首页 ,从首页中根据关键词,找到由于 "延迟" 而未出现在搜索页中的资源,再一并返回给客户端。

如果你感兴趣的话,可以帮我测试一下。

具体效果如下:

screen-recode

worker.js 代码如下(代码块的右上角有复制按钮):

// https://github.com/LussacZheng/dandanplay-resource-service
// version: 0.0.4-alpha
// build: 2021-08-15 01:31 GMT+8
// wrangler: 1.19.0

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e){e.exports=JSON.parse('{"b":"0.0.4-alpha","a":"https://github.com/LussacZheng/dandanplay-resource-service"}')},function(e,t,n){"use strict";n.r(t);class r{constructor(e){const{keyword:t,options:n}=function(e){let t={};return{keyword:e.replace(/ ?\$(\w+)(:(\d+))?/gi,(e,n,r,o)=>(t[n]=parseInt(o)||1,"")),options:t}}(e);this.keyword=t,this.realtime=n.realtime||0}}function o(e,t){return e.replace(/\$\{(\w+)\}/gi,(e,n)=>t[n])}function a(e){const t=new Date(e).toLocaleString("default",{formatMatcher:"best fit",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"});return new Date(t+" UTC").toISOString().substr(0,19).replace("T"," ")}function s(e,t){if(0===t.length)return e[1];let n={};return t.length>e.length-1&&t.splice(e.length-1),t.forEach((t,r)=>{n[t]=e[r+1]}),n}var i=function(e,t,n,r="first"){switch(r){case"all":return function(e,t,n){const r=e.matchAll(t),o=Array.from(r,e=>s(e,n));return 0===o.length?null:o}(e,t,n);case"last":return function(e,t,n){const r=[...e.matchAll(t)],o=r[r.length-1];return 0===r.length?null:s(o,n)}(e,t,n);default:return function(e,t,n){let r=t.exec(e);return t.lastIndex=0,null===r?null:s(r,n)}(e,t,n)}};const u={headers:{"content-type":"application/json;charset=utf-8"}},l={headers:{"content-type":"text/html;charset=utf-8"}},c={headers:{accept:"text/html;charset=utf-8","user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}};async function d(e,t=c){let n;try{n=await fetch(decodeURI(e),t)}catch(e){console.error(e)}return await async function(e){const{headers:t}=e,n=t.get("content-type");return n.includes("application/json")?await e.json():(n.includes("application/text")||n.includes("text/html"),await e.text())}(n)}const p="https://share.dmhy.org",h={type_and_subgroup_url:p+"/topics/advanced-search?team_id=0&sort_id=0&orderby=",list_url:p+"/topics/list/page/1?keyword=${keyword}&sort_id=${type}&team_id=${subgroup}&order=date-desc",index_url:p+"/topics/list/page/${realtime}"},g="未能成功解析标题",f=-2,y="未能成功解析类别",m=-1,w="未知字幕组",b="magnet_not_found_未能成功解析磁力链接或磁力链接不存在",_="未能成功解析资源发布页面",I="未能成功解析资源大小",v="1970-01-01 08:00:00",R=/<option value="(\d+)">(.+?)<\/option>/gim,S=/<option value="(\d+)" style="color: [\w#]+">(.+?)<\/option>/gim,x={HasMore:/下一頁/g,Resources:/<tr class="">(.*?)<\/tr>/gis,TypeId:/href="\/topics\/list\/sort_id\/(\d+)"/gim,TypeName:/<font color=[\w#]+>(.+)<\/font>/gim,SubgroupId:/href="\/topics\/list\/team_id\/(\d+)"/gim,SubgroupName:/\s+(.*)<\/a><\/span>/gim,Magnet:/href="(magnet:\?xt=urn:btih:.+?)"/gim,PageUrl:/href="(.+?)"\s*target="_blank"/gim,FileSize:/<td.*>([\w\.]+B)<\/td>/gim,PublishDate:/<span style="display: none;">([\d\/ :]+)<\/span>/gim,Title:/target="_blank" ?>(.+?)<\/a>/gis,TitleReplacer:/<span class="keyword">(.*?)<\/span>/gi};async function T(e){const t=new URL(encodeURI(e.url)).searchParams,n=t.get("type")||0,a=t.get("subgroup")||0,{keyword:s,realtime:u}=new r(decodeURIComponent(t.get("keyword"))),l=encodeURI(o(h.list_url,{keyword:s,type:n<0?0:n,subgroup:a<0?0:a}));let c=await d(l),p=function(e){let t={HasMore:null!==i(e,x.HasMore,[]),Resources:[]};const n=i(e,x.Resources,[],"all");return null===n||n.forEach(e=>{t.Resources.push(O(e))}),t}(c);if(u){const e=encodeURI(o(h.index_url,{realtime:u}));c=await d(e);const t=U(c,s,p.Resources);p.Resources=t.concat(p.Resources)}return p}function N(e){const t=e.replace(/&amp;/gi,"&");let n=i(t,R,["Id","Name"],"all");return null===n?[]:(n.forEach(e=>e.Id=parseInt(e.Id)),n.shift(),n)}function P(e){let t=i(e,S,["Id","Name"],"all");return null===t?[]:(t.forEach(e=>e.Id=parseInt(e.Id)),t.unshift({Id:0,Name:"全部"}),t)}function U(e,t,n){let r=[];const o=i(e,x.Resources,[],"all");return null===o?result:(o.forEach(e=>{let o=O(e);const a=t.split(" ").every(e=>o.Title.includes(e)),s=n.some(e=>o.PageUrl===e.PageUrl);a&&!s&&r.push(o)}),r)}function O(e){const t=i(e,x.Title,[]),n=i(e,x.TypeId,[]),r=i(e,x.TypeName,[]),o=i(e,x.SubgroupId,[]),s=i(e,x.SubgroupName,[]),u=i(e,x.Magnet,[]),l=i(e,x.PageUrl,[]),c=i(e,x.FileSize,[]),d=i(e,x.PublishDate,[]);return{Title:null===t?g:t.trim().replace(x.TitleReplacer,"$1"),TypeId:parseInt(n)||f,TypeName:r||y,SubgroupId:parseInt(o)||m,SubgroupName:s||w,Magnet:u||b,PageUrl:null===l?_:p+l,FileSize:c||I,PublishDate:null===d?v:a(d)}}var k=n(0);const M=`\n<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n  <meta charset="UTF-8" />\n  <meta name="viewport" content="width=device-width,initial-scale=1" />\n  <title>弹弹play资源搜索节点API - v${k.b}</title>\n</head>\n<body>\n  <h1>使用说明</h1>\n  <h2>GitHub - <a href="${k.a}">LussacZheng/dandanplay-resource-service</a></h2>\n</body>\n</html>\n`;const j=e=>t=>t.method.toLowerCase()===e.toLowerCase(),L=j("connect"),$=j("delete"),E=j("get"),C=j("head"),A=j("options"),D=j("patch"),z=j("post"),F=j("put"),H=j("trace"),J=e=>t=>{const n=new URL(encodeURI(t.url)).pathname;return(n.match(e)||[])[0]===n};var W=class{constructor(){this.routes=[]}handle(e,t){return this.routes.push({conditions:e,handler:t}),this}connect(e,t){return this.handle([L,J(e)],t)}delete(e,t){return this.handle([$,J(e)],t)}get(e,t){return this.handle([E,J(e)],t)}head(e,t){return this.handle([C,J(e)],t)}options(e,t){return this.handle([A,J(e)],t)}patch(e,t){return this.handle([D,J(e)],t)}post(e,t){return this.handle([z,J(e)],t)}put(e,t){return this.handle([F,J(e)],t)}trace(e,t){return this.handle([H,J(e)],t)}all(e){return this.handle([],e)}route(e){const t=this.resolve(e);return t?t.handler(e):new Response("resource not found",{status:404,statusText:"not found",headers:{"content-type":"text/plain"}})}resolve(e){return this.routes.find(t=>!(t.conditions&&(!Array.isArray(t)||t.conditions.length))||("function"==typeof t.conditions?t.conditions(e):t.conditions.every(t=>t(e))))}};async function Z(e){const t=new W;t.get("/subgroup",async()=>{const e=await async function(){return{Subgroups:N(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/type",async()=>{const e=await async function(){return{Types:P(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/list",async e=>{const t=await T(e);return new Response(JSON.stringify(t),u)}),t.get("/",async()=>new Response(await async function(){let e;try{e=await d("https://cdn.jsdelivr.net/gh/LussacZheng/dandanplay-resource-service@dist/web/index.html"),e=o(e,{VERSION:k.b})}catch(t){e=M}return e}(),l));return await t.route(e)}addEventListener("fetch",e=>e.respondWith(Z(e.request)))}]);

目前的缺点如下:

  • 使用 $realtime 时,只允许空格这一种符号。不能与带 & | ! ( ) 的高级搜索同时使用。
  • $realtime 不支持 "延迟" 资源的简繁体转换。即关键词为简中时,同名的繁中 "延迟" 资源不会出现在结果中。

此外,$realtime 还支持指定页码。如 $realtime:2 表示从 第二页 中寻找 "延迟" 资源,而非 首页
当然这个可能没什么用,这么设计主要时为了以后可以添加更多的 "搜索指令/选项" ,如:

  • $page:n : 获取搜索结果的第 n 页,而不是总是返回第一页
  • $limit:n : 限制搜索结果的数量上限为 n ,而不是总为 80
  • ......

from dandanplay-resource-service.

zzz6839 avatar zzz6839 commented on June 2, 2024

我用 cf-worker 版本简单实现了一个 "搜索指令/选项" 的功能:
在原来搜索的关键词后添加 $realtime ,即可要求 API 额外访问一次 首页 ,从首页中根据关键词,找到由于 "延迟" 而未出现在搜索页中的资源,再一并返回给客户端。

如果你感兴趣的话,可以帮我测试一下。

具体效果如下:

screen-recode

worker.js 代码如下(代码块的右上角有复制按钮):

// https://github.com/LussacZheng/dandanplay-resource-service
// version: 0.0.4-alpha
// build: 2021-08-15 01:31 GMT+8
// wrangler: 1.19.0

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e){e.exports=JSON.parse('{"b":"0.0.4-alpha","a":"https://github.com/LussacZheng/dandanplay-resource-service"}')},function(e,t,n){"use strict";n.r(t);class r{constructor(e){const{keyword:t,options:n}=function(e){let t={};return{keyword:e.replace(/ ?\$(\w+)(:(\d+))?/gi,(e,n,r,o)=>(t[n]=parseInt(o)||1,"")),options:t}}(e);this.keyword=t,this.realtime=n.realtime||0}}function o(e,t){return e.replace(/\$\{(\w+)\}/gi,(e,n)=>t[n])}function a(e){const t=new Date(e).toLocaleString("default",{formatMatcher:"best fit",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"});return new Date(t+" UTC").toISOString().substr(0,19).replace("T"," ")}function s(e,t){if(0===t.length)return e[1];let n={};return t.length>e.length-1&&t.splice(e.length-1),t.forEach((t,r)=>{n[t]=e[r+1]}),n}var i=function(e,t,n,r="first"){switch(r){case"all":return function(e,t,n){const r=e.matchAll(t),o=Array.from(r,e=>s(e,n));return 0===o.length?null:o}(e,t,n);case"last":return function(e,t,n){const r=[...e.matchAll(t)],o=r[r.length-1];return 0===r.length?null:s(o,n)}(e,t,n);default:return function(e,t,n){let r=t.exec(e);return t.lastIndex=0,null===r?null:s(r,n)}(e,t,n)}};const u={headers:{"content-type":"application/json;charset=utf-8"}},l={headers:{"content-type":"text/html;charset=utf-8"}},c={headers:{accept:"text/html;charset=utf-8","user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}};async function d(e,t=c){let n;try{n=await fetch(decodeURI(e),t)}catch(e){console.error(e)}return await async function(e){const{headers:t}=e,n=t.get("content-type");return n.includes("application/json")?await e.json():(n.includes("application/text")||n.includes("text/html"),await e.text())}(n)}const p="https://share.dmhy.org",h={type_and_subgroup_url:p+"/topics/advanced-search?team_id=0&sort_id=0&orderby=",list_url:p+"/topics/list/page/1?keyword=${keyword}&sort_id=${type}&team_id=${subgroup}&order=date-desc",index_url:p+"/topics/list/page/${realtime}"},g="未能成功解析标题",f=-2,y="未能成功解析类别",m=-1,w="未知字幕组",b="magnet_not_found_未能成功解析磁力链接或磁力链接不存在",_="未能成功解析资源发布页面",I="未能成功解析资源大小",v="1970-01-01 08:00:00",R=/<option value="(\d+)">(.+?)<\/option>/gim,S=/<option value="(\d+)" style="color: [\w#]+">(.+?)<\/option>/gim,x={HasMore:/下一頁/g,Resources:/<tr class="">(.*?)<\/tr>/gis,TypeId:/href="\/topics\/list\/sort_id\/(\d+)"/gim,TypeName:/<font color=[\w#]+>(.+)<\/font>/gim,SubgroupId:/href="\/topics\/list\/team_id\/(\d+)"/gim,SubgroupName:/\s+(.*)<\/a><\/span>/gim,Magnet:/href="(magnet:\?xt=urn:btih:.+?)"/gim,PageUrl:/href="(.+?)"\s*target="_blank"/gim,FileSize:/<td.*>([\w\.]+B)<\/td>/gim,PublishDate:/<span style="display: none;">([\d\/ :]+)<\/span>/gim,Title:/target="_blank" ?>(.+?)<\/a>/gis,TitleReplacer:/<span class="keyword">(.*?)<\/span>/gi};async function T(e){const t=new URL(encodeURI(e.url)).searchParams,n=t.get("type")||0,a=t.get("subgroup")||0,{keyword:s,realtime:u}=new r(decodeURIComponent(t.get("keyword"))),l=encodeURI(o(h.list_url,{keyword:s,type:n<0?0:n,subgroup:a<0?0:a}));let c=await d(l),p=function(e){let t={HasMore:null!==i(e,x.HasMore,[]),Resources:[]};const n=i(e,x.Resources,[],"all");return null===n||n.forEach(e=>{t.Resources.push(O(e))}),t}(c);if(u){const e=encodeURI(o(h.index_url,{realtime:u}));c=await d(e);const t=U(c,s,p.Resources);p.Resources=t.concat(p.Resources)}return p}function N(e){const t=e.replace(/&amp;/gi,"&");let n=i(t,R,["Id","Name"],"all");return null===n?[]:(n.forEach(e=>e.Id=parseInt(e.Id)),n.shift(),n)}function P(e){let t=i(e,S,["Id","Name"],"all");return null===t?[]:(t.forEach(e=>e.Id=parseInt(e.Id)),t.unshift({Id:0,Name:"全部"}),t)}function U(e,t,n){let r=[];const o=i(e,x.Resources,[],"all");return null===o?result:(o.forEach(e=>{let o=O(e);const a=t.split(" ").every(e=>o.Title.includes(e)),s=n.some(e=>o.PageUrl===e.PageUrl);a&&!s&&r.push(o)}),r)}function O(e){const t=i(e,x.Title,[]),n=i(e,x.TypeId,[]),r=i(e,x.TypeName,[]),o=i(e,x.SubgroupId,[]),s=i(e,x.SubgroupName,[]),u=i(e,x.Magnet,[]),l=i(e,x.PageUrl,[]),c=i(e,x.FileSize,[]),d=i(e,x.PublishDate,[]);return{Title:null===t?g:t.trim().replace(x.TitleReplacer,"$1"),TypeId:parseInt(n)||f,TypeName:r||y,SubgroupId:parseInt(o)||m,SubgroupName:s||w,Magnet:u||b,PageUrl:null===l?_:p+l,FileSize:c||I,PublishDate:null===d?v:a(d)}}var k=n(0);const M=`\n<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n  <meta charset="UTF-8" />\n  <meta name="viewport" content="width=device-width,initial-scale=1" />\n  <title>弹弹play资源搜索节点API - v${k.b}</title>\n</head>\n<body>\n  <h1>使用说明</h1>\n  <h2>GitHub - <a href="${k.a}">LussacZheng/dandanplay-resource-service</a></h2>\n</body>\n</html>\n`;const j=e=>t=>t.method.toLowerCase()===e.toLowerCase(),L=j("connect"),$=j("delete"),E=j("get"),C=j("head"),A=j("options"),D=j("patch"),z=j("post"),F=j("put"),H=j("trace"),J=e=>t=>{const n=new URL(encodeURI(t.url)).pathname;return(n.match(e)||[])[0]===n};var W=class{constructor(){this.routes=[]}handle(e,t){return this.routes.push({conditions:e,handler:t}),this}connect(e,t){return this.handle([L,J(e)],t)}delete(e,t){return this.handle([$,J(e)],t)}get(e,t){return this.handle([E,J(e)],t)}head(e,t){return this.handle([C,J(e)],t)}options(e,t){return this.handle([A,J(e)],t)}patch(e,t){return this.handle([D,J(e)],t)}post(e,t){return this.handle([z,J(e)],t)}put(e,t){return this.handle([F,J(e)],t)}trace(e,t){return this.handle([H,J(e)],t)}all(e){return this.handle([],e)}route(e){const t=this.resolve(e);return t?t.handler(e):new Response("resource not found",{status:404,statusText:"not found",headers:{"content-type":"text/plain"}})}resolve(e){return this.routes.find(t=>!(t.conditions&&(!Array.isArray(t)||t.conditions.length))||("function"==typeof t.conditions?t.conditions(e):t.conditions.every(t=>t(e))))}};async function Z(e){const t=new W;t.get("/subgroup",async()=>{const e=await async function(){return{Subgroups:N(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/type",async()=>{const e=await async function(){return{Types:P(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/list",async e=>{const t=await T(e);return new Response(JSON.stringify(t),u)}),t.get("/",async()=>new Response(await async function(){let e;try{e=await d("https://cdn.jsdelivr.net/gh/LussacZheng/dandanplay-resource-service@dist/web/index.html"),e=o(e,{VERSION:k.b})}catch(t){e=M}return e}(),l));return await t.route(e)}addEventListener("fetch",e=>e.respondWith(Z(e.request)))}]);

目前的缺点如下:

  • 使用 $realtime 时,只允许空格这一种符号。不能与带 & | ! ( ) 的高级搜索同时使用。
  • $realtime 不支持 "延迟" 资源的简繁体转换。即关键词为简中时,同名的繁中 "延迟" 资源不会出现在结果中。

此外,$realtime 还支持指定页码。如 $realtime:2 表示从 第二页 中寻找 "延迟" 资源,而非 首页
当然这个可能没什么用,这么设计主要时为了以后可以添加更多的 "搜索指令/选项" ,如:

  • $page:n : 获取搜索结果的第 n 页,而不是总是返回第一页
  • $limit:n : 限制搜索结果的数量上限为 n ,而不是总为 80
  • ......

感谢你的回复,我测试过新的脚本可以实现实时搜索的功能,并且可以配合自动下载关键词功能实现资源的实时自动下载,并且如你所反映的,无法实现简繁体的转换,不过对于我来说已经很实用了,谢谢(我用的是cf-worker)。
1

from dandanplay-resource-service.

zzz6839 avatar zzz6839 commented on June 2, 2024

我用 cf-worker 版本简单实现了一个 "搜索指令/选项" 的功能:
在原来搜索的关键词后添加 $realtime ,即可要求 API 额外访问一次 首页 ,从首页中根据关键词,找到由于 "延迟" 而未出现在搜索页中的资源,再一并返回给客户端。
如果你感兴趣的话,可以帮我测试一下。
具体效果如下:
screen-recode
worker.js 代码如下(代码块的右上角有复制按钮):

// https://github.com/LussacZheng/dandanplay-resource-service
// version: 0.0.4-alpha
// build: 2021-08-15 01:31 GMT+8
// wrangler: 1.19.0

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e){e.exports=JSON.parse('{"b":"0.0.4-alpha","a":"https://github.com/LussacZheng/dandanplay-resource-service"}')},function(e,t,n){"use strict";n.r(t);class r{constructor(e){const{keyword:t,options:n}=function(e){let t={};return{keyword:e.replace(/ ?\$(\w+)(:(\d+))?/gi,(e,n,r,o)=>(t[n]=parseInt(o)||1,"")),options:t}}(e);this.keyword=t,this.realtime=n.realtime||0}}function o(e,t){return e.replace(/\$\{(\w+)\}/gi,(e,n)=>t[n])}function a(e){const t=new Date(e).toLocaleString("default",{formatMatcher:"best fit",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"});return new Date(t+" UTC").toISOString().substr(0,19).replace("T"," ")}function s(e,t){if(0===t.length)return e[1];let n={};return t.length>e.length-1&&t.splice(e.length-1),t.forEach((t,r)=>{n[t]=e[r+1]}),n}var i=function(e,t,n,r="first"){switch(r){case"all":return function(e,t,n){const r=e.matchAll(t),o=Array.from(r,e=>s(e,n));return 0===o.length?null:o}(e,t,n);case"last":return function(e,t,n){const r=[...e.matchAll(t)],o=r[r.length-1];return 0===r.length?null:s(o,n)}(e,t,n);default:return function(e,t,n){let r=t.exec(e);return t.lastIndex=0,null===r?null:s(r,n)}(e,t,n)}};const u={headers:{"content-type":"application/json;charset=utf-8"}},l={headers:{"content-type":"text/html;charset=utf-8"}},c={headers:{accept:"text/html;charset=utf-8","user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}};async function d(e,t=c){let n;try{n=await fetch(decodeURI(e),t)}catch(e){console.error(e)}return await async function(e){const{headers:t}=e,n=t.get("content-type");return n.includes("application/json")?await e.json():(n.includes("application/text")||n.includes("text/html"),await e.text())}(n)}const p="https://share.dmhy.org",h={type_and_subgroup_url:p+"/topics/advanced-search?team_id=0&sort_id=0&orderby=",list_url:p+"/topics/list/page/1?keyword=${keyword}&sort_id=${type}&team_id=${subgroup}&order=date-desc",index_url:p+"/topics/list/page/${realtime}"},g="未能成功解析标题",f=-2,y="未能成功解析类别",m=-1,w="未知字幕组",b="magnet_not_found_未能成功解析磁力链接或磁力链接不存在",_="未能成功解析资源发布页面",I="未能成功解析资源大小",v="1970-01-01 08:00:00",R=/<option value="(\d+)">(.+?)<\/option>/gim,S=/<option value="(\d+)" style="color: [\w#]+">(.+?)<\/option>/gim,x={HasMore:/下一頁/g,Resources:/<tr class="">(.*?)<\/tr>/gis,TypeId:/href="\/topics\/list\/sort_id\/(\d+)"/gim,TypeName:/<font color=[\w#]+>(.+)<\/font>/gim,SubgroupId:/href="\/topics\/list\/team_id\/(\d+)"/gim,SubgroupName:/\s+(.*)<\/a><\/span>/gim,Magnet:/href="(magnet:\?xt=urn:btih:.+?)"/gim,PageUrl:/href="(.+?)"\s*target="_blank"/gim,FileSize:/<td.*>([\w\.]+B)<\/td>/gim,PublishDate:/<span style="display: none;">([\d\/ :]+)<\/span>/gim,Title:/target="_blank" ?>(.+?)<\/a>/gis,TitleReplacer:/<span class="keyword">(.*?)<\/span>/gi};async function T(e){const t=new URL(encodeURI(e.url)).searchParams,n=t.get("type")||0,a=t.get("subgroup")||0,{keyword:s,realtime:u}=new r(decodeURIComponent(t.get("keyword"))),l=encodeURI(o(h.list_url,{keyword:s,type:n<0?0:n,subgroup:a<0?0:a}));let c=await d(l),p=function(e){let t={HasMore:null!==i(e,x.HasMore,[]),Resources:[]};const n=i(e,x.Resources,[],"all");return null===n||n.forEach(e=>{t.Resources.push(O(e))}),t}(c);if(u){const e=encodeURI(o(h.index_url,{realtime:u}));c=await d(e);const t=U(c,s,p.Resources);p.Resources=t.concat(p.Resources)}return p}function N(e){const t=e.replace(/&amp;/gi,"&");let n=i(t,R,["Id","Name"],"all");return null===n?[]:(n.forEach(e=>e.Id=parseInt(e.Id)),n.shift(),n)}function P(e){let t=i(e,S,["Id","Name"],"all");return null===t?[]:(t.forEach(e=>e.Id=parseInt(e.Id)),t.unshift({Id:0,Name:"全部"}),t)}function U(e,t,n){let r=[];const o=i(e,x.Resources,[],"all");return null===o?result:(o.forEach(e=>{let o=O(e);const a=t.split(" ").every(e=>o.Title.includes(e)),s=n.some(e=>o.PageUrl===e.PageUrl);a&&!s&&r.push(o)}),r)}function O(e){const t=i(e,x.Title,[]),n=i(e,x.TypeId,[]),r=i(e,x.TypeName,[]),o=i(e,x.SubgroupId,[]),s=i(e,x.SubgroupName,[]),u=i(e,x.Magnet,[]),l=i(e,x.PageUrl,[]),c=i(e,x.FileSize,[]),d=i(e,x.PublishDate,[]);return{Title:null===t?g:t.trim().replace(x.TitleReplacer,"$1"),TypeId:parseInt(n)||f,TypeName:r||y,SubgroupId:parseInt(o)||m,SubgroupName:s||w,Magnet:u||b,PageUrl:null===l?_:p+l,FileSize:c||I,PublishDate:null===d?v:a(d)}}var k=n(0);const M=`\n<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n  <meta charset="UTF-8" />\n  <meta name="viewport" content="width=device-width,initial-scale=1" />\n  <title>弹弹play资源搜索节点API - v${k.b}</title>\n</head>\n<body>\n  <h1>使用说明</h1>\n  <h2>GitHub - <a href="${k.a}">LussacZheng/dandanplay-resource-service</a></h2>\n</body>\n</html>\n`;const j=e=>t=>t.method.toLowerCase()===e.toLowerCase(),L=j("connect"),$=j("delete"),E=j("get"),C=j("head"),A=j("options"),D=j("patch"),z=j("post"),F=j("put"),H=j("trace"),J=e=>t=>{const n=new URL(encodeURI(t.url)).pathname;return(n.match(e)||[])[0]===n};var W=class{constructor(){this.routes=[]}handle(e,t){return this.routes.push({conditions:e,handler:t}),this}connect(e,t){return this.handle([L,J(e)],t)}delete(e,t){return this.handle([$,J(e)],t)}get(e,t){return this.handle([E,J(e)],t)}head(e,t){return this.handle([C,J(e)],t)}options(e,t){return this.handle([A,J(e)],t)}patch(e,t){return this.handle([D,J(e)],t)}post(e,t){return this.handle([z,J(e)],t)}put(e,t){return this.handle([F,J(e)],t)}trace(e,t){return this.handle([H,J(e)],t)}all(e){return this.handle([],e)}route(e){const t=this.resolve(e);return t?t.handler(e):new Response("resource not found",{status:404,statusText:"not found",headers:{"content-type":"text/plain"}})}resolve(e){return this.routes.find(t=>!(t.conditions&&(!Array.isArray(t)||t.conditions.length))||("function"==typeof t.conditions?t.conditions(e):t.conditions.every(t=>t(e))))}};async function Z(e){const t=new W;t.get("/subgroup",async()=>{const e=await async function(){return{Subgroups:N(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/type",async()=>{const e=await async function(){return{Types:P(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/list",async e=>{const t=await T(e);return new Response(JSON.stringify(t),u)}),t.get("/",async()=>new Response(await async function(){let e;try{e=await d("https://cdn.jsdelivr.net/gh/LussacZheng/dandanplay-resource-service@dist/web/index.html"),e=o(e,{VERSION:k.b})}catch(t){e=M}return e}(),l));return await t.route(e)}addEventListener("fetch",e=>e.respondWith(Z(e.request)))}]);

目前的缺点如下:

  • 使用 $realtime 时,只允许空格这一种符号。不能与带 & | ! ( ) 的高级搜索同时使用。
  • $realtime 不支持 "延迟" 资源的简繁体转换。即关键词为简中时,同名的繁中 "延迟" 资源不会出现在结果中。

此外,$realtime 还支持指定页码。如 $realtime:2 表示从 第二页 中寻找 "延迟" 资源,而非 首页
当然这个可能没什么用,这么设计主要时为了以后可以添加更多的 "搜索指令/选项" ,如:

  • $page:n : 获取搜索结果的第 n 页,而不是总是返回第一页
  • $limit:n : 限制搜索结果的数量上限为 n ,而不是总为 80
  • ......

感谢你的回复,我测试过新的脚本可以实现实时搜索的功能,并且可以配合自动下载关键词功能实现资源的实时自动下载,并且如你所反映的,无法实现简繁体的转换,不过对于我来说已经很实用了,谢谢(我用的是cf-worker)。
1

from dandanplay-resource-service.

zzz6839 avatar zzz6839 commented on June 2, 2024

我发现这个新的脚本不支持多个关键词搜索,以侦探已死为例,如果搜索关键词“侦探已死”+“$realtime”可以得到实时结果,但是如果进一步增加关键词比如“侦探已死”+“$realtime”+“global”就无法得到实时数据,希望可以修复一下,或者在脚本里集成realtime search的功能

from dandanplay-resource-service.

LussacZheng avatar LussacZheng commented on June 2, 2024

其实是支持的,只是我忘记了在使用 $realtime 时,应该将关键词 toLowerCase() 即全部转换为小写字母后,再进行比较。
对于之前的脚本,你使用 "侦探已死" + "$realtime" + "Global" 应该是可以得到实时数据的。

修复后的 worker.js 如下:

// https://github.com/LussacZheng/dandanplay-resource-service
// version: 0.0.4-alpha.1
// build: 2021-08-15 22:41 GMT+8
// wrangler: 1.19.0

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e){e.exports=JSON.parse('{"b":"0.0.4-alpha.1","a":"https://github.com/LussacZheng/dandanplay-resource-service"}')},function(e,t,n){"use strict";n.r(t);class r{constructor(e){const{keyword:t,options:n}=function(e){let t={};return{keyword:e.replace(/ ?\$(\w+)(:(\d+))?/gi,(e,n,r,o)=>(t[n]=parseInt(o)||1,"")),options:t}}(e);this.keyword=t,this.realtime=n.realtime||0}}function o(e,t){return e.replace(/\$\{(\w+)\}/gi,(e,n)=>t[n])}function a(e){const t=new Date(e).toLocaleString("default",{formatMatcher:"best fit",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"});return new Date(t+" UTC").toISOString().substr(0,19).replace("T"," ")}function s(e,t){if(0===t.length)return e[1];let n={};return t.length>e.length-1&&t.splice(e.length-1),t.forEach((t,r)=>{n[t]=e[r+1]}),n}var i=function(e,t,n,r="first"){switch(r){case"all":return function(e,t,n){const r=e.matchAll(t),o=Array.from(r,e=>s(e,n));return 0===o.length?null:o}(e,t,n);case"last":return function(e,t,n){const r=[...e.matchAll(t)],o=r[r.length-1];return 0===r.length?null:s(o,n)}(e,t,n);default:return function(e,t,n){let r=t.exec(e);return t.lastIndex=0,null===r?null:s(r,n)}(e,t,n)}};const u={headers:{"content-type":"application/json;charset=utf-8"}},l={headers:{"content-type":"text/html;charset=utf-8"}},c={headers:{accept:"text/html;charset=utf-8","user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}};async function d(e,t=c){let n;try{n=await fetch(decodeURI(e),t)}catch(e){console.error(e)}return await async function(e){const{headers:t}=e,n=t.get("content-type");return n.includes("application/json")?await e.json():(n.includes("application/text")||n.includes("text/html"),await e.text())}(n)}const p="https://share.dmhy.org",h={type_and_subgroup_url:p+"/topics/advanced-search?team_id=0&sort_id=0&orderby=",list_url:p+"/topics/list/page/1?keyword=${keyword}&sort_id=${type}&team_id=${subgroup}&order=date-desc",index_url:p+"/topics/list/page/${realtime}"},g="未能成功解析标题",f=-2,y="未能成功解析类别",m=-1,w="未知字幕组",b="magnet_not_found_未能成功解析磁力链接或磁力链接不存在",_="未能成功解析资源发布页面",I="未能成功解析资源大小",v="1970-01-01 08:00:00",R=/<option value="(\d+)">(.+?)<\/option>/gim,S=/<option value="(\d+)" style="color: [\w#]+">(.+?)<\/option>/gim,x={HasMore:/下一頁/g,Resources:/<tr class="">(.*?)<\/tr>/gis,TypeId:/href="\/topics\/list\/sort_id\/(\d+)"/gim,TypeName:/<font color=[\w#]+>(.+)<\/font>/gim,SubgroupId:/href="\/topics\/list\/team_id\/(\d+)"/gim,SubgroupName:/\s+(.*)<\/a><\/span>/gim,Magnet:/href="(magnet:\?xt=urn:btih:.+?)"/gim,PageUrl:/href="(.+?)"\s*target="_blank"/gim,FileSize:/<td.*>([\w\.]+B)<\/td>/gim,PublishDate:/<span style="display: none;">([\d\/ :]+)<\/span>/gim,Title:/target="_blank" ?>(.+?)<\/a>/gis,TitleReplacer:/<span class="keyword">(.*?)<\/span>/gi};async function T(e){const t=new URL(encodeURI(e.url)).searchParams,n=t.get("type")||0,a=t.get("subgroup")||0,{keyword:s,realtime:u}=new r(decodeURIComponent(t.get("keyword"))),l=encodeURI(o(h.list_url,{keyword:s,type:n<0?0:n,subgroup:a<0?0:a}));let c=await d(l),p=function(e){let t={HasMore:null!==i(e,x.HasMore,[]),Resources:[]};const n=i(e,x.Resources,[],"all");return null===n||n.forEach(e=>{t.Resources.push(O(e))}),t}(c);if(u){const e=encodeURI(o(h.index_url,{realtime:u}));c=await d(e);const t=U(c,s,p.Resources);p.Resources=t.concat(p.Resources)}return p}function N(e){const t=e.replace(/&amp;/gi,"&");let n=i(t,R,["Id","Name"],"all");return null===n?[]:(n.forEach(e=>e.Id=parseInt(e.Id)),n.shift(),n)}function P(e){let t=i(e,S,["Id","Name"],"all");return null===t?[]:(t.forEach(e=>e.Id=parseInt(e.Id)),t.unshift({Id:0,Name:"全部"}),t)}function U(e,t,n){let r=[];const o=i(e,x.Resources,[],"all");return null===o?result:(o.forEach(e=>{let o=O(e);const a=t.split(" ").every(e=>o.Title.toLowerCase().includes(e.toLowerCase())),s=n.some(e=>o.PageUrl===e.PageUrl);a&&!s&&r.push(o)}),r)}function O(e){const t=i(e,x.Title,[]),n=i(e,x.TypeId,[]),r=i(e,x.TypeName,[]),o=i(e,x.SubgroupId,[]),s=i(e,x.SubgroupName,[]),u=i(e,x.Magnet,[]),l=i(e,x.PageUrl,[]),c=i(e,x.FileSize,[]),d=i(e,x.PublishDate,[]);return{Title:null===t?g:t.trim().replace(x.TitleReplacer,"$1"),TypeId:parseInt(n)||f,TypeName:r||y,SubgroupId:parseInt(o)||m,SubgroupName:s||w,Magnet:u||b,PageUrl:null===l?_:p+l,FileSize:c||I,PublishDate:null===d?v:a(d)}}var k=n(0);const M=`\n<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n  <meta charset="UTF-8" />\n  <meta name="viewport" content="width=device-width,initial-scale=1" />\n  <title>弹弹play资源搜索节点API - v${k.b}</title>\n</head>\n<body>\n  <h1>使用说明</h1>\n  <h2>GitHub - <a href="${k.a}">LussacZheng/dandanplay-resource-service</a></h2>\n</body>\n</html>\n`;const j=e=>t=>t.method.toLowerCase()===e.toLowerCase(),L=j("connect"),C=j("delete"),$=j("get"),E=j("head"),A=j("options"),D=j("patch"),z=j("post"),F=j("put"),H=j("trace"),J=e=>t=>{const n=new URL(encodeURI(t.url)).pathname;return(n.match(e)||[])[0]===n};var W=class{constructor(){this.routes=[]}handle(e,t){return this.routes.push({conditions:e,handler:t}),this}connect(e,t){return this.handle([L,J(e)],t)}delete(e,t){return this.handle([C,J(e)],t)}get(e,t){return this.handle([$,J(e)],t)}head(e,t){return this.handle([E,J(e)],t)}options(e,t){return this.handle([A,J(e)],t)}patch(e,t){return this.handle([D,J(e)],t)}post(e,t){return this.handle([z,J(e)],t)}put(e,t){return this.handle([F,J(e)],t)}trace(e,t){return this.handle([H,J(e)],t)}all(e){return this.handle([],e)}route(e){const t=this.resolve(e);return t?t.handler(e):new Response("resource not found",{status:404,statusText:"not found",headers:{"content-type":"text/plain"}})}resolve(e){return this.routes.find(t=>!(t.conditions&&(!Array.isArray(t)||t.conditions.length))||("function"==typeof t.conditions?t.conditions(e):t.conditions.every(t=>t(e))))}};async function Z(e){const t=new W;t.get("/subgroup",async()=>{const e=await async function(){return{Subgroups:N(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/type",async()=>{const e=await async function(){return{Types:P(await d(h.type_and_subgroup_url))}}();return new Response(JSON.stringify(e),u)}),t.get("/list",async e=>{const t=await T(e);return new Response(JSON.stringify(t),u)}),t.get("/",async()=>new Response(await async function(){let e;try{e=await d("https://cdn.jsdelivr.net/gh/LussacZheng/dandanplay-resource-service@dist/web/index.html"),e=o(e,{VERSION:k.b})}catch(t){e=M}return e}(),l));return await t.route(e)}addEventListener("fetch",e=>e.respondWith(Z(e.request)))}]);

(其实你回复时不需要整段引用,直接评论回复,或只引用关键语句就行。这样可以减少无效信息。)

from dandanplay-resource-service.

LussacZheng avatar LussacZheng commented on June 2, 2024

v0.0.4-beta

v0.0.4-beta 现已发布,正式支持 "搜索指令" : $realtime , $page , $limit

获取

如果你感兴趣的话,可以试用并测试一下。有任何 Bug 或不合理的地方欢迎指出。

from dandanplay-resource-service.

zzz6839 avatar zzz6839 commented on June 2, 2024

感谢你对脚本的改进。

因为我只用到realtime 这个指令,所以测试beta版本了下是可以用的,建议加入简繁转换,可以更方便搜索出baha的三俗片源
比如奶子宿舍的管理員

from dandanplay-resource-service.

LussacZheng avatar LussacZheng commented on June 2, 2024

理由

cf-worker 版本暂时无法支持的原因如 文档 中所述:

理由是暂时未能实现一个轻量且全面的 JavaScript 简繁转换函数。而 go 实现没有此问题。

为 Node.js 编写的简繁转换库有很多,但直接引用过来会导致打包出的 worker.js 文件体积急剧增大,这可能会导致 "CPU 运行时间" 超出 Cloudflare Workers 免费账户 10 ms 的时间限制,或 其他限制

worker.js 的体积可能由 10 KB 增大到几百 KB 甚至几 MB 。而 golang 版本是编译后在服务器上运行的,其本身体积可达 15~20 MB ,再嵌入一个简繁体字符转换库,这对体积的影响微乎其微。

建议

我的建议是:对于 cf-worker 版本,用户可以分析目标资源的标题,从中提取出多个具有标识性的 “英文单词” 、“罗马音单词” 、“简繁体同形的字” 来作为关键词,并搭配组合,以规避这一问题。

以你所提到的《女神宿舍的管理员》为例,该番剧可能出现的名称有:

  • 女神宿舍的管理员
  • 女神宿舍的管理員
  • Megami-ryou no Ryoubo-kun

因此不妨将 女神ryoubo 作为搜索关键词,还可以带上 baha ,最终的关键词可以使用 女神 ryoubo baha $realtime

需求

如果你确实需要 cf-worker 版本支持简繁体转换,这里有一个我使用 chinese-conv 库完成简繁转换并打包成的临时版本

https://paste.ubuntu.com/p/NHcs6Xy35H/
https://gist.github.com/LussacZheng/30655835397714f4689960b2beb4fc0a

如你所见,由于内嵌了简繁体字符的转换字典,其代码长度急剧增加,因此我无法保证其稳定性。你可以尝试使用一段时间看看。

from dandanplay-resource-service.

Related Issues (5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.