├── .gitignore ├── img ├── favicon-dmhy.png └── favicon-popgo.png ├── robots.txt ├── dht ├── README.md └── inc │ ├── Node.class.php │ └── Base.class.php ├── lib ├── phpQuery │ └── phpQuery │ │ ├── plugins │ │ ├── Scripts │ │ │ ├── __config.example.php │ │ │ ├── print_source.php │ │ │ ├── print_websafe.php │ │ │ ├── example.php │ │ │ ├── fix_webroot.php │ │ │ └── google_login.php │ │ ├── example.php │ │ └── Scripts.php │ │ ├── bootstrap.example.php │ │ ├── Zend │ │ ├── Exception.php │ │ ├── Json │ │ │ └── Exception.php │ │ ├── Http │ │ │ ├── Exception.php │ │ │ └── Client │ │ │ │ ├── Exception.php │ │ │ │ └── Adapter │ │ │ │ ├── Exception.php │ │ │ │ └── Interface.php │ │ ├── Uri │ │ │ └── Exception.php │ │ ├── Validate │ │ │ ├── Exception.php │ │ │ ├── Hostname │ │ │ │ ├── Se.php │ │ │ │ ├── Fi.php │ │ │ │ ├── Hu.php │ │ │ │ ├── At.php │ │ │ │ ├── Ch.php │ │ │ │ ├── Li.php │ │ │ │ ├── No.php │ │ │ │ ├── Interface.php │ │ │ │ └── De.php │ │ │ ├── Ip.php │ │ │ ├── NotEmpty.php │ │ │ ├── Hex.php │ │ │ ├── Int.php │ │ │ ├── Float.php │ │ │ ├── Interface.php │ │ │ ├── File │ │ │ │ ├── NotExists.php │ │ │ │ ├── FilesSize.php │ │ │ │ ├── Exists.php │ │ │ │ └── MimeType.php │ │ │ ├── LessThan.php │ │ │ ├── GreaterThan.php │ │ │ ├── Digits.php │ │ │ ├── Barcode │ │ │ │ ├── UpcA.php │ │ │ │ └── Ean13.php │ │ │ ├── Barcode.php │ │ │ ├── Identical.php │ │ │ ├── Ccnum.php │ │ │ ├── Regex.php │ │ │ ├── Alpha.php │ │ │ ├── Alnum.php │ │ │ ├── InArray.php │ │ │ ├── StringLength.php │ │ │ └── Between.php │ │ └── Uri.php │ │ ├── compat │ │ └── mbstring.php │ │ ├── DOMEvent.php │ │ ├── Callback.php │ │ └── phpQueryEvents.php ├── base32.php └── lightbenc.php ├── nav.tpl.php ├── misc └── build_popgo_btih.php ├── README.md ├── header.php ├── js ├── index.js └── desc.js ├── indexer_popgo.php ├── search.tpl.php ├── about.php ├── ajax └── info.php ├── footer.tpl.php ├── css └── index.css ├── indexer.php ├── seed.php ├── rss.php ├── popgo_helper.php ├── webkit_crawl.js ├── popgo_history_indexer.php ├── popular-keyword.php ├── popular-resource.php ├── indexer_dmhy.php ├── config.sample.php ├── desc.php ├── db_definition.sql └── indexer_base.php /.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | archive 3 | torrent 4 | -------------------------------------------------------------------------------- /img/favicon-dmhy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greensea/rssindexer/HEAD/img/favicon-dmhy.png -------------------------------------------------------------------------------- /img/favicon-popgo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greensea/rssindexer/HEAD/img/favicon-popgo.png -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /seed.php* 3 | Disallow: /seed-* 4 | Disallow: /ajax/ 5 | -------------------------------------------------------------------------------- /dht/README.md: -------------------------------------------------------------------------------- 1 | # DHT 2 | 一个使用PHP编写的DHT爬虫 3 | 4 | 可自动加入DHT网络并获取到info_hash信息,获取速率当前大概半分钟到一分钟一个hash 5 | 6 | 使用方法: 7 | 8 | 1. 安装PHP,请安装新版本 9 | 2. 安装swoole扩展 10 | 3. 运行命令启动 11 | 12 | ``` 13 | php dht.php 14 | ``` 15 | 16 | 作者主页:[http://www.lovekk.org/](http://www.lovekk.org/) 17 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/Scripts/__config.example.php: -------------------------------------------------------------------------------- 1 | array('login@mail', 'password'), 9 | ); 10 | ?> -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/Scripts/print_source.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | /** @var phpQueryObject */ 8 | $self = $self; 9 | $return = htmlspecialchars($self); -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/Scripts/print_websafe.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | /** @var phpQueryObject */ 8 | $self = $self; 9 | $self 10 | ->find('script') 11 | ->add('meta[http-equiv=refresh]') 12 | ->add('meta[http-equiv=Refresh]') 13 | ->remove(); -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/bootstrap.example.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nav.tpl.php: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/Scripts/example.php: -------------------------------------------------------------------------------- 1 | find($params[0]); 14 | ?> -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/Scripts/fix_webroot.php: -------------------------------------------------------------------------------- 1 | filter($filter) as $el) { 9 | $el = pq($el, $self->getDocumentID()); 10 | // imgs and scripts 11 | if ( $el->is('img') || $el->is('script') ) 12 | $el->attr('src', $params[0].$el->attr('src')); 13 | // css 14 | if ( $el->is('link') ) 15 | $el->attr('href', $params[0].$el->attr('href')); 16 | } -------------------------------------------------------------------------------- /misc/build_popgo_btih.php: -------------------------------------------------------------------------------- 1 | query($sql) or die($mysqli->error); 9 | 10 | if (!$res) { 11 | LOGI("已经没有需要更新的资源了"); 12 | exit(0); 13 | } 14 | 15 | LOGI(sprintf("需要更新 %d 个资源", $res->num_rows)); 16 | 17 | while ($row = $res->fetch_assoc()) { 18 | $btih = popgo_get_btih_from_link($row['guid']); 19 | $sql = "UPDATE b_resource SET btih='{$btih}' WHERE resource_id={$row['resource_id']}"; 20 | $mysqli->query($sql) or die($mysqli->error); 21 | } 22 | 23 | 24 | ?> 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rssindexer 2 | 索引 BT RSS 资源,提供搜索服务,实现 RSS 大法 3 | 4 | ## 生产站点 5 | 6 | http://moe4sale.in 7 | 8 | 9 | ## 部署方法 10 | 11 | 1. 在库目录下创建 archive 和 torrent 目录,并保证 PHP 有权读写这两个目录 12 | 1. 创建一个网站,网站根目录就是仓库目录 13 | 1. 创建网址重写规则:rss.php -> rss.xml 14 | 1. 创建 CRON 任务,每隔 30 分钟(建议值)通过 PHP-CLI 在库目录下运行 indexer.php。请保证用户具有对 archive 目录的写权限 15 | 1. 创建 CRON 任务,每隔 5 分钟(建议值)通过 PHP-CLI 在库目录下运行 popgo_helper.php。请保证用户具有对 torrent 目录的写权限 16 | 1. 使用 db_definition.sql 创建数据库 17 | 1. 将 config.sample.php 重命为 config.php,并根据实际情况修改配置 18 | 19 | ### 注意事项 20 | 21 | 1. popgo_helper.php 和 seed.php 均会在 torrent 目录下创建子目录,前者作为 CLI 运行,后者作为网页脚本运行。建议使用相同的用户运行 CLI 和网页脚本,避免出现奇怪的权限问题。 22 | 1. 如果出现了奇怪的问题,请在 config.php 中配置日志文件,然后查阅日志。如果已经在 config.php 中填写了日志文件地址,但没有看到日志文件,请检查 PHP 是否具有创建和写入日志文件的权限。 23 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | error); 16 | die(); 17 | } 18 | $mysqli->query("set NAMES 'utf8'"); 19 | 20 | $CSP_NONCE = uniqid() . 'X' . mt_rand(); 21 | 22 | 23 | 24 | require_once(__DIR__ . '/func.php'); 25 | 26 | function ob_i18n_handler($in) { 27 | $pattern = <<]*>)(.+)<\/\2>/gsU' 29 | EOF; 30 | $matches = NULL; 31 | preg_match($pattern, $in, $matches); 32 | } 33 | ?> 34 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Exception.php: -------------------------------------------------------------------------------- 1 | 255) { 35 | obj.css("font-weight", "bold"); 36 | } 37 | } 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Json/Exception.php: -------------------------------------------------------------------------------- 1 | _fetch(); 8 | 9 | $ret = array(); 10 | foreach ($rs as $r) { 11 | $match = NULL; 12 | $btih = ''; 13 | preg_match('([0-9a-f]{40})', $r['guid'], $match); 14 | if ($match) { 15 | $btih = $match[0]; 16 | } 17 | else { 18 | LOGW("无法解析资源的 BTIH, r = " . var_export($r, TRUE)); 19 | continue; 20 | } 21 | 22 | $ret[] = array( 23 | 'btih' => $btih, 24 | 'title' => $r['title'], 25 | 'guid' => $r['guid'], 26 | 'link' => $r['link'], 27 | 'description' => $r['description'], 28 | 'pubDate' => strtotime($r['pubDate']), 29 | 'magnet' => '', 30 | ); 31 | } 32 | 33 | return $ret; 34 | } 35 | 36 | 37 | static public function getSrcSeedURL($btih) { 38 | return popgo_get_seed_url($btih); 39 | } 40 | } 41 | 42 | 43 | ?> 44 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Uri/Exception.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | phpQuery::ajaxAllowHost( 10 | 'code.google.com', 11 | 'google.com', 'www.google.com', 12 | 'mail.google.com', 13 | 'docs.google.com', 14 | 'reader.google.com' 15 | ); 16 | if (! function_exists('ndfasui8923')) { 17 | function ndfasui8923($browser, $scope) { 18 | extract($scope); 19 | $browser 20 | ->WebBrowser() 21 | ->find('#Email') 22 | ->val($config['google_login'][0])->end() 23 | ->find('#Passwd') 24 | ->val($config['google_login'][1]) 25 | ->parents('form') 26 | ->submit(); 27 | } 28 | $ndfasui8923 = new Callback('ndfasui8923', new CallbackParam, compact( 29 | 'config', 'self', 'return', 'params' 30 | )); 31 | } 32 | phpQuery::plugin('WebBrowser'); 33 | $self->document->xhr = phpQuery::$plugins->browserGet( 34 | 'https://www.google.com/accounts/Login', 35 | $ndfasui8923 36 | ); 37 | //$self->document->xhr = phpQuery::$plugins->browserGet('https://www.google.com/accounts/Login', create_function('$browser', " 38 | // \$browser 39 | // ->WebBrowser() 40 | // ->find('#Email') 41 | // ->val('{$config['google_login'][0]}')->end() 42 | // ->find('#Passwd') 43 | // ->val('".str_replace("'", "\\'", $config['google_login'][1])."') 44 | // ->parents('form') 45 | // ->submit();" 46 | //)); 47 | ?> -------------------------------------------------------------------------------- /search.tpl.php: -------------------------------------------------------------------------------- 1 | $v) { 10 | /* 11 | $popularity = 0; 12 | if ($v['popularity'] >= 0) { 13 | $decays = (time() - $v['pmtime']) / 86400 / $POPULARITY_HALFLIFE_DAYS; 14 | $popularity = $v['popularity'] * pow(2, -1 * $decays); 15 | } 16 | */ 17 | if ($v['popularity2'] < 1.001) { 18 | unset($popular_kws[$k]); 19 | } 20 | } 21 | ?> 22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 | 31 | 32 | 40 | 41 | 42 |
43 |
44 | -------------------------------------------------------------------------------- /about.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 关于本站 - KOTOMI RSS 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |

起源

24 | 25 |

极影发布站的恢复遥遥无期,漫游 RSS 没有搜索功能。为了保证丰衣足食,还是自己搭一个支持搜索的 RSS 站比较靠谱,于是本站就出现了。

26 |

本站于 2015 年 4 月 9 日开始运行,仅索引了发布时间晚于该日期的资源,早于该日期发布的资源索引不完整。

27 | 28 |

资源来源

29 | 30 |

所有资源来自于漫游发布页,资源定期从漫游发布页同步到此(会有一些延迟)。

31 | 32 |

关于源码

33 | 34 |

源码托管在 github.com 上,为了发动人民群众的力量,建立具有鲁棒性的 ACG 分享网络,源码使用 WTFPL 授权,欢迎在 github 上插我(fork me on github),你插越多我越高兴。

35 | 36 |

互动

37 | 38 |

如有疑问或建议可在 Masochist-board 上发贴,也可借助 github 直接与我联系。

39 |
40 |
41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /dht/inc/Node.class.php: -------------------------------------------------------------------------------- 1 | nid = $nid; 31 | $this->ip = $ip; 32 | $this->port = $port; 33 | } 34 | 35 | /** 36 | * 使外部可获取私有属性 37 | * @param string $name 属性名称 38 | * @return mixed 属性值 39 | */ 40 | public function __get($name){ 41 | // 检查属性是否存在 42 | if(isset($this->$name)) 43 | return $this->$name; 44 | 45 | return null; 46 | } 47 | 48 | /** 49 | * 使外部可直接对私有属性赋值 50 | * @param string $name 属性名称 51 | * @param mixed $value 属性值 52 | * @return void 53 | */ 54 | public function __set($name, $value){ 55 | $this->$name = $value; 56 | } 57 | 58 | /** 59 | * 检查属性是否设置 60 | * @param string $name 属性名称 61 | * @return boolean 是否设置 62 | */ 63 | public function __isset($name){ 64 | return isset($this->$name); 65 | } 66 | 67 | /** 68 | * 将Node模型转换为数组 69 | * @return array 转换后的数组 70 | */ 71 | public function to_array(){ 72 | return array('nid' => $this->nid, 'ip' => $this->ip, 'port' => $this->port); 73 | } 74 | } -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Hostname/Se.php: -------------------------------------------------------------------------------- 1 | bdecode(file_get_contents($path)); 26 | 27 | if (!$raw) { 28 | apiout(-2, '无法解析种子文件'); 29 | } 30 | 31 | $data = []; 32 | foreach (['announce', 'announce-list', 'created by', 'creation date'] as $k) { 33 | if (isset($raw[$k])) { 34 | $data[$k] = $raw[$k]; 35 | } 36 | } 37 | 38 | /// 如果 info 中没有 files 字段,则种子文件只包含一个共享文件,且该共享文件信息直接放在 info 字段中 39 | 40 | if (!isset($raw['info']['files'])) { 41 | $raw['info']['files'] = [$raw['info']]; 42 | } 43 | 44 | $data['files'] = []; 45 | foreach ($raw['info']['files'] as $file) { 46 | if (!isset($file['path']) && isset($file['name'])) { 47 | $file['path'] = $file['name']; 48 | } 49 | 50 | if (!is_array($file['path'])) { 51 | $file['path'] = [$file['path']]; 52 | } 53 | 54 | $f = [ 55 | 'path' => implode('/', $file['path']), 56 | 'size' => $file['length'], 57 | ]; 58 | 59 | /// 过滤比特彗星的垃圾文件 60 | if ($FILTER_BITCOMET_PADDING_FILE == TRUE) { 61 | if (strpos($f['path'], '_____padding_file_') === 0 && stripos($file['path'], 'BitComet') !== FALSE) { 62 | $f = NULL; 63 | } 64 | } 65 | 66 | if ($f) { 67 | $data['files'][] = $f; 68 | } 69 | } 70 | 71 | 72 | apiout(0, '', $data); 73 | 74 | ?> 75 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Hostname/No.php: -------------------------------------------------------------------------------- 1 | ").text(file.path); 28 | var td2 = $("").text(size2readable(file.size)); 29 | var tr = $("").append(td1).append(td2); 30 | 31 | $(".files table tbody").append(tr); 32 | 33 | totalSize += parseFloat(file.size); 34 | } 35 | 36 | var tip = $("
").addClass("tip").html("共" + d.data.files.length + "个文件,总大小" + size2readable(totalSize) + ""); 37 | $(".files").append(tip); 38 | } 39 | }); 40 | }); 41 | 42 | function size2readable(size) { 43 | size = parseFloat(size); 44 | var map = { 45 | "TiB" : 1024 * 1024 * 1024 * 1024, 46 | "GiB": 1024 * 1024 * 1024, 47 | "MiB": 1024 * 1024, 48 | "KiB": 1024 49 | }; 50 | 51 | for (k in map) { 52 | if (size / map[k] > 1) { 53 | return (size / map[k]).toFixed(2) + " " + k; 54 | } 55 | } 56 | 57 | return size + " B"; 58 | } 59 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/compat/mbstring.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 本站资源索引自:漫游 BT 发布页 4 |
5 | 6 |
7 | 本站由这些赞助商有爱人士提供支援: 8 | 麻痹科学网 9 |
10 |
11 | 12 | 18 | 19 | 25 | 34 | 35 | 36 | 42 | 52 | 53 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Hostname/Interface.php: -------------------------------------------------------------------------------- 1 | "'%value%' does not appear to be a valid IP address" 46 | ); 47 | 48 | /** 49 | * Defined by Zend_Validate_Interface 50 | * 51 | * Returns true if and only if $value is a valid IP address 52 | * 53 | * @param mixed $value 54 | * @return boolean 55 | */ 56 | public function isValid($value) 57 | { 58 | $valueString = (string) $value; 59 | 60 | $this->_setValue($valueString); 61 | 62 | if (ip2long($valueString) === false) { 63 | $this->_error(); 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/example.php: -------------------------------------------------------------------------------- 1 | plugin('example') 9 | * pq('ul')->plugin('example', 'example.php') 10 | * 11 | * Plugin classes are never intialized, just method calls are forwarded 12 | * in static way from phpQuery. 13 | * 14 | * Have fun writing plugins :) 15 | */ 16 | 17 | /** 18 | * phpQuery plugin class extending phpQuery object. 19 | * Methods from this class are callable on every phpQuery object. 20 | * 21 | * Class name prefix 'phpQueryObjectPlugin_' must be preserved. 22 | */ 23 | abstract class phpQueryObjectPlugin_example { 24 | /** 25 | * Limit binded methods. 26 | * 27 | * null means all public. 28 | * array means only specified ones. 29 | * 30 | * @var array|null 31 | */ 32 | public static $phpQueryMethods = null; 33 | /** 34 | * Enter description here... 35 | * 36 | * @param phpQueryObject $self 37 | */ 38 | public static function example($self, $arg1) { 39 | // this method can be called on any phpQuery object, like this: 40 | // pq('div')->example('$arg1 Value') 41 | 42 | // do something 43 | $self->append('Im just an example !'); 44 | // change stack of result object 45 | return $self->find('div'); 46 | } 47 | protected static function helperFunction() { 48 | // this method WONT be avaible as phpQuery method, 49 | // because it isn't publicly callable 50 | } 51 | } 52 | 53 | /** 54 | * phpQuery plugin class extending phpQuery static namespace. 55 | * Methods from this class are callable as follows: 56 | * phpQuery::$plugins->staticMethod() 57 | * 58 | * Class name prefix 'phpQueryPlugin_' must be preserved. 59 | */ 60 | abstract class phpQueryPlugin_example { 61 | /** 62 | * Limit binded methods. 63 | * 64 | * null means all public. 65 | * array means only specified ones. 66 | * 67 | * @var array|null 68 | */ 69 | public static $phpQueryMethods = null; 70 | public static function staticMethod() { 71 | // this method can be called within phpQuery class namespace, like this: 72 | // phpQuery::$plugins->staticMethod() 73 | } 74 | } 75 | ?> -------------------------------------------------------------------------------- /css/index.css: -------------------------------------------------------------------------------- 1 | .beta { 2 | font-size: 0.5em; 3 | transform: rotate(-10deg); 4 | display: inline-block; 5 | } 6 | 7 | .head-title { 8 | font-size: 4em; 9 | } 10 | .head-subtitle { 11 | color: #888; 12 | font-size: 1.2em; 13 | } 14 | 15 | .popular-keywords { 16 | text-align: center; 17 | margin-top: -0.5em; 18 | opacity: 0.8; 19 | margin-bottom: 0.5em; 20 | } 21 | .popular-keywords a { 22 | margin-left: 0.5em; 23 | } 24 | 25 | 26 | .search { 27 | margin-top: 2em; 28 | margin-bottom: 1em; 29 | } 30 | .search-result-sub-title { 31 | font-size: 1.2em; 32 | font-weight: bold; 33 | margin-top: 1em; 34 | } 35 | 36 | 37 | table th { 38 | text-align: center; 39 | } 40 | 41 | table td.pubDate { 42 | min-width: 7em; 43 | } 44 | 45 | table td.popularity { 46 | min-width: 4em; 47 | text-align: center; 48 | font-family: sans-serif; 49 | } 50 | 51 | .resources .guid { 52 | min-width: 6em; 53 | text-align: center; 54 | } 55 | .resources .link { 56 | min-width: 6em; 57 | text-align: center; 58 | } 59 | .resources .source { 60 | min-width: 5em; 61 | text-align: center; 62 | } 63 | 64 | .footer div>span { 65 | margin-left: 0.5em; 66 | margin-right: 0.5em; 67 | } 68 | 69 | .favicon-dmhy:before { 70 | content: url('../img/favicon-dmhy.png') " "; 71 | } 72 | .favicon-popgo:before { 73 | content: url('../img/favicon-popgo.png') " "; 74 | } 75 | 76 | 77 | /** desc.php 页面的样式 */ 78 | .desc { 79 | margin-top: 1%; 80 | } 81 | .desc .content { 82 | background-color: #eef; 83 | border: 1px solid #ececec; 84 | } 85 | 86 | .files table td { 87 | padding-right: 1em; 88 | padding-top: 0.3em; 89 | padding-bottom: 0.3em; 90 | font-size: 14px; 91 | } 92 | .files .tip { 93 | font-weight: bold; 94 | margin-top: 0.5em; 95 | } 96 | .files .tip strong { 97 | color: #78f; 98 | padding: 0 0.25em; 99 | } 100 | 101 | /** popularkw.php 页面的样式 **/ 102 | table.popular-kw-list { 103 | width: auto; 104 | margin: 0 auto; 105 | } 106 | table.popular-kw-list td { 107 | text-align: center; 108 | } 109 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/plugins/Scripts.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/NotEmpty.php: -------------------------------------------------------------------------------- 1 | "Value is empty, but a non-empty value is required" 46 | ); 47 | 48 | /** 49 | * Defined by Zend_Validate_Interface 50 | * 51 | * Returns true if and only if $value is not an empty value. 52 | * 53 | * @param string $value 54 | * @return boolean 55 | */ 56 | public function isValid($value) 57 | { 58 | $this->_setValue((string) $value); 59 | 60 | if (is_string($value) 61 | && (('' === $value) 62 | || preg_match('/^\s+$/s', $value)) 63 | ) { 64 | $this->_error(); 65 | return false; 66 | } elseif (!is_string($value) && empty($value)) { 67 | $this->_error(); 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Hex.php: -------------------------------------------------------------------------------- 1 | "'%value%' has not only hexadecimal digit characters" 50 | ); 51 | 52 | /** 53 | * Defined by Zend_Validate_Interface 54 | * 55 | * Returns true if and only if $value contains only hexadecimal digit characters 56 | * 57 | * @param string $value 58 | * @return boolean 59 | */ 60 | public function isValid($value) 61 | { 62 | $valueString = (string) $value; 63 | 64 | $this->_setValue($valueString); 65 | 66 | if (!ctype_xdigit($valueString)) { 67 | $this->_error(); 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Int.php: -------------------------------------------------------------------------------- 1 | "'%value%' does not appear to be an integer" 46 | ); 47 | 48 | /** 49 | * Defined by Zend_Validate_Interface 50 | * 51 | * Returns true if and only if $value is a valid integer 52 | * 53 | * @param string $value 54 | * @return boolean 55 | */ 56 | public function isValid($value) 57 | { 58 | $valueString = (string) $value; 59 | 60 | $this->_setValue($valueString); 61 | 62 | $locale = localeconv(); 63 | 64 | $valueFiltered = str_replace($locale['decimal_point'], '.', $valueString); 65 | $valueFiltered = str_replace($locale['thousands_sep'], '', $valueFiltered); 66 | 67 | if (strval(intval($valueFiltered)) != $valueFiltered) { 68 | $this->_error(); 69 | return false; 70 | } 71 | 72 | return true; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Float.php: -------------------------------------------------------------------------------- 1 | "'%value%' does not appear to be a float" 46 | ); 47 | 48 | /** 49 | * Defined by Zend_Validate_Interface 50 | * 51 | * Returns true if and only if $value is a floating-point value 52 | * 53 | * @param string $value 54 | * @return boolean 55 | */ 56 | public function isValid($value) 57 | { 58 | $valueString = (string) $value; 59 | 60 | $this->_setValue($valueString); 61 | 62 | $locale = localeconv(); 63 | 64 | $valueFiltered = str_replace($locale['thousands_sep'], '', $valueString); 65 | $valueFiltered = str_replace($locale['decimal_point'], '.', $valueFiltered); 66 | 67 | if (strval(floatval($valueFiltered)) != $valueFiltered) { 68 | $this->_error(); 69 | return false; 70 | } 71 | 72 | return true; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /indexer.php: -------------------------------------------------------------------------------- 1 | new Indexer_Popgo(), 17 | 'dmhy' => new Indexer_DMHY(), 18 | ); 19 | 20 | 21 | foreach ($indexers as $src => $indexer) { 22 | $resources = $indexer->fetch(); 23 | 24 | LOGI(sprintf("从 %s 获取了 %d 个资源", $src, count($resources))); 25 | 26 | /// 将资源丢进数据库 27 | foreach ($resources as $res) { 28 | $title = $mysqli->real_escape_string($res['title']); 29 | $guid = $mysqli->real_escape_string($res['guid']); 30 | $link = $mysqli->real_escape_string($res['link']); 31 | $description = $mysqli->real_escape_string($res['description']); 32 | $pubDate = $res['pubDate']; 33 | $btih = $mysqli->real_escape_string($res['btih']); 34 | $magnet = $mysqli->real_escape_string($res['magnet']); 35 | $src = $mysqli->real_escape_string($src); 36 | 37 | 38 | $ctime = time(); 39 | 40 | $mysqli->query('start transaction'); 41 | 42 | $sql = "SELECT * FROM b_resource WHERE btih='{$btih}' LIMIT 1"; 43 | $result = $mysqli->query($sql); 44 | 45 | if (!$result) { 46 | LOGE("数据库查询失败: " . $mysqli->error); 47 | continue; 48 | } 49 | 50 | if ($result->num_rows > 0) { 51 | LOGI("{$res['title']} 已存在 (因为已存在相同的 btih,btih={$btih}, src={$res['src']})"); 52 | $mysqli->query('rollback'); 53 | 54 | continue; 55 | } 56 | 57 | LOGI("保存来自 {$src} 的数据:{$res['title']}"); 58 | 59 | $sql = "INSERT INTO b_resource(title, guid, link, description, btih, pubDate, src, magnet, ctime) 60 | VALUES('${title}', '${guid}', '{$link}', '{$description}', '{$btih}', ${pubDate}, '{$src}', '{$magnet}', ${ctime})"; 61 | $ret = $mysqli->query($sql); 62 | if ($ret === FALSE) { 63 | LOGE("无法保存数据: " . $mysqli->error . ",原 SQL: " . $sql); 64 | } 65 | 66 | $mysqli->query('commit'); 67 | } 68 | } 69 | 70 | LOGI("索引完成"); 71 | 72 | ?> 73 | -------------------------------------------------------------------------------- /seed.php: -------------------------------------------------------------------------------- 1 | 404 Not Found'); 43 | } 44 | else if ($err == -2) { 45 | /// BTIH 为 {$btih} 的资源不存在 46 | header('HTTP/1.1 404 Not Found'); 47 | die('

404 Not Found

BTIH not exists

'); 48 | } 49 | else if ($err == -3) { 50 | /// 无法获得 BTIH 为 {$btih} 的资源原始种子地址 51 | header('HTTP/1.1 404 Not Found'); 52 | die('

404 Not Found

Could not get source torrent URL

'); 53 | } 54 | else if ($err == -4) { 55 | /// 无法从漫游下载种子({$url}); 56 | header('HTTP/1.1 500 Internal Error'); 57 | die('

500 Internal Error

'); 58 | } 59 | 60 | 61 | /// 2. 检查下载的内容是否是种子,漫游在发生错误的时候仍会返回 HTTP 200,所以我们需要通过 MIME 来进行检查 62 | if (!$path || mime_content_type($path) != 'application/x-bittorrent') { 63 | if ($path) { 64 | unlink($path); 65 | } 66 | 67 | LOGD("从漫游下载到的种子不是合法的 ({$url}):" . curl_error($ch)); 68 | 69 | header('HTTP/1.1 500 Internal Error'); 70 | die('

500 Internal Error

'); 71 | } 72 | 73 | 74 | header("Content-Disposition: attachment; filename={$btih}.torrent"); 75 | header('Content-Type: application/x-bittorrent'); 76 | ob_clean(); 77 | 78 | logDownload($btih); 79 | echo file_get_contents($path); 80 | 81 | ?> 82 | -------------------------------------------------------------------------------- /dht/inc/Base.class.php: -------------------------------------------------------------------------------- 1 | nid, ip2long($node->ip), $node->port); 74 | 75 | return $n; 76 | } 77 | 78 | /** 79 | * 对nodes列表解码 80 | * @param string $msg 要解码的数据 81 | * @return mixed 解码后的数据 82 | */ 83 | static public function decode_nodes($msg){ 84 | // 先判断数据长度是否正确 85 | if((strlen($msg) % 26) != 0) 86 | return array(); 87 | 88 | $n = array(); 89 | 90 | // 每次截取26字节进行解码 91 | foreach(str_split($msg, 26) as $s){ 92 | // 将截取到的字节进行字节序解码 93 | $r = unpack('a20nid/Nip/np', $s); 94 | $n[] = new Node($r['nid'], long2ip($r['ip']), $r['p']); 95 | } 96 | 97 | return $n; 98 | } 99 | } -------------------------------------------------------------------------------- /rss.php: -------------------------------------------------------------------------------- 1 | 0) { 16 | $title = "$kw - $title"; 17 | } 18 | $title = htmlspecialchars($title); 19 | 20 | 21 | $date = date(DATE_RSS); 22 | 23 | header('Content-Type: text/xml'); 24 | ob_clean(); 25 | 26 | echo << 28 | 29 | 30 | {$title} 31 | https://kotomi-rss.moe/ 32 | KOTOMI RSS 资源页 33 | zh-cn 34 | 版权属于原作者所有,本站仅作索引。 35 | ${date} 36 | 37 | EOF; 38 | 39 | 40 | foreach ($result as $res) { 41 | /// 对于漫游,使用 guid 作为资源链接,对于花园,使用 magnet 作为资源链接 42 | $link = $res['guid']; 43 | 44 | $btih = $res['btih']; 45 | if ($USE_LOCAL_SEED == TRUE && $btih != '') { 46 | $link = '/' . btih_seed_url($btih); 47 | $link = mkurl($link); 48 | } 49 | 50 | 51 | /** 52 | * 为向后兼容,对 guid 的特殊处理 53 | * 在 popgo-gone 版本之前,当 $USE_LOCAL_SEED == TRUE 时,对于漫游资源,我们会输出本地种子地址作为 guid 54 | * 而现在,我们会直接使用原始源的 RSS guid 作为 guid,也就是说,如果 USE_LOCAL_SEED == TRUE 时,如果我们不做处理, 55 | * 那么输出的 RSS 中的漫游资源的 guid 会改变,这会导致重复下载。 56 | * 为了解决此问题,在此对 guid 做特殊处理: 57 | * 1. 如果资源是漫游的,则按照旧方法对 guid 进行处理 58 | * 2. 如果资源不是漫游的,则直接输出原始 RSS 中的 guid 59 | */ 60 | $guid = $res['guid']; 61 | if ($res['src'] == 'popgo') { 62 | $guid = $link; 63 | } 64 | 65 | 66 | 67 | /// 统一进行 HTML 转义 68 | foreach ($res as $k => $v) { 69 | if ($k == 'description') { 70 | continue; 71 | } 72 | $res[$k] = htmlspecialchars($v); 73 | } 74 | $link = htmlspecialchars($link); 75 | $guid = htmlspecialchars($guid); 76 | 77 | /// 输出 78 | echo << 80 | {$res['title']} 81 | $guid 82 | {$res['link']} 83 | 84 | 85 | 86 | 87 | EOF; 88 | } 89 | 90 | echo << 92 | 93 | EOF; 94 | ?> 95 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Http/Client/Adapter/Interface.php: -------------------------------------------------------------------------------- 1 | 8 | * @package phpQuery 9 | * @todo implement ArrayAccess ? 10 | */ 11 | class DOMEvent { 12 | /** 13 | * Returns a boolean indicating whether the event bubbles up through the DOM or not. 14 | * 15 | * @var unknown_type 16 | */ 17 | public $bubbles = true; 18 | /** 19 | * Returns a boolean indicating whether the event is cancelable. 20 | * 21 | * @var unknown_type 22 | */ 23 | public $cancelable = true; 24 | /** 25 | * Returns a reference to the currently registered target for the event. 26 | * 27 | * @var unknown_type 28 | */ 29 | public $currentTarget; 30 | /** 31 | * Returns detail about the event, depending on the type of event. 32 | * 33 | * @var unknown_type 34 | * @link http://developer.mozilla.org/en/DOM/event.detail 35 | */ 36 | public $detail; // ??? 37 | /** 38 | * Used to indicate which phase of the event flow is currently being evaluated. 39 | * 40 | * NOT IMPLEMENTED 41 | * 42 | * @var unknown_type 43 | * @link http://developer.mozilla.org/en/DOM/event.eventPhase 44 | */ 45 | public $eventPhase; // ??? 46 | /** 47 | * The explicit original target of the event (Mozilla-specific). 48 | * 49 | * NOT IMPLEMENTED 50 | * 51 | * @var unknown_type 52 | */ 53 | public $explicitOriginalTarget; // moz only 54 | /** 55 | * The original target of the event, before any retargetings (Mozilla-specific). 56 | * 57 | * NOT IMPLEMENTED 58 | * 59 | * @var unknown_type 60 | */ 61 | public $originalTarget; // moz only 62 | /** 63 | * Identifies a secondary target for the event. 64 | * 65 | * @var unknown_type 66 | */ 67 | public $relatedTarget; 68 | /** 69 | * Returns a reference to the target to which the event was originally dispatched. 70 | * 71 | * @var unknown_type 72 | */ 73 | public $target; 74 | /** 75 | * Returns the time that the event was created. 76 | * 77 | * @var unknown_type 78 | */ 79 | public $timeStamp; 80 | /** 81 | * Returns the name of the event (case-insensitive). 82 | */ 83 | public $type; 84 | public $runDefault = true; 85 | public $data = null; 86 | public function __construct($data) { 87 | foreach($data as $k => $v) { 88 | $this->$k = $v; 89 | } 90 | if (! $this->timeStamp) 91 | $this->timeStamp = time(); 92 | } 93 | /** 94 | * Cancels the event (if it is cancelable). 95 | * 96 | */ 97 | public function preventDefault() { 98 | $this->runDefault = false; 99 | } 100 | /** 101 | * Stops the propagation of events further along in the DOM. 102 | * 103 | */ 104 | public function stopPropagation() { 105 | $this->bubbles = false; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/File/NotExists.php: -------------------------------------------------------------------------------- 1 | "The file '%value%' does exist" 47 | ); 48 | 49 | /** 50 | * Defined by Zend_Validate_Interface 51 | * 52 | * Returns true if and only if the file does not exist in the set destinations 53 | * 54 | * @param string $value Real file to check for 55 | * @param array $file File data from Zend_File_Transfer 56 | * @return boolean 57 | */ 58 | public function isValid($value, $file = null) 59 | { 60 | $directories = $this->getDirectory(true); 61 | if (($file !== null) and (!empty($file['destination']))) { 62 | $directories[] = $file['destination']; 63 | } else if (!isset($file['name'])) { 64 | $file['name'] = $value; 65 | } 66 | 67 | foreach ($directories as $directory) { 68 | if (empty($directory)) { 69 | continue; 70 | } 71 | 72 | $check = true; 73 | if (file_exists($directory . DIRECTORY_SEPARATOR . $file['name'])) { 74 | $this->_throw($file, self::DOES_EXIST); 75 | return false; 76 | } 77 | } 78 | 79 | if (!isset($check)) { 80 | $this->_throw($file, self::DOES_EXIST); 81 | return false; 82 | } 83 | 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/LessThan.php: -------------------------------------------------------------------------------- 1 | "'%value%' is not less than '%max%'" 46 | ); 47 | 48 | /** 49 | * @var array 50 | */ 51 | protected $_messageVariables = array( 52 | 'max' => '_max' 53 | ); 54 | 55 | /** 56 | * Maximum value 57 | * 58 | * @var mixed 59 | */ 60 | protected $_max; 61 | 62 | /** 63 | * Sets validator options 64 | * 65 | * @param mixed $max 66 | * @return void 67 | */ 68 | public function __construct($max) 69 | { 70 | $this->setMax($max); 71 | } 72 | 73 | /** 74 | * Returns the max option 75 | * 76 | * @return mixed 77 | */ 78 | public function getMax() 79 | { 80 | return $this->_max; 81 | } 82 | 83 | /** 84 | * Sets the max option 85 | * 86 | * @param mixed $max 87 | * @return Zend_Validate_LessThan Provides a fluent interface 88 | */ 89 | public function setMax($max) 90 | { 91 | $this->_max = $max; 92 | return $this; 93 | } 94 | 95 | /** 96 | * Defined by Zend_Validate_Interface 97 | * 98 | * Returns true if and only if $value is less than max option 99 | * 100 | * @param mixed $value 101 | * @return boolean 102 | */ 103 | public function isValid($value) 104 | { 105 | $this->_setValue($value); 106 | if ($this->_max <= $value) { 107 | $this->_error(); 108 | return false; 109 | } 110 | return true; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/GreaterThan.php: -------------------------------------------------------------------------------- 1 | "'%value%' is not greater than '%min%'" 46 | ); 47 | 48 | /** 49 | * @var array 50 | */ 51 | protected $_messageVariables = array( 52 | 'min' => '_min' 53 | ); 54 | 55 | /** 56 | * Minimum value 57 | * 58 | * @var mixed 59 | */ 60 | protected $_min; 61 | 62 | /** 63 | * Sets validator options 64 | * 65 | * @param mixed $min 66 | * @return void 67 | */ 68 | public function __construct($min) 69 | { 70 | $this->setMin($min); 71 | } 72 | 73 | /** 74 | * Returns the min option 75 | * 76 | * @return mixed 77 | */ 78 | public function getMin() 79 | { 80 | return $this->_min; 81 | } 82 | 83 | /** 84 | * Sets the min option 85 | * 86 | * @param mixed $min 87 | * @return Zend_Validate_GreaterThan Provides a fluent interface 88 | */ 89 | public function setMin($min) 90 | { 91 | $this->_min = $min; 92 | return $this; 93 | } 94 | 95 | /** 96 | * Defined by Zend_Validate_Interface 97 | * 98 | * Returns true if and only if $value is greater than min option 99 | * 100 | * @param mixed $value 101 | * @return boolean 102 | */ 103 | public function isValid($value) 104 | { 105 | $this->_setValue($value); 106 | 107 | if ($this->_min >= $value) { 108 | $this->_error(); 109 | return false; 110 | } 111 | return true; 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /popgo_helper.php: -------------------------------------------------------------------------------- 1 | query($sql); 23 | if (!$result) { 24 | LOGE('数据库查询出错:' . $mysqli->error); 25 | die(''); 26 | } 27 | 28 | if ($result->num_rows <= 0) { 29 | LOGE('没有需要更新的数据'); 30 | die(''); 31 | } 32 | 33 | $res = $result->fetch_assoc(); 34 | 35 | 36 | 37 | /// 2. 访问漫游资源页面 38 | $r = rand() % 10 + 1; 39 | echo "等待 {$r} 秒后去访问漫游页面:{$res['link']}\n"; 40 | sleep($r); 41 | 42 | $content = NULL; 43 | 44 | $ch = curl_init($res['link']); 45 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 46 | curl_setopt($ch, CURLOPT_ENCODING, ''); 47 | curl_setopt($ch, CURLOPT_USERAGENT, $USER_AGENT); 48 | $content = curl_exec($ch); 49 | 50 | if (!$content) { 51 | LOGE("无法访问漫游资源页面:`${res['link']}'"); 52 | die(''); 53 | } 54 | 55 | 56 | $match = array(); 57 | $ret = preg_match('(magnet([^"]+))', $content, $match); 58 | if (empty($match)) { 59 | LOGE("无法从漫游资源页面(${res['link']})中找到磁力链接,原始内容如下:" . $content); 60 | die(''); 61 | } 62 | 63 | $magnet = $match[0]; 64 | 65 | $magnet = $mysqli->real_escape_string($magnet); 66 | $sql = "UPDATE b_resource SET magnet='{$magnet}' WHERE resource_id={$res['resource_id']}"; 67 | $mysqli->query($sql); 68 | 69 | LOGI("已保存磁力链接: ${magnet}"); 70 | 71 | 72 | /// 3. 下载 BT 种子文件 73 | $r = rand() % 20 + 10; 74 | LOGI("等待 {$r} 秒后去下载种子文件"); 75 | sleep($r); 76 | 77 | 78 | $content = NULL; 79 | 80 | $ch = curl_init($res['guid']); 81 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 82 | curl_setopt($ch, CURLOPT_ENCODING, ''); 83 | curl_setopt($ch, CURLOPT_USERAGENT, $USER_AGENT); 84 | curl_setopt($ch, CURLOPT_REFERER, $res['link']); 85 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 86 | curl_setopt($ch, CURLOPT_MAXREDIRS, 5); 87 | 88 | $content = curl_exec($ch); 89 | 90 | if (!$content) { 91 | LOGE("无法下载种子文件:`${res['guid']}'"); 92 | die(''); 93 | } 94 | 95 | $btih = $res['btih']; 96 | if ($btih == '') { 97 | $match = array(); 98 | preg_match('([0-9a-f]{40})', $res['link'], $match); 99 | $btih = $match[0]; 100 | } 101 | 102 | if ($btih == '') { 103 | LOGE('无法获得种子文件的 BTIH,无法保存种子文件'); 104 | die(''); 105 | } 106 | 107 | archive_torrent($content, $btih); 108 | 109 | LOGI("“{$res['title']}”处理完毕"); 110 | 111 | ?> 112 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Digits.php: -------------------------------------------------------------------------------- 1 | "'%value%' contains not only digit characters", 62 | self::STRING_EMPTY => "'%value%' is an empty string" 63 | ); 64 | 65 | /** 66 | * Defined by Zend_Validate_Interface 67 | * 68 | * Returns true if and only if $value only contains digit characters 69 | * 70 | * @param string $value 71 | * @return boolean 72 | */ 73 | public function isValid($value) 74 | { 75 | $valueString = (string) $value; 76 | 77 | $this->_setValue($valueString); 78 | 79 | if ('' === $valueString) { 80 | $this->_error(self::STRING_EMPTY); 81 | return false; 82 | } 83 | 84 | if (null === self::$_filter) { 85 | /** 86 | * @see Zend_Filter_Digits 87 | */ 88 | require_once 'Zend/Filter/Digits.php'; 89 | self::$_filter = new Zend_Filter_Digits(); 90 | } 91 | 92 | if ($valueString !== self::$_filter->filter($valueString)) { 93 | $this->_error(self::NOT_DIGITS); 94 | return false; 95 | } 96 | 97 | return true; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Barcode/UpcA.php: -------------------------------------------------------------------------------- 1 | "'%value%' is an invalid UPC-A barcode", 57 | self::INVALID_LENGTH => "'%value%' should be 12 characters", 58 | ); 59 | 60 | /** 61 | * Defined by Zend_Validate_Interface 62 | * 63 | * Returns true if and only if $value contains a valid barcode 64 | * 65 | * @param string $value 66 | * @return boolean 67 | */ 68 | public function isValid($value) 69 | { 70 | $valueString = (string) $value; 71 | $this->_setValue($valueString); 72 | 73 | if (strlen($valueString) !== 12) { 74 | $this->_error(self::INVALID_LENGTH); 75 | return false; 76 | } 77 | 78 | $barcode = substr($valueString, 0, -1); 79 | $oddSum = 0; 80 | $evenSum = 0; 81 | 82 | for ($i = 0; $i < 11; $i++) { 83 | if ($i % 2 === 0) { 84 | $oddSum += $barcode[$i] * 3; 85 | } elseif ($i % 2 === 1) { 86 | $evenSum += $barcode[$i]; 87 | } 88 | } 89 | 90 | $calculation = ($oddSum + $evenSum) % 10; 91 | $checksum = ($calculation === 0) ? 0 : 10 - $calculation; 92 | 93 | if ($valueString[11] != $checksum) { 94 | $this->_error(self::INVALID); 95 | return false; 96 | } 97 | 98 | return true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Barcode.php: -------------------------------------------------------------------------------- 1 | setType($barcodeType); 55 | } 56 | 57 | /** 58 | * Sets a new barcode validator 59 | * 60 | * @param string $barcodeType - Barcode validator to use 61 | * @return void 62 | * @throws Zend_Validate_Exception 63 | */ 64 | public function setType($barcodeType) 65 | { 66 | switch (strtolower($barcodeType)) { 67 | case 'upc': 68 | case 'upc-a': 69 | $className = 'UpcA'; 70 | break; 71 | case 'ean13': 72 | case 'ean-13': 73 | $className = 'Ean13'; 74 | break; 75 | default: 76 | require_once 'Zend/Validate/Exception.php'; 77 | throw new Zend_Validate_Exception("Barcode type '$barcodeType' is not supported'"); 78 | break; 79 | } 80 | 81 | require_once 'Zend/Validate/Barcode/' . $className . '.php'; 82 | 83 | $class = 'Zend_Validate_Barcode_' . $className; 84 | $this->_barcodeValidator = new $class; 85 | } 86 | 87 | /** 88 | * Defined by Zend_Validate_Interface 89 | * 90 | * Returns true if and only if $value contains a valid barcode 91 | * 92 | * @param string $value 93 | * @return boolean 94 | */ 95 | public function isValid($value) 96 | { 97 | return call_user_func(array($this->_barcodeValidator, 'isValid'), $value); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Barcode/Ean13.php: -------------------------------------------------------------------------------- 1 | "'%value%' is an invalid EAN-13 barcode", 57 | self::INVALID_LENGTH => "'%value%' should be 13 characters", 58 | ); 59 | 60 | /** 61 | * Defined by Zend_Validate_Interface 62 | * 63 | * Returns true if and only if $value contains a valid barcode 64 | * 65 | * @param string $value 66 | * @return boolean 67 | */ 68 | public function isValid($value) 69 | { 70 | $valueString = (string) $value; 71 | $this->_setValue($valueString); 72 | 73 | if (strlen($valueString) !== 13) { 74 | $this->_error(self::INVALID_LENGTH); 75 | return false; 76 | } 77 | 78 | $barcode = strrev(substr($valueString, 0, -1)); 79 | $oddSum = 0; 80 | $evenSum = 0; 81 | 82 | for ($i = 0; $i < 12; $i++) { 83 | if ($i % 2 === 0) { 84 | $oddSum += $barcode[$i] * 3; 85 | } elseif ($i % 2 === 1) { 86 | $evenSum += $barcode[$i]; 87 | } 88 | } 89 | 90 | $calculation = ($oddSum + $evenSum) % 10; 91 | $checksum = ($calculation === 0) ? 0 : 10 - $calculation; 92 | 93 | if ($valueString[12] != $checksum) { 94 | $this->_error(self::INVALID); 95 | return false; 96 | } 97 | 98 | return true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /webkit_crawl.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var sys = require("system"); 4 | var page = require("webpage").create(); 5 | 6 | console.error = function () { 7 | sys.stderr.write(Array.prototype.join.call(arguments, ' ') + '\n'); 8 | }; 9 | console.log = function() { 10 | sys.stderr.write(Array.prototype.join.call(arguments, ' ') + '\n'); 11 | }; 12 | 13 | phantom.onError = function(msg, trace) { 14 | var msgStack = ['PHANTOM ERROR: ' + msg]; 15 | if (trace && trace.length) { 16 | msgStack.push('TRACE:'); 17 | trace.forEach(function(t) { 18 | msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : '')); 19 | }); 20 | } 21 | console.error(msgStack.join('\n')); 22 | phantom.exit(1); 23 | }; 24 | 25 | 26 | if (sys.args.length < 3) { 27 | console.error("Usage: " + sys.args[0] + " [timeout = 120]"); 28 | phantom.exit(-1); 29 | } 30 | 31 | var url = sys.args[1]; 32 | var keyword = sys.args[2]; 33 | var timeout = sys.args.length >= 4 ? sys.args[3] : 120; 34 | 35 | 36 | var url_no_schema = url; 37 | try { 38 | url_no_schema = url.match(/https?\:\/\/(.+)/)[1]; 39 | } 40 | catch (e) { 41 | console.error("无法从 URL `" + url + "' 中提取不带协议前缀的网址"); 42 | phantom.exit(-2); 43 | } 44 | 45 | 46 | setTimeout(function() { 47 | console.log("等待超时,退出(timeout = " + timeout + "s)"); 48 | phantom.exit(-5); 49 | }, timeout * 1000); 50 | 51 | 52 | page.onInitialized = function() { 53 | }; 54 | 55 | page.onLoadStarted = function() { 56 | }; 57 | 58 | page.onLoadFinished = function() { 59 | /// 页面载入完成,解析页面并做处理 60 | /// 只有当前页面的 window.location.href 中包括 url_no_schema,且源码包含关键字的时候,才返回结果 61 | 62 | console.log("页面载入完成: " + Array.prototype.join.call(arguments, ' ')); 63 | 64 | console.log("开始解析页面"); 65 | var rect = page.evaluate(function() { 66 | return { 67 | href: window.location.href, 68 | innerHTML: document.body.innerHTML 69 | }; 70 | }); 71 | console.log("页面解析完成"); 72 | 73 | 74 | /// 1. 判断 window.location.href 是否包含 url_no_schema 75 | if (rect.href.indexOf(url_no_schema) < 0) { 76 | console.log("当前页面的 URL `" + rect.href + "' 不包含我们需要的网址, 跳过"); 77 | return; 78 | } 79 | 80 | /// 2. 判断 HTML 代码中有没有关键字 81 | if (rect.innerHTML.indexOf(keyword) < 0) { 82 | console.log("当前页面的源码中没有关键字 `" + keyword + "',跳过"); 83 | return; 84 | } 85 | 86 | console.log("在页面中发现了关键字 `" + keyword + "',返回当前页面的源码并退出"); 87 | sys.stdout.write(rect.innerHTML); 88 | 89 | page.close(); 90 | phantom.exit(0); 91 | }; 92 | 93 | page.onUrlChanged = function() { 94 | console.log("URL 改变: " + Array.prototype.join.call(arguments, ' ')); 95 | }; 96 | 97 | page.onNavigationRequested = function() { 98 | }; 99 | 100 | page.onRepaintRequested = function() { 101 | }; 102 | 103 | 104 | page.onClosing = function() { 105 | phantom.exit(0) 106 | }; 107 | 108 | page.onConsoleMessage = function() { 109 | }; 110 | 111 | page.onAlert = function() { 112 | }; 113 | 114 | page.onConfirm = function() { 115 | }; 116 | 117 | page.onPrompt = function() { 118 | }; 119 | 120 | 121 | page.open(url, function() { 122 | }); 123 | -------------------------------------------------------------------------------- /popgo_history_indexer.php: -------------------------------------------------------------------------------- 1 | autocommit(0); 18 | } 19 | 20 | 21 | $cnt_new = 0; 22 | $cnt_count = 0; 23 | 24 | for ($i = 1; file_exists("${popgo_html_archive_dir}/${i}.html"); $i++) { 25 | $path = "${popgo_html_archive_dir}/${i}.html"; 26 | $content = file_get_contents($path); 27 | 28 | if (!$content) { 29 | LOGW("无法读取 `{$path}' 文件的内容,跳过该文件"); 30 | continue; 31 | } 32 | 33 | LOGD("解析文件:`{$path}'"); 34 | 35 | $resources = popgo_parse_html($content); 36 | if (empty($resources)) { 37 | LOGW("无法解析 `{$path}' 文件的内容,跳过该文件"); 38 | continue; 39 | } 40 | 41 | LOGI("`{$path}' 中共有 " . count($resources) . " 个资源"); 42 | $cnt_count += count($resources); 43 | 44 | foreach ($resources as $res) { 45 | LOGI("检查“{$res['title']}”是否已经在数据库中"); 46 | 47 | 48 | /// 1. 检查数据库中是否有同名,且 btih 为空的资源 49 | $title = $mysqli->real_escape_string($res['title']); 50 | $sql = "SELECT COUNT(*) AS cnt FROM b_resource WHERE title='${title}' AND btih=''"; 51 | $result = $mysqli->query($sql); 52 | if (!$result) { 53 | LOGW("数据库查询出错,跳过这个资源:" . $mysqli->error); 54 | continue; 55 | } 56 | $row = $result->fetch_assoc(); 57 | if ($row['cnt'] > 0) { 58 | LOGI("数据库中已经有同名且 btih 为空的资源了,跳过这个资源"); 59 | continue; 60 | } 61 | 62 | 63 | /// 2. 检查数据库中是否存在相同 btih 的资源 64 | $btih = $mysqli->real_escape_string($res['btih']); 65 | $sql = "SELECT COUNT(*) AS cnt FROM b_resource WHERE btih='{$btih}'"; 66 | $result = $mysqli->query($sql); 67 | if (!$result) { 68 | LOGW("数据库查询出错,跳过这个资源:" . $mysqli->error); 69 | continue; 70 | } 71 | $row = $result->fetch_assoc(); 72 | if ($row['cnt'] > 0) { 73 | LOGI("数据库中已经存在相同 btih 的资源了,跳过这个资源"); 74 | continue; 75 | } 76 | 77 | 78 | /// 3. 将这个资源添加到数据库中 79 | LOGI("将“{$title}”保存到数据库中"); 80 | 81 | $guid = $mysqli->real_escape_string($res['guid']); 82 | $link = $mysqli->real_escape_string($res['link']); 83 | $pubDate = (int)$res['pubDate']; 84 | $description = ''; 85 | $ctime = time(); 86 | 87 | $sql = "INSERT INTO b_resource(title, guid, link, description, btih, pubDate, ctime) 88 | VALUES('${title}', '${guid}', '{$link}', '{$description}', '{$btih}', ${pubDate}, ${ctime})"; 89 | $ret = $mysqli->query($sql); 90 | if ($ret === FALSE) { 91 | LOGW("数据库查询出错:" . $mysqli->error); 92 | } 93 | else { 94 | $cnt_new++; 95 | } 96 | 97 | } 98 | } 99 | 100 | if ($DRY_RUN) { 101 | LOGI("目前运行在测试模式,将回滚数据库"); 102 | $mysqli->rollback(); 103 | } 104 | 105 | LOGI("资源索引完成,分析得到 {$cnt_count} 个资源,共新添加了 {$cnt_new} 个资源"); 106 | 107 | ?> 108 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Identical.php: -------------------------------------------------------------------------------- 1 | 'Tokens do not match', 47 | self::MISSING_TOKEN => 'No token was provided to match against', 48 | ); 49 | 50 | /** 51 | * Original token against which to validate 52 | * @var string 53 | */ 54 | protected $_token; 55 | 56 | /** 57 | * Sets validator options 58 | * 59 | * @param string $token 60 | * @return void 61 | */ 62 | public function __construct($token = null) 63 | { 64 | if (null !== $token) { 65 | $this->setToken($token); 66 | } 67 | } 68 | 69 | /** 70 | * Set token against which to compare 71 | * 72 | * @param string $token 73 | * @return Zend_Validate_Identical 74 | */ 75 | public function setToken($token) 76 | { 77 | $this->_token = (string) $token; 78 | return $this; 79 | } 80 | 81 | /** 82 | * Retrieve token 83 | * 84 | * @return string 85 | */ 86 | public function getToken() 87 | { 88 | return $this->_token; 89 | } 90 | 91 | /** 92 | * Defined by Zend_Validate_Interface 93 | * 94 | * Returns true if and only if a token has been set and the provided value 95 | * matches that token. 96 | * 97 | * @param string $value 98 | * @return boolean 99 | */ 100 | public function isValid($value) 101 | { 102 | $this->_setValue($value); 103 | $token = $this->getToken(); 104 | 105 | if (empty($token)) { 106 | $this->_error(self::MISSING_TOKEN); 107 | return false; 108 | } 109 | 110 | if ($value !== $token) { 111 | $this->_error(self::NOT_SAME); 112 | return false; 113 | } 114 | 115 | return true; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /popular-keyword.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 搜索关键字热度排行 - KOTOMI RSS 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | KOTOMI RSS 33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 |
42 | 搜索关键字热度排行 43 |
44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | = 0) { 56 | $popularity = sprintf('%0.3f', round($res['popularity2'], 3)); 57 | } 58 | ?> 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
74 | 75 |
76 | 101 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /lib/base32.php: -------------------------------------------------------------------------------- 1 | '0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7', 22 | 'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15', 23 | 'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23', 24 | 'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31' 25 | ); 26 | 27 | /** 28 | * Use padding false when encoding for urls 29 | * 30 | * @return base32 encoded string 31 | * @author Bryan Ruiz 32 | **/ 33 | public static function encode($input, $padding = true) { 34 | if(empty($input)) return ""; 35 | $input = str_split($input); 36 | $binaryString = ""; 37 | for($i = 0; $i < count($input); $i++) { 38 | $binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT); 39 | } 40 | $fiveBitBinaryArray = str_split($binaryString, 5); 41 | $base32 = ""; 42 | $i=0; 43 | while($i < count($fiveBitBinaryArray)) { 44 | $base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)]; 45 | $i++; 46 | } 47 | if($padding && ($x = strlen($binaryString) % 40) != 0) { 48 | if($x == 8) $base32 .= str_repeat(self::$map[32], 6); 49 | else if($x == 16) $base32 .= str_repeat(self::$map[32], 4); 50 | else if($x == 24) $base32 .= str_repeat(self::$map[32], 3); 51 | else if($x == 32) $base32 .= self::$map[32]; 52 | } 53 | return $base32; 54 | } 55 | 56 | public static function decode($input) { 57 | if(empty($input)) return; 58 | $paddingCharCount = substr_count($input, self::$map[32]); 59 | $allowedValues = array(6,4,3,1,0); 60 | if(!in_array($paddingCharCount, $allowedValues)) return false; 61 | for($i=0; $i<4; $i++){ 62 | if($paddingCharCount == $allowedValues[$i] && 63 | substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false; 64 | } 65 | $input = str_replace('=','', $input); 66 | $input = str_split($input); 67 | $binaryString = ""; 68 | for($i=0; $i < count($input); $i = $i+8) { 69 | $x = ""; 70 | if(!in_array($input[$i], self::$map)) return false; 71 | for($j=0; $j < 8; $j++) { 72 | $x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); 73 | } 74 | $eightBits = str_split($x, 8); 75 | for($z = 0; $z < count($eightBits); $z++) { 76 | $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:""; 77 | } 78 | } 79 | return $binaryString; 80 | } 81 | } 82 | ?> 83 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Ccnum.php: -------------------------------------------------------------------------------- 1 | "'%value%' must contain between 13 and 19 digits", 62 | self::CHECKSUM => "Luhn algorithm (mod-10 checksum) failed on '%value%'" 63 | ); 64 | 65 | /** 66 | * Defined by Zend_Validate_Interface 67 | * 68 | * Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum) 69 | * 70 | * @param string $value 71 | * @return boolean 72 | */ 73 | public function isValid($value) 74 | { 75 | $this->_setValue($value); 76 | 77 | if (null === self::$_filter) { 78 | /** 79 | * @see Zend_Filter_Digits 80 | */ 81 | require_once 'Zend/Filter/Digits.php'; 82 | self::$_filter = new Zend_Filter_Digits(); 83 | } 84 | 85 | $valueFiltered = self::$_filter->filter($value); 86 | 87 | $length = strlen($valueFiltered); 88 | 89 | if ($length < 13 || $length > 19) { 90 | $this->_error(self::LENGTH); 91 | return false; 92 | } 93 | 94 | $sum = 0; 95 | $weight = 2; 96 | 97 | for ($i = $length - 2; $i >= 0; $i--) { 98 | $digit = $weight * $valueFiltered[$i]; 99 | $sum += floor($digit / 10) + $digit % 10; 100 | $weight = $weight % 2 + 1; 101 | } 102 | 103 | if ((10 - $sum % 10) % 10 != $valueFiltered[$length - 1]) { 104 | $this->_error(self::CHECKSUM, $valueFiltered); 105 | return false; 106 | } 107 | 108 | return true; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /popular-resource.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 18 | 19 | 热门资源排行 - KOTOMI RSS 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 | KOTOMI RSS 34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | 42 |
43 | 资源下载热度排行 44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | = 0) { 58 | $popularity = sprintf('%0.3f', round($res['popularity2'], 3)); 59 | } 60 | ?> 61 | 62 | 63 | 64 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
热度资源发布时间最后被下载时间
66 | 67 |
77 |
78 | 79 |
80 | 105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Regex.php: -------------------------------------------------------------------------------- 1 | "'%value%' does not match against pattern '%pattern%'" 46 | ); 47 | 48 | /** 49 | * @var array 50 | */ 51 | protected $_messageVariables = array( 52 | 'pattern' => '_pattern' 53 | ); 54 | 55 | /** 56 | * Regular expression pattern 57 | * 58 | * @var string 59 | */ 60 | protected $_pattern; 61 | 62 | /** 63 | * Sets validator options 64 | * 65 | * @param string $pattern 66 | * @return void 67 | */ 68 | public function __construct($pattern) 69 | { 70 | $this->setPattern($pattern); 71 | } 72 | 73 | /** 74 | * Returns the pattern option 75 | * 76 | * @return string 77 | */ 78 | public function getPattern() 79 | { 80 | return $this->_pattern; 81 | } 82 | 83 | /** 84 | * Sets the pattern option 85 | * 86 | * @param string $pattern 87 | * @return Zend_Validate_Regex Provides a fluent interface 88 | */ 89 | public function setPattern($pattern) 90 | { 91 | $this->_pattern = (string) $pattern; 92 | return $this; 93 | } 94 | 95 | /** 96 | * Defined by Zend_Validate_Interface 97 | * 98 | * Returns true if and only if $value matches against the pattern option 99 | * 100 | * @param string $value 101 | * @throws Zend_Validate_Exception if there is a fatal error in pattern matching 102 | * @return boolean 103 | */ 104 | public function isValid($value) 105 | { 106 | $valueString = (string) $value; 107 | 108 | $this->_setValue($valueString); 109 | 110 | $status = @preg_match($this->_pattern, $valueString); 111 | if (false === $status) { 112 | /** 113 | * @see Zend_Validate_Exception 114 | */ 115 | require_once 'Zend/Validate/Exception.php'; 116 | throw new Zend_Validate_Exception("Internal error matching pattern '$this->_pattern' against value '$valueString'"); 117 | } 118 | if (!$status) { 119 | $this->_error(); 120 | return false; 121 | } 122 | return true; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Alpha.php: -------------------------------------------------------------------------------- 1 | "'%value%' has not only alphabetic characters", 69 | self::STRING_EMPTY => "'%value%' is an empty string" 70 | ); 71 | 72 | /** 73 | * Sets default option values for this instance 74 | * 75 | * @param boolean $allowWhiteSpace 76 | * @return void 77 | */ 78 | public function __construct($allowWhiteSpace = false) 79 | { 80 | $this->allowWhiteSpace = (boolean) $allowWhiteSpace; 81 | } 82 | 83 | /** 84 | * Defined by Zend_Validate_Interface 85 | * 86 | * Returns true if and only if $value contains only alphabetic characters 87 | * 88 | * @param string $value 89 | * @return boolean 90 | */ 91 | public function isValid($value) 92 | { 93 | $valueString = (string) $value; 94 | 95 | $this->_setValue($valueString); 96 | 97 | if ('' === $valueString) { 98 | $this->_error(self::STRING_EMPTY); 99 | return false; 100 | } 101 | 102 | if (null === self::$_filter) { 103 | /** 104 | * @see Zend_Filter_Alpha 105 | */ 106 | require_once 'Zend/Filter/Alpha.php'; 107 | self::$_filter = new Zend_Filter_Alpha(); 108 | } 109 | 110 | self::$_filter->allowWhiteSpace = $this->allowWhiteSpace; 111 | 112 | if ($valueString !== self::$_filter->filter($valueString)) { 113 | $this->_error(self::NOT_ALPHA); 114 | return false; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Alnum.php: -------------------------------------------------------------------------------- 1 | "'%value%' has not only alphabetic and digit characters", 69 | self::STRING_EMPTY => "'%value%' is an empty string" 70 | ); 71 | 72 | /** 73 | * Sets default option values for this instance 74 | * 75 | * @param boolean $allowWhiteSpace 76 | * @return void 77 | */ 78 | public function __construct($allowWhiteSpace = false) 79 | { 80 | $this->allowWhiteSpace = (boolean) $allowWhiteSpace; 81 | } 82 | 83 | /** 84 | * Defined by Zend_Validate_Interface 85 | * 86 | * Returns true if and only if $value contains only alphabetic and digit characters 87 | * 88 | * @param string $value 89 | * @return boolean 90 | */ 91 | public function isValid($value) 92 | { 93 | $valueString = (string) $value; 94 | 95 | $this->_setValue($valueString); 96 | 97 | if ('' === $valueString) { 98 | $this->_error(self::STRING_EMPTY); 99 | return false; 100 | } 101 | 102 | if (null === self::$_filter) { 103 | /** 104 | * @see Zend_Filter_Alnum 105 | */ 106 | require_once 'Zend/Filter/Alnum.php'; 107 | self::$_filter = new Zend_Filter_Alnum(); 108 | } 109 | 110 | self::$_filter->allowWhiteSpace = $this->allowWhiteSpace; 111 | 112 | if ($valueString !== self::$_filter->filter($valueString)) { 113 | $this->_error(self::NOT_ALNUM); 114 | return false; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /indexer_dmhy.php: -------------------------------------------------------------------------------- 1 | _fetch(); 11 | 12 | $ret = array(); 13 | foreach ($rs as $r) { 14 | $match = NULL; 15 | $btih = ''; 16 | preg_match('([0-9A-Z]{32})', $r['enclosure'], $match); 17 | if ($match) { 18 | $btih = hexdump(Base32::decode($match[0])); 19 | } 20 | if ($btih == '') { 21 | LOGW("无法解析资源的 BTIH, r = " . var_export($r, TRUE)); 22 | } 23 | 24 | 25 | 26 | $ret[] = array( 27 | 'btih' => $btih, 28 | 'title' => $r['title'], 29 | 'guid' => $r['guid'], 30 | 'link' => $r['link'], 31 | 'description' => $r['description'], 32 | 'pubDate' => strtotime($r['pubDate']), 33 | 'magnet' => $r['enclosure'], 34 | ); 35 | } 36 | 37 | return $ret; 38 | } 39 | 40 | 41 | static public function getSrcSeedURL($btih) { 42 | global $USER_AGENT; 43 | global $DMHY_FETCH_WORKAROUND; 44 | 45 | 46 | /// 1. 从数据库中查询原始页面链接 47 | $res = get_by_btih($btih); 48 | if (!$res) { 49 | LOGW("BTIH 为 {$btih} 的资源在数据库中不存在"); 50 | return FALSE; 51 | } 52 | 53 | /// 2. 获取 link 页面内容 54 | LOGI("正在获取动漫花园的资源页面内容: ${res['link']}"); 55 | 56 | $content = NULL; 57 | $url = NULL; 58 | 59 | /// 2.1 尝试直接通过 curl 获取 60 | $ch = curl_init($res['link']); 61 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 62 | curl_setopt($ch, CURLOPT_ENCODING, ''); 63 | curl_setopt($ch, CURLOPT_USERAGENT, $USER_AGENT); 64 | $content = curl_exec($ch); 65 | 66 | if (!$content) { 67 | LOGE("无法通过 cURL 抓取动漫花园的资源页面: ${res['link']}'"); 68 | } 69 | else { 70 | $url = self::getTorrentURLFromHTML($content); 71 | LOGD("无法从动漫花园的 HTML 源页面中解析出 torrent 地址"); 72 | } 73 | 74 | if ($url) { 75 | return $url; 76 | } 77 | else if (!$DMHY_FETCH_WORKAROUND) { 78 | return FALSE; 79 | } 80 | 81 | 82 | /// 2.2 尝试通过 webkit 获取 83 | LOGD("由于无法通过 cURL 获取动漫花园的源页面,尝试使用 webkit 获取"); 84 | 85 | set_time_limit(60); 86 | $content = webkit_fetch_url($res['link'], "{$btih}.torrent", 60); 87 | 88 | if (!$content) { 89 | LOGE("无法通过 webkit 抓取动漫花园的资源页面: ${res['link']}'"); 90 | } 91 | else { 92 | $url = self::getTorrentURLFromHTML($content); 93 | 94 | if (!$url) { 95 | LOGD("无法从动漫花园的 HTML 源页面中解析出 torrent 地址"); 96 | } 97 | } 98 | 99 | 100 | return $url; 101 | } 102 | 103 | 104 | /** 105 | * 从 HTML 源代码中获取种子下载地址 106 | * 107 | * @param content HTML 源代码内容 108 | */ 109 | static public function getTorrentURLFromHTML($content) { 110 | $matches = []; 111 | $pattern = '/\/\/.+[a-f0-9]{40}\.torrent/'; 112 | $ret = preg_match($pattern, $content, $matches); 113 | 114 | if ($ret >= 1) { 115 | return 'http:' . $matches[0]; 116 | } 117 | else { 118 | return FALSE; 119 | } 120 | } 121 | } 122 | 123 | ?> 124 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/InArray.php: -------------------------------------------------------------------------------- 1 | "'%value%' was not found in the haystack" 46 | ); 47 | 48 | /** 49 | * Haystack of possible values 50 | * 51 | * @var array 52 | */ 53 | protected $_haystack; 54 | 55 | /** 56 | * Whether a strict in_array() invocation is used 57 | * 58 | * @var boolean 59 | */ 60 | protected $_strict; 61 | 62 | /** 63 | * Sets validator options 64 | * 65 | * @param array $haystack 66 | * @param boolean $strict 67 | * @return void 68 | */ 69 | public function __construct(array $haystack, $strict = false) 70 | { 71 | $this->setHaystack($haystack) 72 | ->setStrict($strict); 73 | } 74 | 75 | /** 76 | * Returns the haystack option 77 | * 78 | * @return mixed 79 | */ 80 | public function getHaystack() 81 | { 82 | return $this->_haystack; 83 | } 84 | 85 | /** 86 | * Sets the haystack option 87 | * 88 | * @param mixed $haystack 89 | * @return Zend_Validate_InArray Provides a fluent interface 90 | */ 91 | public function setHaystack(array $haystack) 92 | { 93 | $this->_haystack = $haystack; 94 | return $this; 95 | } 96 | 97 | /** 98 | * Returns the strict option 99 | * 100 | * @return boolean 101 | */ 102 | public function getStrict() 103 | { 104 | return $this->_strict; 105 | } 106 | 107 | /** 108 | * Sets the strict option 109 | * 110 | * @param boolean $strict 111 | * @return Zend_Validate_InArray Provides a fluent interface 112 | */ 113 | public function setStrict($strict) 114 | { 115 | $this->_strict = $strict; 116 | return $this; 117 | } 118 | 119 | /** 120 | * Defined by Zend_Validate_Interface 121 | * 122 | * Returns true if and only if $value is contained in the haystack option. If the strict 123 | * option is true, then the type of $value is also checked. 124 | * 125 | * @param mixed $value 126 | * @return boolean 127 | */ 128 | public function isValid($value) 129 | { 130 | $this->_setValue($value); 131 | if (!in_array($value, $this->_haystack, $this->_strict)) { 132 | $this->_error(); 133 | return false; 134 | } 135 | return true; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /config.sample.php: -------------------------------------------------------------------------------- 1 | /seed.php?btih={$btih} 48 | * 其中,{$btih} 是一个 40 字节的字符串,仅包含小写英文字母和数字 49 | * 50 | * Nginx 示例: 51 | * rewrite /seed-([a-z0-9]+).torrent /seed.php?btih=$1 52 | */ 53 | $STATIC_SEED_URL = FALSE; 54 | 55 | 56 | /** 57 | * 是否在全站使用静态链接 58 | * 59 | * 如果设定为 TRUE,资源详情页面(desc.php)的链接会被显示为 info-{$BTIH}.html 60 | * 你需要自行设定服务器的 URL 重写规则 61 | * 62 | * Nginx 示例: 63 | * rewrite /info-([a-z0-9]+).html /desc.php?btih=$1 64 | */ 65 | $STATIC_URL = TRUE; 66 | 67 | 68 | /** 69 | * rssowl 存在一个问题,在下载种子时,如果服务器发送了 Location HTTP 头要求进行重定向(HTTP 301/302 重定向),那么 rssowl 将无法正确识别下载文件的文件名。 70 | * 如果没有开启静态种子下载地址功能($STATIC_SEED_URL == TRUE),那么 rssowl 会将下载回来的种子文件命名为 seed.php,在批量下载时,后下载的文件会覆盖先前下载的文件,导致无法完整下载。 71 | * 开启此选项后,对 RSSOWL 的下载请求将由 PHP 进行处理,这可以保证 rssowl 正确识别下载文件的名字,但会增加服务器负担。 72 | * 如果开启了静态种子下载地址功能,则可以关闭此选项。 73 | * 74 | * 仅 $USE_LOCAL_SEED == TRUE 时该配置项才有效。 75 | * 76 | * 如果你不理解上述说明,或者你不知道你在做什么,请保持此开关开启 77 | */ 78 | $RSSOWL_WORKAROUND = TRUE; 79 | 80 | /** 81 | * 是否过滤比特彗星(BitComet)的提示升级文件。 82 | * 比特彗星在创建种子时,会在种子文件中添加一些没有用的共享文件,这些没有用的共享文件的文件名是一些提醒用户升级软件的信息,比特彗星借助此手段提醒用户更新软件,以便使用比特彗星自创的“文件按分块大小对齐”功能。 83 | * 这些文件的文件名类似这样:_____padding_file_9_如果您看到此文件,请升级到BitComet(比特彗星)0.85或以上版本____.!mv 84 | * 如果关闭此开关,那么资源详情页面中的种子文件列表中就会将比特彗星的升级提醒文件一并显示出来,建议保持此开关开启。 85 | * 86 | * 在此顺便强烈鄙视一下比特彗星这种损人利己的行为,比特彗星真是不愧对“国产软件”(Liu Mang Ruan Jian)的名头。 87 | */ 88 | $FILTER_BITCOMET_PADDING_FILE = TRUE; 89 | 90 | 91 | /** 92 | * 搜索时是否使用全文索引。 93 | * 如果设为 TRUE,则你必须手动为 MySQL 配置全文索引,如果你不了解 MySQL 的全文索引,请不要打开此选项。(提醒:MySQL 5.7.6 以前的版本的全文索引不支持中文) 94 | * 如果使用全文索引,需要在 title 字段上建立全文索引 95 | */ 96 | $USE_FULLTEXT = TRUE; 97 | 98 | 99 | /** 100 | * 资源热度半衰期 101 | * 102 | * 考虑到新番一般是每周一播,将半衰期设为 7,那么上一周的下载贡献的热度正好小于 0.5,四舍五入后正好为 0,意为上周下载对资源热度的贡献正好变为 0 103 | */ 104 | $POPULARITY_HALFLIFE_DAYS = 7; 105 | 106 | 107 | 108 | /** 109 | * 小提示,可以在首页搜索框中随机显示一些小提示,如果不需要显示小提示,请留空。 110 | * 小提示只能是纯文本,不支持 HTML 111 | * 如果你想让某个提示出现的次数更多一些,可以把这条提示复制几次 112 | */ 113 | $TIPS = array( 114 | '小提示:在 RSS 源的链接中增加 limit 参数可以控制 RSS 输出条目的数量', 115 | '输入关键词', 116 | '输入关键词', 117 | '输入关键词', 118 | '输入关键词', 119 | ); 120 | 121 | 122 | /** 123 | * 是否使用 webkit 去抓取动漫花园的网页地址。 124 | * 动漫花园使用了 CloudFare 的 DDoS 保护机制,我们无法直接使用程序抓取动漫花园的资源页。 125 | * 为了绕开这个保护,我们需要模拟浏览器的行为,如果开启此选项,则将使用 webkit 去抓取动漫花园的资源页内容。 126 | * 注意:开启此选项需要安装 nodejs 的 phantomjs 模块。要检查安装是否成功,请在本文件目录下执行下面的命令,看看是否能够抓取到网页: 127 | * 128 | * phantomjs webkit_crawl.js http://www.baidu.com baidu.com 129 | * 130 | * 如果能看到百度的源代码,则说明抓取成功。 131 | */ 132 | $DMHY_FETCH_WORKAROUND = TRUE; 133 | 134 | 135 | /** 136 | * 如果使用 webkit 去抓取动漫花园的网页地址,需要定义 phantomjs 的路径。 137 | * 如果不知道 phantomjs 安装在哪里,可以先执行一次 phantomjs 命令,然后再执行 hash 命令,这样就可以看到 phantomjs 的路径了。 138 | */ 139 | $PHANTOMJS_PATH = '/usr/local/bin/phantomjs'; 140 | 141 | ?> 142 | -------------------------------------------------------------------------------- /desc.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | real_escape_string($btih); 14 | $result = $mysqli->query("SELECT * FROM b_resource WHERE btih='{$btih_qs}'"); 15 | 16 | if (!$result) { 17 | die($mysqli->error()); 18 | } 19 | $res = $result->fetch_assoc(); 20 | if (!$res) { 21 | die('资源不存在'); 22 | } 23 | 24 | $TIPS[] = '输入关键词'; 25 | shuffle($TIPS); 26 | $tip = array_pop($TIPS); 27 | ?> 28 | 29 | 30 | 31 | 32 | <?php echo htmlspecialchars($res['title']);?> - KOTOMI RSS 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 |
48 |
49 | KOTOMI RSS 50 |
51 |
52 | Anime RSS 索引站,将你的搜索结果订阅为 RSS 源 53 |
54 |
55 |
56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 |

内容介绍

71 | 76 |
77 |
78 | 79 | 80 |
81 |
82 |

资源信息

83 | 84 | = 0) { 90 | $decays = (time() - $res['pmtime']) / 86400 / $POPULARITY_HALFLIFE_DAYS; 91 | $popularity = round($res['popularity'] * pow(2, -1 * $decays)); 92 | } 93 | ?> 94 |

资源来源:

95 |

索引建立时间:

96 |

热门程度:

97 |

种子地址(BT):

98 |

磁力链接(magnet):

99 |
100 |
101 | 102 |
103 |
104 |

文件列表

105 |
正在加载文件列表…
106 | 107 | 108 | 109 |
110 |
111 |
112 | 113 | 114 | 115 |
116 | 117 | 118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /db_definition.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Database: `rssindexer` 3 | -- 4 | 5 | -- -------------------------------------------------------- 6 | 7 | -- 8 | -- Table structure for table `b_dht_log` 9 | -- 10 | 11 | CREATE TABLE `b_dht_log` ( 12 | `log_id` int(10) UNSIGNED NOT NULL, 13 | `node_id` binary(20) NOT NULL, 14 | `btih` binary(20) NOT NULL, 15 | `ctime` int(11) NOT NULL 16 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 17 | 18 | -- -------------------------------------------------------- 19 | 20 | -- 21 | -- Table structure for table `b_download_log` 22 | -- 23 | 24 | CREATE TABLE `b_download_log` ( 25 | `log_id` bigint(20) NOT NULL, 26 | `btih` binary(20) NOT NULL, 27 | `ctime` int(11) NOT NULL, 28 | `ip` varchar(15) COLLATE utf8_general_ci NOT NULL, 29 | `useragent` varchar(4096) COLLATE utf8_general_ci NOT NULL 30 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='种子下载记录表'; 31 | 32 | -- -------------------------------------------------------- 33 | 34 | -- 35 | -- Table structure for table `b_keyword_log` 36 | -- 37 | 38 | CREATE TABLE `b_keyword_log` ( 39 | `log_id` bigint(20) NOT NULL, 40 | `ip` varchar(15) COLLATE utf8_general_ci NOT NULL, 41 | `kw` varchar(256) COLLATE utf8_general_ci NOT NULL, 42 | `ctime` int(11) NOT NULL, 43 | `useragent` varchar(4096) COLLATE utf8_general_ci NOT NULL 44 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='记录用户搜索的关键词'; 45 | 46 | -- -------------------------------------------------------- 47 | 48 | -- 49 | -- Table structure for table `b_keyword_popularity` 50 | -- 51 | 52 | CREATE TABLE `b_keyword_popularity` ( 53 | `popularity_id` int(11) NOT NULL, 54 | `kw` varchar(256) COLLATE utf8_general_ci NOT NULL, 55 | `popularity` double NOT NULL, 56 | `pmtime` int(11) NOT NULL 57 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='关键词热度表'; 58 | 59 | -- -------------------------------------------------------- 60 | 61 | -- 62 | -- Table structure for table `b_resource` 63 | -- 64 | 65 | CREATE TABLE `b_resource` ( 66 | `resource_id` int(11) NOT NULL, 67 | `title` text NOT NULL, 68 | `guid` varchar(4096) NOT NULL, 69 | `link` varchar(4096) NOT NULL, 70 | `description` text NOT NULL, 71 | `magnet` varchar(4096) NOT NULL DEFAULT '', 72 | `btih` char(40) NOT NULL DEFAULT '', 73 | `src` enum('popgo','dmhy','','') NOT NULL DEFAULT '' COMMENT '资源来源', 74 | `pubDate` int(11) NOT NULL, 75 | `ctime` int(11) NOT NULL, 76 | `popularity` float NOT NULL DEFAULT '-1' COMMENT '热门程度缓存', 77 | `pmtime` int(11) NOT NULL DEFAULT '0' 78 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 79 | 80 | -- 81 | -- Indexes for dumped tables 82 | -- 83 | 84 | -- 85 | -- Indexes for table `b_dht_log` 86 | -- 87 | ALTER TABLE `b_dht_log` 88 | ADD PRIMARY KEY (`log_id`), 89 | ADD KEY `btih` (`btih`,`ctime`) USING BTREE, 90 | ADD KEY `ctime` (`ctime`); 91 | 92 | -- 93 | -- Indexes for table `b_download_log` 94 | -- 95 | ALTER TABLE `b_download_log` 96 | ADD PRIMARY KEY (`log_id`), 97 | ADD KEY `btih` (`btih`,`ctime`) USING BTREE; 98 | 99 | -- 100 | -- Indexes for table `b_keyword_log` 101 | -- 102 | ALTER TABLE `b_keyword_log` 103 | ADD PRIMARY KEY (`log_id`), 104 | ADD KEY `kw` (`kw`(255)); 105 | 106 | -- 107 | -- Indexes for table `b_keyword_popularity` 108 | -- 109 | ALTER TABLE `b_keyword_popularity` 110 | ADD PRIMARY KEY (`popularity_id`), 111 | ADD UNIQUE `kw` (`kw`(255)) USING HASH, 112 | ADD KEY `popularity` (`popularity`,`pmtime`) USING BTREE; 113 | 114 | -- 115 | -- Indexes for table `b_resource` 116 | -- 117 | ALTER TABLE `b_resource` 118 | ADD PRIMARY KEY (`resource_id`), 119 | ADD UNIQUE KEY `btih` (`btih`) USING BTREE, 120 | ADD KEY `guid` (`guid`(255)), 121 | ADD KEY `link` (`link`(255)), 122 | ADD KEY `magnet` (`magnet`(255)), 123 | ADD KEY `pubDate` (`pubDate`); 124 | 125 | -- 126 | -- AUTO_INCREMENT for dumped tables 127 | -- 128 | 129 | -- 130 | -- AUTO_INCREMENT for table `b_dht_log` 131 | -- 132 | ALTER TABLE `b_dht_log` 133 | MODIFY `log_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; 134 | -- 135 | -- AUTO_INCREMENT for table `b_download_log` 136 | -- 137 | ALTER TABLE `b_download_log` 138 | MODIFY `log_id` bigint(20) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; 139 | -- 140 | -- AUTO_INCREMENT for table `b_keyword_log` 141 | -- 142 | ALTER TABLE `b_keyword_log` 143 | MODIFY `log_id` bigint(20) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; 144 | -- 145 | -- AUTO_INCREMENT for table `b_keyword_popularity` 146 | -- 147 | ALTER TABLE `b_keyword_popularity` 148 | MODIFY `popularity_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; 149 | -- 150 | -- AUTO_INCREMENT for table `b_resource` 151 | -- 152 | ALTER TABLE `b_resource` 153 | MODIFY `resource_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; 154 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Callback.php: -------------------------------------------------------------------------------- 1 | 25 | * 26 | * @TODO??? return fake forwarding function created via create_function 27 | * @TODO honor paramStructure 28 | */ 29 | class Callback 30 | implements ICallbackNamed { 31 | public $callback = null; 32 | public $params = null; 33 | protected $name; 34 | public function __construct($callback, $param1 = null, $param2 = null, 35 | $param3 = null) { 36 | $params = func_get_args(); 37 | $params = array_slice($params, 1); 38 | if ($callback instanceof Callback) { 39 | // TODO implement recurention 40 | } else { 41 | $this->callback = $callback; 42 | $this->params = $params; 43 | } 44 | } 45 | public function getName() { 46 | return 'Callback: '.$this->name; 47 | } 48 | public function hasName() { 49 | return isset($this->name) && $this->name; 50 | } 51 | public function setName($name) { 52 | $this->name = $name; 53 | return $this; 54 | } 55 | // TODO test me 56 | // public function addParams() { 57 | // $params = func_get_args(); 58 | // return new Callback($this->callback, $this->params+$params); 59 | // } 60 | } 61 | /** 62 | * Shorthand for new Callback(create_function(...), ...); 63 | * 64 | * @author Tobiasz Cudnik 65 | */ 66 | class CallbackBody extends Callback { 67 | public function __construct($paramList, $code, $param1 = null, $param2 = null, 68 | $param3 = null) { 69 | $params = func_get_args(); 70 | $params = array_slice($params, 2); 71 | $this->callback = create_function($paramList, $code); 72 | $this->params = $params; 73 | } 74 | } 75 | /** 76 | * Callback type which on execution returns reference passed during creation. 77 | * 78 | * @author Tobiasz Cudnik 79 | */ 80 | class CallbackReturnReference extends Callback 81 | implements ICallbackNamed { 82 | protected $reference; 83 | public function __construct(&$reference, $name = null){ 84 | $this->reference =& $reference; 85 | $this->callback = array($this, 'callback'); 86 | } 87 | public function callback() { 88 | return $this->reference; 89 | } 90 | public function getName() { 91 | return 'Callback: '.$this->name; 92 | } 93 | public function hasName() { 94 | return isset($this->name) && $this->name; 95 | } 96 | } 97 | /** 98 | * Callback type which on execution returns value passed during creation. 99 | * 100 | * @author Tobiasz Cudnik 101 | */ 102 | class CallbackReturnValue extends Callback 103 | implements ICallbackNamed { 104 | protected $value; 105 | protected $name; 106 | public function __construct($value, $name = null){ 107 | $this->value =& $value; 108 | $this->name = $name; 109 | $this->callback = array($this, 'callback'); 110 | } 111 | public function callback() { 112 | return $this->value; 113 | } 114 | public function __toString() { 115 | return $this->getName(); 116 | } 117 | public function getName() { 118 | return 'Callback: '.$this->name; 119 | } 120 | public function hasName() { 121 | return isset($this->name) && $this->name; 122 | } 123 | } 124 | /** 125 | * CallbackParameterToReference can be used when we don't really want a callback, 126 | * only parameter passed to it. CallbackParameterToReference takes first 127 | * parameter's value and passes it to reference. 128 | * 129 | * @author Tobiasz Cudnik 130 | */ 131 | class CallbackParameterToReference extends Callback { 132 | /** 133 | * @param $reference 134 | * @TODO implement $paramIndex; 135 | * param index choose which callback param will be passed to reference 136 | */ 137 | public function __construct(&$reference){ 138 | $this->callback =& $reference; 139 | } 140 | } 141 | //class CallbackReference extends Callback { 142 | // /** 143 | // * 144 | // * @param $reference 145 | // * @param $paramIndex 146 | // * @todo implement $paramIndex; param index choose which callback param will be passed to reference 147 | // */ 148 | // public function __construct(&$reference, $name = null){ 149 | // $this->callback =& $reference; 150 | // } 151 | //} 152 | class CallbackParam {} -------------------------------------------------------------------------------- /lib/lightbenc.php: -------------------------------------------------------------------------------- 1 | 21 | array(1) { 22 | [0]=> 23 | string(8) "a string" 24 | } 25 | ["oneInteger"]=> 26 | int(34) 27 | ["isDct"]=> 28 | bool(true) 29 | } 30 | 31 | The returned value is a nested data type with the following type of elements: 32 | - ints (test type with is_integer($x)) 33 | - strings (test type with is_string($x)) 34 | - lists (test type with is_array($x) && !isset($x[isDct]) 35 | - dicts (test type with is_array($x) && isset($x[isDct]) 36 | 37 | All elements have the native PHP type, except for the dictionary which is an array with an "isDct" key. 38 | This is necessary since PHP makes no distinction between flat and associative arrays. Note that the isDct 39 | key is allways set as a bool, so that even if the dictionary contains an actual "isDct" value, the 40 | functions behave transparently, i.e. they don't strip out or overwrite actual "isDct" keys. 41 | 42 | As such, this implementation is not a drop-in replacement of the TBDev code, hence the new function names 43 | For all practical purposes, it's just as flexible, and very easy to use. For example: 44 | 45 | // decode the torrent file 46 | $dict= bdecode_file($torrentfilename); 47 | // change announce url 48 | $dict['announce']='http://inferno.demonoid.com'; 49 | // add private tracker flag 50 | $dict['info']['private']=1; 51 | // compute infohash 52 | $infohash = pack("H*", sha1(bencode($dict["info"]))); 53 | // recreate the torrent file 54 | $torrentfile=bencode($dict); 55 | 56 | After calling bencode(), the passed nested array will have all it's dictionaries sorted by key. 57 | The bencoded data generated by bencode() will have sorted dictionaries, but bdecode() does not require 58 | this in the input stream, and will keep the order unchanged. 59 | 60 | This implementation is hereby released under the GFYPL, version 1.00. 61 | 62 | 63 | The Go Fuck Yourself Public License, version 1.00 64 | 65 | Article 1 66 | You can go fuck yourself. 67 | 68 | END OF ALL TERMS AND CONDITIONS 69 | 70 | */ 71 | class lightbenc { 72 | function bdecode($s, &$pos=0) { 73 | if($pos>=strlen($s)) { 74 | return null; 75 | } 76 | switch($s[$pos]){ 77 | case 'd': 78 | $pos++; 79 | $retval=array(); 80 | while ($s[$pos]!='e'){ 81 | $key=self::bdecode($s, $pos); 82 | $val=self::bdecode($s, $pos); 83 | if ($key===null || $val===null) 84 | break; 85 | $retval[$key]=$val; 86 | } 87 | $retval["isDct"]=true; 88 | $pos++; 89 | return $retval; 90 | 91 | case 'l': 92 | $pos++; 93 | $retval=array(); 94 | while ($s[$pos]!='e'){ 95 | $val=self::bdecode($s, $pos); 96 | if ($val===null) 97 | break; 98 | $retval[]=$val; 99 | } 100 | $pos++; 101 | return $retval; 102 | 103 | case 'i': 104 | $pos++; 105 | $digits=strpos($s, 'e', $pos)-$pos; 106 | $val=(int)substr($s, $pos, $digits); 107 | $pos+=$digits+1; 108 | return $val; 109 | 110 | // case "0": case "1": case "2": case "3": case "4": 111 | // case "5": case "6": case "7": case "8": case "9": 112 | default: 113 | $digits=strpos($s, ':', $pos)-$pos; 114 | if ($digits<0 || $digits >20) 115 | return null; 116 | $len=(int)substr($s, $pos, $digits); 117 | $pos+=$digits+1; 118 | $str=substr($s, $pos, $len); 119 | $pos+=$len; 120 | //echo "pos: $pos str: [$str] len: $len digits: $digits\n"; 121 | return (string)$str; 122 | } 123 | return null; 124 | } 125 | 126 | function bencode(&$d){ 127 | if(is_array($d)){ 128 | $ret="l"; 129 | if($d["isDct"]){ 130 | $isDict=1; 131 | $ret="d"; 132 | // this is required by the specs, and BitTornado actualy chokes on unsorted dictionaries 133 | ksort($d, SORT_STRING); 134 | } 135 | foreach($d as $key=>$value) { 136 | if($isDict){ 137 | // skip the isDct element, only if it's set by us 138 | if($key=="isDct" and is_bool($value)) continue; 139 | $ret.=strlen($key).":".$key; 140 | } 141 | if (is_string($value)) { 142 | $ret.=strlen($value).":".$value; 143 | } elseif (is_int($value)){ 144 | $ret.="i${value}e"; 145 | } else { 146 | $ret.=self::bencode ($value); 147 | } 148 | } 149 | return $ret."e"; 150 | } elseif (is_string($d)) // fallback if we're given a single bencoded string or int 151 | return strlen($d).":".$d; 152 | elseif (is_int($d)) 153 | return "i${d}e"; 154 | else 155 | return null; 156 | } 157 | 158 | function bdecode_file($filename){ 159 | $f=file_get_contents($filename, FILE_BINARY); 160 | return bdecode($f); 161 | } 162 | } 163 | ?> 164 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/File/FilesSize.php: -------------------------------------------------------------------------------- 1 | "The files in sum exceed the maximum allowed size", 49 | self::TOO_SMALL => "All files are in sum smaller than required", 50 | self::NOT_READABLE => "One or more files can not be read" 51 | ); 52 | 53 | /** 54 | * @var array Error message template variables 55 | */ 56 | protected $_messageVariables = array( 57 | 'min' => '_min', 58 | 'max' => '_max' 59 | ); 60 | 61 | /** 62 | * Minimum filesize 63 | * 64 | * @var integer 65 | */ 66 | protected $_min; 67 | 68 | /** 69 | * Maximum filesize 70 | * 71 | * @var integer|null 72 | */ 73 | protected $_max; 74 | 75 | /** 76 | * Internal file array 77 | * 78 | * @var array 79 | */ 80 | protected $_files; 81 | 82 | /** 83 | * Internal file size counter 84 | * 85 | * @var integer 86 | */ 87 | protected $_size; 88 | 89 | /** 90 | * Sets validator options 91 | * 92 | * Min limits the used diskspace for all files, when used with max=null it is the maximum filesize 93 | * It also accepts an array with the keys 'min' and 'max' 94 | * 95 | * @param integer|array $min Minimum diskspace for all files 96 | * @param integer $max Maximum diskspace for all files 97 | * @return void 98 | */ 99 | public function __construct($min, $max = null) 100 | { 101 | $this->_files = array(); 102 | $this->_size = 0; 103 | parent::__construct($min, $max); 104 | } 105 | 106 | /** 107 | * Defined by Zend_Validate_Interface 108 | * 109 | * Returns true if and only if the disk usage of all files is at least min and 110 | * not bigger than max (when max is not null). 111 | * 112 | * @param string|array $value Real file to check for size 113 | * @param array $file File data from Zend_File_Transfer 114 | * @return boolean 115 | */ 116 | public function isValid($value, $file = null) 117 | { 118 | if (is_string($value)) { 119 | $value = array($value); 120 | } 121 | 122 | foreach ($value as $files) { 123 | // Is file readable ? 124 | if (!@is_readable($files)) { 125 | $this->_throw($file, self::NOT_READABLE); 126 | return false; 127 | } 128 | 129 | if (!isset($this->_files[$files])) { 130 | $this->_files[$files] = $files; 131 | } else { 132 | // file already counted... do not count twice 133 | continue; 134 | } 135 | 136 | // limited to 2GB files 137 | $size = @filesize($files); 138 | $this->_size += $size; 139 | $this->_setValue($this->_size); 140 | if (($this->_max !== null) && ($this->_max < $this->_size)) { 141 | $this->_throw($file, self::TOO_BIG); 142 | } 143 | } 144 | 145 | // Check that aggregate files are >= minimum size 146 | if (($this->_min !== null) && ($this->_size < $this->_min)) { 147 | $this->_throw($file, self::TOO_SMALL); 148 | } 149 | 150 | if (count($this->_messages) > 0) { 151 | return false; 152 | } else { 153 | return true; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/StringLength.php: -------------------------------------------------------------------------------- 1 | "'%value%' is less than %min% characters long", 47 | self::TOO_LONG => "'%value%' is greater than %max% characters long" 48 | ); 49 | 50 | /** 51 | * @var array 52 | */ 53 | protected $_messageVariables = array( 54 | 'min' => '_min', 55 | 'max' => '_max' 56 | ); 57 | 58 | /** 59 | * Minimum length 60 | * 61 | * @var integer 62 | */ 63 | protected $_min; 64 | 65 | /** 66 | * Maximum length 67 | * 68 | * If null, there is no maximum length 69 | * 70 | * @var integer|null 71 | */ 72 | protected $_max; 73 | 74 | /** 75 | * Sets validator options 76 | * 77 | * @param integer $min 78 | * @param integer $max 79 | * @return void 80 | */ 81 | public function __construct($min = 0, $max = null) 82 | { 83 | $this->setMin($min); 84 | $this->setMax($max); 85 | } 86 | 87 | /** 88 | * Returns the min option 89 | * 90 | * @return integer 91 | */ 92 | public function getMin() 93 | { 94 | return $this->_min; 95 | } 96 | 97 | /** 98 | * Sets the min option 99 | * 100 | * @param integer $min 101 | * @throws Zend_Validate_Exception 102 | * @return Zend_Validate_StringLength Provides a fluent interface 103 | */ 104 | public function setMin($min) 105 | { 106 | if (null !== $this->_max && $min > $this->_max) { 107 | /** 108 | * @see Zend_Validate_Exception 109 | */ 110 | require_once 'Zend/Validate/Exception.php'; 111 | throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum length, but $min >" 112 | . " $this->_max"); 113 | } 114 | $this->_min = max(0, (integer) $min); 115 | return $this; 116 | } 117 | 118 | /** 119 | * Returns the max option 120 | * 121 | * @return integer|null 122 | */ 123 | public function getMax() 124 | { 125 | return $this->_max; 126 | } 127 | 128 | /** 129 | * Sets the max option 130 | * 131 | * @param integer|null $max 132 | * @throws Zend_Validate_Exception 133 | * @return Zend_Validate_StringLength Provides a fluent interface 134 | */ 135 | public function setMax($max) 136 | { 137 | if (null === $max) { 138 | $this->_max = null; 139 | } else if ($max < $this->_min) { 140 | /** 141 | * @see Zend_Validate_Exception 142 | */ 143 | require_once 'Zend/Validate/Exception.php'; 144 | throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum length, but " 145 | . "$max < $this->_min"); 146 | } else { 147 | $this->_max = (integer) $max; 148 | } 149 | 150 | return $this; 151 | } 152 | 153 | /** 154 | * Defined by Zend_Validate_Interface 155 | * 156 | * Returns true if and only if the string length of $value is at least the min option and 157 | * no greater than the max option (when the max option is not null). 158 | * 159 | * @param string $value 160 | * @return boolean 161 | */ 162 | public function isValid($value) 163 | { 164 | $valueString = (string) $value; 165 | $this->_setValue($valueString); 166 | $length = iconv_strlen($valueString); 167 | if ($length < $this->_min) { 168 | $this->_error(self::TOO_SHORT); 169 | } 170 | if (null !== $this->_max && $this->_max < $length) { 171 | $this->_error(self::TOO_LONG); 172 | } 173 | if (count($this->_messages)) { 174 | return false; 175 | } else { 176 | return true; 177 | } 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/phpQueryEvents.php: -------------------------------------------------------------------------------- 1 | document) 33 | $pq->find('*')->add($pq->document) 34 | ->trigger($type, $data); 35 | } 36 | } else { 37 | if (isset($data[0]) && $data[0] instanceof DOMEvent) { 38 | $event = $data[0]; 39 | $event->relatedTarget = $event->target; 40 | $event->target = $node; 41 | $data = array_slice($data, 1); 42 | } else { 43 | $event = new DOMEvent(array( 44 | 'type' => $type, 45 | 'target' => $node, 46 | 'timeStamp' => time(), 47 | )); 48 | } 49 | $i = 0; 50 | while($node) { 51 | // TODO whois 52 | phpQuery::debug("Triggering ".($i?"bubbled ":'')."event '{$type}' on " 53 | ."node \n");//.phpQueryObject::whois($node)."\n"); 54 | $event->currentTarget = $node; 55 | $eventNode = self::getNode($documentID, $node); 56 | if (isset($eventNode->eventHandlers)) { 57 | foreach($eventNode->eventHandlers as $eventType => $handlers) { 58 | $eventNamespace = null; 59 | if (strpos($type, '.') !== false) 60 | list($eventName, $eventNamespace) = explode('.', $eventType); 61 | else 62 | $eventName = $eventType; 63 | if ($name != $eventName) 64 | continue; 65 | if ($namespace && $eventNamespace && $namespace != $eventNamespace) 66 | continue; 67 | foreach($handlers as $handler) { 68 | phpQuery::debug("Calling event handler\n"); 69 | $event->data = $handler['data'] 70 | ? $handler['data'] 71 | : null; 72 | $params = array_merge(array($event), $data); 73 | $return = phpQuery::callbackRun($handler['callback'], $params); 74 | if ($return === false) { 75 | $event->bubbles = false; 76 | } 77 | } 78 | } 79 | } 80 | // to bubble or not to bubble... 81 | if (! $event->bubbles) 82 | break; 83 | $node = $node->parentNode; 84 | $i++; 85 | } 86 | } 87 | } 88 | /** 89 | * Binds a handler to one or more events (like click) for each matched element. 90 | * Can also bind custom events. 91 | * 92 | * @param DOMNode|phpQueryObject|string $document 93 | * @param unknown_type $type 94 | * @param unknown_type $data Optional 95 | * @param unknown_type $callback 96 | * 97 | * @TODO support '!' (exclusive) events 98 | * @TODO support more than event in $type (space-separated) 99 | * @TODO support binding to global events 100 | */ 101 | public static function add($document, $node, $type, $data, $callback = null) { 102 | phpQuery::debug("Binding '$type' event"); 103 | $documentID = phpQuery::getDocumentID($document); 104 | // if (is_null($callback) && is_callable($data)) { 105 | // $callback = $data; 106 | // $data = null; 107 | // } 108 | $eventNode = self::getNode($documentID, $node); 109 | if (! $eventNode) 110 | $eventNode = self::setNode($documentID, $node); 111 | if (!isset($eventNode->eventHandlers[$type])) 112 | $eventNode->eventHandlers[$type] = array(); 113 | $eventNode->eventHandlers[$type][] = array( 114 | 'callback' => $callback, 115 | 'data' => $data, 116 | ); 117 | } 118 | /** 119 | * Enter description here... 120 | * 121 | * @param DOMNode|phpQueryObject|string $document 122 | * @param unknown_type $type 123 | * @param unknown_type $callback 124 | * 125 | * @TODO namespace events 126 | * @TODO support more than event in $type (space-separated) 127 | */ 128 | public static function remove($document, $node, $type = null, $callback = null) { 129 | $documentID = phpQuery::getDocumentID($document); 130 | $eventNode = self::getNode($documentID, $node); 131 | if (is_object($eventNode) && isset($eventNode->eventHandlers[$type])) { 132 | if ($callback) { 133 | foreach($eventNode->eventHandlers[$type] as $k => $handler) 134 | if ($handler['callback'] == $callback) 135 | unset($eventNode->eventHandlers[$type][$k]); 136 | } else { 137 | unset($eventNode->eventHandlers[$type]); 138 | } 139 | } 140 | } 141 | protected static function getNode($documentID, $node) { 142 | foreach(phpQuery::$documents[$documentID]->eventsNodes as $eventNode) { 143 | if ($node->isSameNode($eventNode)) 144 | return $eventNode; 145 | } 146 | } 147 | protected static function setNode($documentID, $node) { 148 | phpQuery::$documents[$documentID]->eventsNodes[] = $node; 149 | return phpQuery::$documents[$documentID]->eventsNodes[ 150 | count(phpQuery::$documents[$documentID]->eventsNodes)-1 151 | ]; 152 | } 153 | protected static function issetGlobal($documentID, $type) { 154 | return isset(phpQuery::$documents[$documentID]) 155 | ? in_array($type, phpQuery::$documents[$documentID]->eventsGlobal) 156 | : false; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/Between.php: -------------------------------------------------------------------------------- 1 | "'%value%' is not between '%min%' and '%max%', inclusively", 55 | self::NOT_BETWEEN_STRICT => "'%value%' is not strictly between '%min%' and '%max%'" 56 | ); 57 | 58 | /** 59 | * Additional variables available for validation failure messages 60 | * 61 | * @var array 62 | */ 63 | protected $_messageVariables = array( 64 | 'min' => '_min', 65 | 'max' => '_max' 66 | ); 67 | 68 | /** 69 | * Minimum value 70 | * 71 | * @var mixed 72 | */ 73 | protected $_min; 74 | 75 | /** 76 | * Maximum value 77 | * 78 | * @var mixed 79 | */ 80 | protected $_max; 81 | 82 | /** 83 | * Whether to do inclusive comparisons, allowing equivalence to min and/or max 84 | * 85 | * If false, then strict comparisons are done, and the value may equal neither 86 | * the min nor max options 87 | * 88 | * @var boolean 89 | */ 90 | protected $_inclusive; 91 | 92 | /** 93 | * Sets validator options 94 | * 95 | * @param mixed $min 96 | * @param mixed $max 97 | * @param boolean $inclusive 98 | * @return void 99 | */ 100 | public function __construct($min, $max, $inclusive = true) 101 | { 102 | $this->setMin($min) 103 | ->setMax($max) 104 | ->setInclusive($inclusive); 105 | } 106 | 107 | /** 108 | * Returns the min option 109 | * 110 | * @return mixed 111 | */ 112 | public function getMin() 113 | { 114 | return $this->_min; 115 | } 116 | 117 | /** 118 | * Sets the min option 119 | * 120 | * @param mixed $min 121 | * @return Zend_Validate_Between Provides a fluent interface 122 | */ 123 | public function setMin($min) 124 | { 125 | $this->_min = $min; 126 | return $this; 127 | } 128 | 129 | /** 130 | * Returns the max option 131 | * 132 | * @return mixed 133 | */ 134 | public function getMax() 135 | { 136 | return $this->_max; 137 | } 138 | 139 | /** 140 | * Sets the max option 141 | * 142 | * @param mixed $max 143 | * @return Zend_Validate_Between Provides a fluent interface 144 | */ 145 | public function setMax($max) 146 | { 147 | $this->_max = $max; 148 | return $this; 149 | } 150 | 151 | /** 152 | * Returns the inclusive option 153 | * 154 | * @return boolean 155 | */ 156 | public function getInclusive() 157 | { 158 | return $this->_inclusive; 159 | } 160 | 161 | /** 162 | * Sets the inclusive option 163 | * 164 | * @param boolean $inclusive 165 | * @return Zend_Validate_Between Provides a fluent interface 166 | */ 167 | public function setInclusive($inclusive) 168 | { 169 | $this->_inclusive = $inclusive; 170 | return $this; 171 | } 172 | 173 | /** 174 | * Defined by Zend_Validate_Interface 175 | * 176 | * Returns true if and only if $value is between min and max options, inclusively 177 | * if inclusive option is true. 178 | * 179 | * @param mixed $value 180 | * @return boolean 181 | */ 182 | public function isValid($value) 183 | { 184 | $this->_setValue($value); 185 | 186 | if ($this->_inclusive) { 187 | if ($this->_min > $value || $value > $this->_max) { 188 | $this->_error(self::NOT_BETWEEN); 189 | return false; 190 | } 191 | } else { 192 | if ($this->_min >= $value || $value >= $this->_max) { 193 | $this->_error(self::NOT_BETWEEN_STRICT); 194 | return false; 195 | } 196 | } 197 | return true; 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Uri.php: -------------------------------------------------------------------------------- 1 | getUri(); 53 | } 54 | 55 | /** 56 | * Convenience function, checks that a $uri string is well-formed 57 | * by validating it but not returning an object. Returns TRUE if 58 | * $uri is a well-formed URI, or FALSE otherwise. 59 | * 60 | * @param string $uri The URI to check 61 | * @return boolean 62 | */ 63 | public static function check($uri) 64 | { 65 | try { 66 | $uri = self::factory($uri); 67 | } catch (Exception $e) { 68 | return false; 69 | } 70 | 71 | return $uri->valid(); 72 | } 73 | 74 | /** 75 | * Create a new Zend_Uri object for a URI. If building a new URI, then $uri should contain 76 | * only the scheme (http, ftp, etc). Otherwise, supply $uri with the complete URI. 77 | * 78 | * @param string $uri The URI form which a Zend_Uri instance is created 79 | * @throws Zend_Uri_Exception When an empty string was supplied for the scheme 80 | * @throws Zend_Uri_Exception When an illegal scheme is supplied 81 | * @throws Zend_Uri_Exception When the scheme is not supported 82 | * @return Zend_Uri 83 | * @link http://www.faqs.org/rfcs/rfc2396.html 84 | */ 85 | public static function factory($uri = 'http') 86 | { 87 | // Separate the scheme from the scheme-specific parts 88 | $uri = explode(':', $uri, 2); 89 | $scheme = strtolower($uri[0]); 90 | $schemeSpecific = isset($uri[1]) === true ? $uri[1] : ''; 91 | 92 | if (strlen($scheme) === 0) { 93 | require_once 'Zend/Uri/Exception.php'; 94 | throw new Zend_Uri_Exception('An empty string was supplied for the scheme'); 95 | } 96 | 97 | // Security check: $scheme is used to load a class file, so only alphanumerics are allowed. 98 | if (ctype_alnum($scheme) === false) { 99 | require_once 'Zend/Uri/Exception.php'; 100 | throw new Zend_Uri_Exception('Illegal scheme supplied, only alphanumeric characters are permitted'); 101 | } 102 | 103 | /** 104 | * Create a new Zend_Uri object for the $uri. If a subclass of Zend_Uri exists for the 105 | * scheme, return an instance of that class. Otherwise, a Zend_Uri_Exception is thrown. 106 | */ 107 | switch ($scheme) { 108 | case 'http': 109 | // Break intentionally omitted 110 | case 'https': 111 | $className = 'Zend_Uri_Http'; 112 | break; 113 | 114 | case 'mailto': 115 | // TODO 116 | default: 117 | require_once 'Zend/Uri/Exception.php'; 118 | throw new Zend_Uri_Exception("Scheme \"$scheme\" is not supported"); 119 | break; 120 | } 121 | 122 | Zend_Loader::loadClass($className); 123 | $schemeHandler = new $className($scheme, $schemeSpecific); 124 | 125 | return $schemeHandler; 126 | } 127 | 128 | /** 129 | * Get the URI's scheme 130 | * 131 | * @return string|false Scheme or false if no scheme is set. 132 | */ 133 | public function getScheme() 134 | { 135 | if (empty($this->_scheme) === false) { 136 | return $this->_scheme; 137 | } else { 138 | return false; 139 | } 140 | } 141 | 142 | /** 143 | * Zend_Uri and its subclasses cannot be instantiated directly. 144 | * Use Zend_Uri::factory() to return a new Zend_Uri object. 145 | * 146 | * @param string $scheme The scheme of the URI 147 | * @param string $schemeSpecific The scheme-specific part of the URI 148 | */ 149 | abstract protected function __construct($scheme, $schemeSpecific = ''); 150 | 151 | /** 152 | * Return a string representation of this URI. 153 | * 154 | * @return string 155 | */ 156 | abstract public function getUri(); 157 | 158 | /** 159 | * Returns TRUE if this URI is valid, or FALSE otherwise. 160 | * 161 | * @return boolean 162 | */ 163 | abstract public function valid(); 164 | } 165 | -------------------------------------------------------------------------------- /indexer_base.php: -------------------------------------------------------------------------------- 1 | _fetchRss($this->FEED_URL); 27 | if (!$rawrss) { 28 | LOGW("无法获取 RSS 列表"); 29 | return array(); 30 | } 31 | 32 | $rs = $this->rawrss2arr($rawrss); 33 | if (empty($rs)) { 34 | LOGN("没有从 RSS 列表中获取到资源"); 35 | return array(); 36 | } 37 | 38 | return $rs; 39 | 40 | } 41 | 42 | /** 43 | * 下载 RSS 内容 44 | */ 45 | protected function _fetchRss($url) { 46 | global $USER_AGENT; 47 | 48 | LOGI("正在获取 $url"); 49 | 50 | $content = NULL; 51 | 52 | $ch = curl_init($url); 53 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 54 | curl_setopt($ch, CURLOPT_ENCODING, ''); 55 | curl_setopt($ch, CURLOPT_USERAGENT, $USER_AGENT); 56 | $content = curl_exec($ch); 57 | 58 | if (!$content) { 59 | LOGE("无法抓取 RSS:`${RSS_FEED}'"); 60 | return FALSE; 61 | } 62 | 63 | 64 | /// 2. 归档原始数据 65 | LOGI("正在归档数据"); 66 | 67 | /// FIXME: 将 archive_raw 做成类成员函数 68 | archive_raw($content); 69 | 70 | return $content; 71 | } 72 | 73 | 74 | /** 75 | * 将原始的 RSS 内容解析成资源数组 76 | */ 77 | public function rawrss2arr($content) { 78 | $xml = simplexml_load_string($content, NULL, LIBXML_NOCDATA); 79 | if (!$xml) { 80 | return FALSE; 81 | } 82 | 83 | 84 | if (!isset($xml->channel)) { 85 | echo "RSS 资源中没有 channel 字段\n"; 86 | return FALSE; 87 | } 88 | 89 | if (!isset($xml->channel->item)) { 90 | echo "RSS 资源中没有 item 字段\n"; 91 | return FALSE; 92 | } 93 | 94 | $items = array(); 95 | if (!is_array($xml->channel->item)) { 96 | $items = array($xml->channel->item); 97 | } 98 | else { 99 | $items = $xml->channel->item; 100 | } 101 | 102 | 103 | 104 | $ret = array(); 105 | 106 | foreach ($xml->channel->item as $item) { 107 | $enclosure = ''; 108 | if (isset($item->enclosure)) { 109 | $attrs = $item->enclosure->attributes(); 110 | if (isset($attrs['url'])) { 111 | $enclosure = $attrs['url']; 112 | } 113 | } 114 | 115 | $ret[] = array( 116 | 'title' => (string)$item->title, 117 | 'guid' => (string)$item->guid, 118 | 'pubDate' => (string)$item->pubDate, 119 | 'link' => (string)$item->link, 120 | 'description' => (string)$item->description, 121 | 'enclosure' => (string)$enclosure, 122 | ); 123 | 124 | } 125 | 126 | return $ret; 127 | 128 | } 129 | } 130 | 131 | 132 | if (0) { 133 | 134 | /** 135 | * 该文件应该定期使用 PHP-CLI 运行,建议添加到 CRON 任务中 136 | * 137 | * 建议每隔 30 分钟运行一次 138 | * 139 | * 该脚本会自动访问 $RSS_FEED,并将 RSS 内存保存到数据库中 140 | */ 141 | require_once('header.php'); 142 | 143 | /// 3. 解析资源 144 | LOGI("正在解析资源\n"); 145 | 146 | $resources = parse_rss($content); 147 | if (!$resources) { 148 | LOGE('无法解析 RSS 资源:' . $content); 149 | die(''); 150 | } 151 | 152 | LOGI(sprintf("共 %d 个资源", count($resources))); 153 | 154 | 155 | /// 4. 将资源丢进数据库 156 | foreach ($resources as $res) { 157 | 158 | $title = $mysqli->real_escape_string($res['title']); 159 | $guid = $mysqli->real_escape_string($res['guid']); 160 | $link = $mysqli->real_escape_string($res['link']); 161 | $description = $mysqli->real_escape_string($res['description']); 162 | $pubDate = strtotime($res['pubDate']); 163 | 164 | $btih = ''; 165 | $match = array(); 166 | preg_match('([0-9a-f]{40})', $res['link'], $match); 167 | if (!empty($match)) { 168 | $btih = $match[0]; 169 | $btih = $mysqli->real_escape_string($btih); 170 | } 171 | else { 172 | LOGW("警告:无法从 `{$res['link']}' 中解析出 BTIH"); 173 | } 174 | 175 | 176 | $ctime = time(); 177 | 178 | $mysqli->query('start transaction'); 179 | 180 | $sql = "SELECT * FROM b_resource WHERE guid='{$guid}' LIMIT 1"; 181 | $result = $mysqli->query($sql); 182 | 183 | if (!$result) { 184 | LOGE("数据库查询失败: " . $mysqli->error); 185 | continue; 186 | } 187 | 188 | if ($result->num_rows > 0) { 189 | LOGI("{$res['title']} 已存在"); 190 | $mysqli->query('rollback'); 191 | 192 | continue; 193 | } 194 | 195 | LOGI("保存数据:{$res['title']}"); 196 | 197 | $sql = "INSERT INTO b_resource(title, guid, link, description, btih, pubDate, ctime) 198 | VALUES('${title}', '${guid}', '{$link}', '{$description}', '{$btih}', ${pubDate}, ${ctime})"; 199 | $ret = $mysqli->query($sql); 200 | if ($ret === FALSE) { 201 | LOGE("无法保存数据: " . $mysqli->error . ",原 SQL: " . $sql); 202 | } 203 | 204 | $mysqli->query('commit'); 205 | } 206 | 207 | LOGI("索引完成"); 208 | 209 | } 210 | ?> 211 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/File/Exists.php: -------------------------------------------------------------------------------- 1 | "The file '%value%' does not exist" 47 | ); 48 | 49 | /** 50 | * Internal list of directories 51 | * @var string 52 | */ 53 | protected $_directory = ''; 54 | 55 | /** 56 | * @var array Error message template variables 57 | */ 58 | protected $_messageVariables = array( 59 | 'directory' => '_directory' 60 | ); 61 | 62 | /** 63 | * Sets validator options 64 | * 65 | * @param string|array $directory 66 | * @return void 67 | */ 68 | public function __construct($directory = array()) 69 | { 70 | $this->setDirectory($directory); 71 | } 72 | 73 | /** 74 | * Returns the set file directories which are checked 75 | * 76 | * @param boolean $asArray Returns the values as array, when false an concated string is returned 77 | * @return string 78 | */ 79 | public function getDirectory($asArray = false) 80 | { 81 | $asArray = (bool) $asArray; 82 | $directory = (string) $this->_directory; 83 | if ($asArray) { 84 | $directory = explode(',', $directory); 85 | } 86 | 87 | return $directory; 88 | } 89 | 90 | /** 91 | * Sets the file directory which will be checked 92 | * 93 | * @param string|array $directory The directories to validate 94 | * @return Zend_Validate_File_Extension Provides a fluent interface 95 | */ 96 | public function setDirectory($directory) 97 | { 98 | $this->_directory = null; 99 | $this->addDirectory($directory); 100 | return $this; 101 | } 102 | 103 | /** 104 | * Adds the file directory which will be checked 105 | * 106 | * @param string|array $directory The directory to add for validation 107 | * @return Zend_Validate_File_Extension Provides a fluent interface 108 | */ 109 | public function addDirectory($directory) 110 | { 111 | $directories = $this->getDirectory(true); 112 | if (is_string($directory)) { 113 | $directory = explode(',', $directory); 114 | } 115 | 116 | foreach ($directory as $content) { 117 | if (empty($content) || !is_string($content)) { 118 | continue; 119 | } 120 | 121 | $directories[] = trim($content); 122 | } 123 | $directories = array_unique($directories); 124 | 125 | // Sanity check to ensure no empty values 126 | foreach ($directories as $key => $dir) { 127 | if (empty($dir)) { 128 | unset($directories[$key]); 129 | } 130 | } 131 | 132 | $this->_directory = implode(',', $directories); 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * Defined by Zend_Validate_Interface 139 | * 140 | * Returns true if and only if the file already exists in the set directories 141 | * 142 | * @param string $value Real file to check for existance 143 | * @param array $file File data from Zend_File_Transfer 144 | * @return boolean 145 | */ 146 | public function isValid($value, $file = null) 147 | { 148 | $directories = $this->getDirectory(true); 149 | if (($file !== null) and (!empty($file['destination']))) { 150 | $directories[] = $file['destination']; 151 | } else if (!isset($file['name'])) { 152 | $file['name'] = $value; 153 | } 154 | 155 | $check = false; 156 | foreach ($directories as $directory) { 157 | if (empty($directory)) { 158 | continue; 159 | } 160 | 161 | $check = true; 162 | if (!file_exists($directory . DIRECTORY_SEPARATOR . $file['name'])) { 163 | $this->_throw($file, self::DOES_NOT_EXIST); 164 | return false; 165 | } 166 | } 167 | 168 | if (!$check) { 169 | $this->_throw($file, self::DOES_NOT_EXIST); 170 | return false; 171 | } 172 | 173 | return true; 174 | } 175 | 176 | /** 177 | * Throws an error of the given type 178 | * 179 | * @param string $file 180 | * @param string $errorType 181 | * @return false 182 | */ 183 | protected function _throw($file, $errorType) 184 | { 185 | if ($file !== null) { 186 | $this->_value = $file['name']; 187 | } 188 | 189 | $this->_error($errorType); 190 | return false; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /lib/phpQuery/phpQuery/Zend/Validate/File/MimeType.php: -------------------------------------------------------------------------------- 1 | "The file '%value%' has a false mimetype", 46 | self::NOT_DETECTED => "The mimetype of file '%value%' has not been detected", 47 | self::NOT_READABLE => "The file '%value%' can not be read" 48 | ); 49 | 50 | /** 51 | * @var array 52 | */ 53 | protected $_messageVariables = array( 54 | 'mimetype' => '_mimetype' 55 | ); 56 | 57 | /** 58 | * Mimetypes 59 | * 60 | * If null, there is no mimetype 61 | * 62 | * @var string|null 63 | */ 64 | protected $_mimetype; 65 | 66 | /** 67 | * Sets validator options 68 | * 69 | * Mimetype to accept 70 | * 71 | * @param string|array $mimetype MimeType 72 | * @return void 73 | */ 74 | public function __construct($mimetype) 75 | { 76 | $this->setMimeType($mimetype); 77 | } 78 | 79 | /** 80 | * Returns the set mimetypes 81 | * 82 | * @param boolean $asArray Returns the values as array, when false an concated string is returned 83 | * @return integer 84 | */ 85 | public function getMimeType($asArray = false) 86 | { 87 | $asArray = (bool) $asArray; 88 | $mimetype = (string) $this->_mimetype; 89 | if ($asArray) { 90 | $mimetype = explode(',', $mimetype); 91 | } 92 | 93 | return $mimetype; 94 | } 95 | 96 | /** 97 | * Sets the mimetypes 98 | * 99 | * @param string|array $mimetype The mimetypes to validate 100 | * @return Zend_Validate_File_Extension Provides a fluent interface 101 | */ 102 | public function setMimeType($mimetype) 103 | { 104 | $this->_mimetype = null; 105 | $this->addMimeType($mimetype); 106 | return $this; 107 | } 108 | 109 | /** 110 | * Adds the mimetypes 111 | * 112 | * @param string|array $mimetype The mimetypes to add for validation 113 | * @return Zend_Validate_File_Extension Provides a fluent interface 114 | */ 115 | public function addMimeType($mimetype) 116 | { 117 | $mimetypes = $this->getMimeType(true); 118 | if (is_string($mimetype)) { 119 | $mimetype = explode(',', $mimetype); 120 | } 121 | 122 | foreach ($mimetype as $content) { 123 | if (empty($content) || !is_string($content)) { 124 | continue; 125 | } 126 | $mimetypes[] = trim($content); 127 | } 128 | $mimetypes = array_unique($mimetypes); 129 | 130 | // Sanity check to ensure no empty values 131 | foreach ($mimetypes as $key => $mt) { 132 | if (empty($mt)) { 133 | unset($mimetypes[$key]); 134 | } 135 | } 136 | 137 | $this->_mimetype = implode(',', $mimetypes); 138 | 139 | return $this; 140 | } 141 | 142 | /** 143 | * Defined by Zend_Validate_Interface 144 | * 145 | * Returns true if the mimetype of the file matches the given ones. Also parts 146 | * of mimetypes can be checked. If you give for example "image" all image 147 | * mime types will be accepted like "image/gif", "image/jpeg" and so on. 148 | * 149 | * @param string $value Real file to check for mimetype 150 | * @param array $file File data from Zend_File_Transfer 151 | * @return boolean 152 | */ 153 | public function isValid($value, $file = null) 154 | { 155 | // Is file readable ? 156 | if (!@is_readable($value)) { 157 | $this->_throw($file, self::NOT_READABLE); 158 | return false; 159 | } 160 | 161 | if ($file !== null) { 162 | $info['type'] = $file['type']; 163 | } else { 164 | $this->_throw($file, self::NOT_DETECTED); 165 | return false; 166 | } 167 | 168 | $mimetype = $this->getMimeType(true); 169 | if (in_array($info['type'], $mimetype)) { 170 | return true; 171 | } 172 | 173 | foreach($mimetype as $mime) { 174 | $types = explode('/', $info['type']); 175 | if (in_array($mime, $types)) { 176 | return true; 177 | } 178 | } 179 | 180 | $this->_throw($file, self::FALSE_TYPE); 181 | return false; 182 | } 183 | 184 | /** 185 | * Throws an error of the given type 186 | * 187 | * @param string $file 188 | * @param string $errorType 189 | * @return false 190 | */ 191 | protected function _throw($file, $errorType) 192 | { 193 | if ($file !== null) { 194 | $this->_value = $file['name']; 195 | } 196 | 197 | $this->_error($errorType); 198 | return false; 199 | } 200 | } 201 | --------------------------------------------------------------------------------