diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ab4689 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +syntax: glob + +.hg +*.coverage +*.egg-info +*.log +*.pyc +*.db +*.swp +*.swo +*.zip +*.orig +*.cfg +*.tox + +build + +*~ + +fab_settings.py +production_settings.py + +dist +docs/output + +_uploads + +.hgignore diff --git a/code.py b/code.py new file mode 100644 index 0000000..9c81d30 --- /dev/null +++ b/code.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# code.py: based on web.py +# +# author: observer +# email: jingchaohu@gmail.com +# blog: http://obmem.com +# last edit @ 2009.12.23 +import os,sys +import web +from web import form +import sqlite3 +import fetchvc +import time + +web.config.debug = False + +path = os.path.dirname(os.path.realpath(sys.argv[0])) + +db = web.database(dbn='sqlite', db='f:/verycd.sqlite3.db')#db='verycd.sqlite3.db') +#customdb = web.database(dbn='sqlite',db='custom.sqlite3.db') + +conn = sqlite3.connect('custom.sqlite3.db') +conn.text_factory = str + +urls = ( + '/', 'index', + '/add','add', + '/edit','edit', + '/del','del', + '/egg','egg', +) + +render = web.template.render('templates/') + +vform = form.regexp(r".{3,20}$", '资源密码必须是3-20长度的字符串') +vemail = form.regexp(r".*@.*", "必须提供合法的邮件地址") + +add_form = form.Form( + form.Textbox("email",vemail,description="邮件地址"), + form.Textbox("password",vform,description="资源密码"), + form.Textbox("title", description="标题"), + form.Textbox("brief", description="摘要"), + form.Dropdown("category1",args=['电影','剧集','音乐','游戏','动漫','综艺','软件','资料'],value="电影",description="分类"), + form.Textbox("category2",description="子类别"), + form.Textarea("ed2k",value="[格式]\n文件名#地址\n文件名#地址#字幕地址\n",description="资源链接",cols=60,rows=5), + form.Textarea("content",description="资源介绍",cols=60,rows=10), + form.Button("提交", type="submit", description="提交"), +) + +app = web.application(urls, globals()) + +class index: + def GET(self): + i = web.input(id=None,page='1',q=None,download=None,qa=None,cat=None) + hot=open('static/hot.html','r').read() + #显示单个资源 + if i.id: + myvar = dict(id=i.id) + rec = db.select('verycd',vars=myvar,where="verycdid=$id") + for r in rec: + fl = None + if i.download: + links = r['ed2k'].split('`') + links = [ x for x in links if 'ed2k:' in x ] + fl = '
\n'.join(links) + return render.id([r,fl,str(r['verycdid'])]) + return render.error(404) + #显示最新更新的资源 + else: + #深度搜索 + if i.qa: + qa = '+'.join(i.qa.split(' ')) + open(path+'/searchqueue','a').write(qa.encode('utf-8')+'\n') + return render.fin(qa) + #默认情况,不指定分类,没有搜索关键字 + elif (not i.q) and (not i.cat): + vc = db.select('verycd',order='updtime DESC',limit=20,offset=20*(int(i.page)-1)) + num = db.select('verycd',what="count(*) as count")[0].count + arg = '/?page' + #无搜索关键字,指定分类 + elif (not i.q) and (i.cat): + myvar = dict(cat=i.cat) + vc = db.select('verycd',order='updtime DESC',vars=myvar,where='category1=$cat',limit=20,offset=20*(int(i.page)-1)) + num = db.select('verycd',what="count(*) as count",vars=myvar,where='category1=$cat')[0].count + arg = '/?cat='+i.cat+'&page' + #有搜索关键字,指定分类 + elif (i.q) and (i.cat): + qs = i.q.split(' ') + qs = [ 'title like \'%'+x+'%\'' for x in qs ] + where = ' and '.join(qs) + where += ' and category1=\''+i.cat+'\'' + vc = db.select('verycd',order='updtime DESC',limit=20,\ + offset=20*(int(i.page)-1),where=where) + num = db.select('verycd',what="count(*) as count",where=where)[0].count + arg = '/?q='+i.q+'&cat='+i.cat+'&page' + #有搜索关键字,不指定分类 + else: + qs = i.q.split(' ') + qs = [ 'title like \'%'+x+'%\'' for x in qs ] + where = ' and '.join(qs) + vc = db.select('verycd',order='updtime DESC',limit=20,\ + offset=20*(int(i.page)-1),where=where) + num = db.select('verycd',what="count(*) as count",where=where)[0].count + arg = '/?q='+i.q+'&page' + prev = int(i.page)-1 == 0 and '1' or str(int(i.page)-1) + next = int(i.page)+1 <= (num-1)/20+1 and str(int(i.page)+1) or i.page + end = str((num-1)/20+1) + pages = [prev,next,end] + left = min(4,int(i.page)-1) + right = min(4,int(end)-int(i.page)) + if left < 4: + right = min(8-left,int(end)-int(i.page)) + if right < 4: + left = min(8-right,int(i.page)-1) + while left > 0: + pages.append(str(int(i.page)-left)) + left -= 1 + j = 0 + while j <= right: + pages.append(str(int(i.page)+j)) + j += 1 + return render.index([vc,pages,arg,i.q,num,i.cat,hot]) + +class add: + def GET(self): + # do $:f.render() in the template + f = add_form() + return render.add(f) + + def POST(self): + f = add_form() + if not f.validates(): + return render.add(f) + else: + # do whatever is required for registration + now = time.strftime('%Y/%m/%d %H:%M:%S',time.gmtime(time.time()+3600*8)) + c = conn.cursor() + c.execute('insert into custom (title,status,brief,pubtime,updtime,\ + category1,category2,ed2k,content) values(?,?,?,?,?,?,?,?,?)',\ + (f.title.get_value(),'新建',f.brief.get_value(),now,now,\ + f.category1.get_value(),f.category2.get_value(),\ + f.ed2k.get_value(),f.content.get_value())) + c.execute('insert into user (email,password,customid) values (?,?,?)',\ + (f.email.get_value(),f.password.get_value(),c.lastrowid)) + conn.commit() + c.close() + return '...' + +if __name__ == "__main__": + # web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr) + app.run() diff --git a/daemon.py b/daemon.py new file mode 100644 index 0000000..db3c3a6 --- /dev/null +++ b/daemon.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +import sys, os, time, atexit +from signal import SIGTERM + +class Daemon: + """ + A generic daemon class. + + Usage: subclass the Daemon class and override the run() method + """ + def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = pidfile + + def daemonize(self): + """ + do the UNIX double-fork magic, see Stevens' "Advanced + Programming in the UNIX Environment" for details (ISBN 0201563177) + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + """ + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = file(self.stdin, 'r') + so = file(self.stdout, 'a+') + se = file(self.stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # write pidfile + atexit.register(self.delpid) + pid = str(os.getpid()) + file(self.pidfile,'w+').write("%s\n" % pid) + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if pid: + message = "pidfile %s already exist. Daemon already running?\n" + sys.stderr.write(message % self.pidfile) + sys.exit(1) + + # Start the daemon + self.daemonize() + self.run() + + def stop(self): + """ + Stop the daemon + """ + # Get the pid from the pidfile + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if not pid: + message = "pidfile %s does not exist. Daemon not running?\n" + sys.stderr.write(message % self.pidfile) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, SIGTERM) + time.sleep(0.1) + except OSError, err: + err = str(err) + if err.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print str(err) + sys.exit(1) + + def restart(self): + """ + Restart the daemon + """ + self.stop() + self.start() + + def run(self): + """ + You should override this method when you subclass Daemon. It will be called after the process has been + daemonized by start() or restart(). + """ diff --git a/download.py b/download.py new file mode 100644 index 0000000..30af443 --- /dev/null +++ b/download.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# download.py: download with report +# +# author: observer +# email: jingchaohu@gmail.com +# blog: http://obmem.com +# last edit @ 2009.12.23 +import os,sys +import urllib,urllib2,cookielib +import re +from time import time,sleep + +path = os.path.dirname(os.path.realpath(sys.argv[0])) + +islogin = False +isproxy = False + +def useproxy(proxy='http://localhost:3128'): +# proxies = {'http':proxy} +# proxy_support = urllib2.ProxyHandler(proxies) +# opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler) +# urllib2.install_opener(opener) +# global isproxy + isproxy = True + +def login(): + print 'try to login...' +# proxies = {'http':'http://localhost:3128'} +# proxy_support = urllib2.ProxyHandler(proxies) + cookie=cookielib.CookieJar() +# opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie), urllib2.HTTPHandler,proxy_support) + opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie), urllib2.HTTPHandler) + urllib2.install_opener(opener) + + print '...getting login form...' + loginform = urllib2.urlopen('http://secure.verycd.com/signin/*/http://www.verycd.com/').read() + fk = re.compile(r'id="fk" value="(.*)"').findall(loginform)[0] + postdata=urllib.urlencode({'username':'simcdple', + 'password':'simcdple', + 'continueURI':'http://www.verycd.com/', + 'fk':fk, + 'login_submit':'登录', + }) + req = urllib2.Request( + url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/', + data = postdata + ) + req.add_header('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6') + req.add_header('Accept','text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8') + print '...login form submitted' + result = urllib2.urlopen(req).read() + print '...login succeed!' + global islogin + islogin = True + +#functions +def report(blocknum, bs, size, t): + if t == 0: + t = 1 + if size == -1: + print '%10s' % (str(blocknum*bs)) + ' downloaded | Speed =' + '%5.2f' % (bs/t/1024) + 'KB/s' + else: + percent = int(blocknum*bs*100/size) + print '%10s' % (str(blocknum*bs)) + '/' + str(size) + 'downloaded | ' + str(percent) + '% Speed =' + '%5.2f'%(bs/t/1024) + 'KB/s' + +def httpfetch(url, headers={}, reporthook=report, postData=None, report=True, needlogin=False): + ok = False + if (not islogin) and needlogin: + login() + if (not isproxy) and (not islogin): + useproxy() + for _ in range(10): + try: + reqObj = urllib2.Request(url, postData, headers) + fp = urllib2.urlopen(reqObj) + headers = fp.info() + ok = True + break + except: + sleep(1) + continue + + if not ok: + open(path+'/errors','a').write(url+'\n') + return '' + + rawdata = '' + bs = 1024*8 + size = -1 + read = 0 + blocknum = 0 + + if reporthook and report: + if "content-length" in headers: + size = int(headers["Content-Length"]) + reporthook(blocknum, bs, size, 1) + + t0 = time() + while 1: + block = '' + try: + block = fp.read(bs) + except: + open(path+'/errors','a').write(url+'\n') + return '' + if block == "": + print '...',url,'downloaded' + break + rawdata += block + read += len(block) + blocknum += 1 + if reporthook and report: + reporthook(blocknum, bs, size, time()-t0) + t0 = time() + + # raise exception if actual size does not match content-length header + if size >= 0 and read < size: + raise ContentTooShortError("retrieval incomplete: got only %i out " + "of %i bytes" % (read, size), result) + return rawdata + +if __name__ == '__main__': + url = 'http://www.verycd.com/topics/2788317' + + #test it + data = httpfetch(url) + open('down','w').write(data) + diff --git a/feed.py b/feed.py new file mode 100644 index 0000000..4b75440 --- /dev/null +++ b/feed.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# feed.py generate rss feed +# +# author: observer +# email: jingchaohu@gmail.com +# blog: http://obmem.com +# last edit @ 2009.12.22 +import sqlite3 +import time +import os,sys + +def feed(path,conn): + + c=conn.cursor() + + for _ in range(10): + try: + c.execute('select * from verycd order by updtime desc limit 20'); + break + except: + time.sleep(5) + continue + + data = None + + try: + data = c.fetchall() + except: + c.close() + conn.commit() + return + + c.close() + conn.commit() + + pubdate = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) + + feed = ''' + + + SimpleCD - 最新电驴资源 + +http://www.simplecd.org + +zh-cn\n''' + feed += '%s\n' % pubdate + feed += '%s\n' % pubdate + feed += '''http://blogs.law.harvard.edu/tech/rss +SimpleCD.com +jingchaohu@gmail.com (webmaster) +jingchaohu@gmail.com (webmaster) +4 +''' + + for d in data: + # data:0 1 2 3 4 5 6 7 8 9 + # id ttl status brief pubtime pudtime cat1 cat2 ed2k content + title = d[1] + link = 'http://www.simplecd.org/?id=%s' % d[0] + rss = '摘要信息:'+d[3]+'
\n类别:'+d[6]+'
\n子类别:'+d[7]+'
\n' + rss += d[9] + feed +=''' + + <![CDATA[%s]]> + %s + + %s + observer + +'''% (title,link,rss,pubdate) + + feed +='''
+
''' + return feed + +if __name__ == '__main__': + path = os.path.dirname(os.path.realpath(sys.argv[0])) + conn = sqlite3.connect(path+'/verycd.sqlite3.db') + conn.text_factory = str + print feed(path,conn) diff --git a/fetchvc.py b/fetchvc.py new file mode 100644 index 0000000..a901231 --- /dev/null +++ b/fetchvc.py @@ -0,0 +1,369 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# fetchvc.py fetch resources from verycd +# +# author: observer +# email: jingchaohu@gmail.com +# blog: http://obmem.com +# last edit @ 2009.12.23 +import urllib +import re +import sqlite3 +import time +import os,sys + +from threading import Thread +from Queue import Queue + +from download import httpfetch + +path = os.path.dirname(os.path.realpath(sys.argv[0])) +conn = sqlite3.connect(path+'/verycd.sqlite3.db') +conn.text_factory = str +q = Queue() +MAXC = 8 + +def thread_fetch(): + conn = sqlite3.connect(path+'/verycd.sqlite3.db') + conn.text_factory = str + while True: + topic = q.get() + fetch(topic,conn) + q.task_done() + +def search(keyword,full=True): + '''search verycd, fetch search results''' + + searchlog = path+'/search.log' + open(searchlog,'a').write('\n'+keyword+'\n') + + url = 'http://www.verycd.com/search/folders/'+keyword + print 'fetching search results ...' + res = httpfetch(url) + topics = re.compile(r'/topics/(\d+)',re.DOTALL).findall(res) + topics = set(topics) + links = [] + if full: + links = re.compile(r'/search/folders/(.*?\?start=\d+)',re.DOTALL).findall(res) + print links + print topics + if topics: + for topic in topics: + open(searchlog,'a').write(topic+',') + q.put(topic) + if full and links: + for key in links: + search(key,full=False) + + +def hot(): + ''' read verycd hot res and keep update very day ''' + url = 'http://www.verycd.com/' + print 'fetching homepage ...' + home = httpfetch(url) + hotzone = re.compile(r'热门资源.*?',re.DOTALL).search(home).group() + hot = re.compile(r']*>(《.*?》)[^<]*',re.DOTALL).findall(hotzone) + html = '

每日热门资源

\n' + for topic in hot: + print 'fetching hot topic',topic[0],'...' + q.put(topic[0]) + html += ' %s \n' % topic + open(path+'/static/hot.html','w').write(html) + +def normal(pages): + '''fetch normal res that need login''' + if '-' in pages: + (f,t)=[ int(x) for x in pages.split('-') ] + else: + f = t = int(pages) + for page in range(f,t+1): + url = 'http://www.verycd.com/orz/page%d?stat=normal' % page + idx = httpfetch(url,needlogin=True) + ids = re.compile(r'/topics/(\d+)',re.DOTALL).findall(idx) + print ids[0] + for id in ids: + q.put(id) + +def request(pages): + '''fetch request res that need login''' + if '-' in pages: + (f,t)=[ int(x) for x in pages.split('-') ] + else: + f = t = int(pages) + for page in range(f,t+1): + url = 'http://www.verycd.com/orz/page%d?stat=request' % page + idx = httpfetch(url,needlogin=True) + ids = re.compile(r'/topics/(\d+)',re.DOTALL).findall(idx) + print ids[0] + for id in ids: + q.put(id) + +def feed(): + ''' read verycd feed and keep update very 30 min ''' + url = 'http://www.verycd.com/sto/feed' + print 'fetching feed ...' + feeds = httpfetch(url) + ids = re.compile(r'/topics/(\d+)',re.DOTALL).findall(feeds) + ids = set(ids) + print ids + now = time.mktime(time.gmtime()) + for id in ids: + q.put(id) + #updtime = fetch(id) + #updtime = time.mktime(time.strptime(updtime,'%Y/%m/%d %H:%M:%S'))-8*3600 #gmt+8->gmt + #diff = now - updtime + #print '%10s secs since update' % (diff) + #if diff > 1900: # only need recent 30min updates + # break + +def update(num=10): + urlbase = 'http://www.verycd.com/sto/~all/page' + for i in range(1,num+1): + print 'fetching list',i,'...' + url = urlbase+str(i) + res = httpfetch(url) + res2 = re.compile(r'"topic-list"(.*?)"pnav"',re.DOTALL).findall(res) + if res2: + res2 = res2[0] + else: + continue + topics = re.compile(r'/topics/(\d+)',re.DOTALL).findall(res2) + topics = set(topics) + print topics + for topic in topics: + q.put(topic) + + +def fetchall(ran='1-max',debug=False): + urlbase = 'http://www.verycd.com/archives/' + if ran == '1-max': + m1 = 1 + res = urllib.urlopen(urlbase).read() + m2 = int(re.compile(r'archives/(\d+)').search(res).group(1)) + else: + m = ran.split('-') + m1 = int(m[0]) + m2 = int(m[1]) + print 'fetching list from',m1,'to',m2,'...' + for i in range(m1,m2+1): + url = urlbase + '%05d'%i + '.html' + print 'fetching from',url,'...' + res = httpfetch(url) + ids = re.compile(r'topics/(\d+)/',re.DOTALL).findall(res) + print ids + for id in ids: + q.put(id) + + +def fetch(id,conn=conn,debug=False): + print 'fetching topic',id,'...' + urlbase = 'http://www.verycd.com/topics/' + url = urlbase + str(id) + + res = '' + for _ in range(3): + try: + res = httpfetch(url,report=True) + break + except: + continue + + abstract = re.compile(r'

.*?visit',re.DOTALL).findall(res) + if not abstract: + print res + if res == '' or '很抱歉' in res: + print 'resource does not exist' + return + else: + print 'fetching',id,'again...' + return fetch(id,conn) + abstract = abstract[0] + + title = re.compile(r'

(.*?)

',re.DOTALL).findall(abstract) + if title: + title=title[0] + else: + return + try: + status = re.compile(r'"requestWords">(.*?)<',re.DOTALL).search(abstract).group(1) + brief = re.compile(r'"font-weight:normal">(.*?)',re.DOTALL).search(abstract).group(1) + brief = re.compile(r'<.*?>',re.DOTALL).sub('',brief).strip() + pubtime = re.compile(r'"date-time">(.*?).*?"date-time">(.*?)',re.DOTALL).findall(abstract)[0] + category1 = re.compile(r'分类.*?(.*?)  (.*?)  ',re.DOTALL).findall(abstract)[0] + category = ['',''] + category[0] = re.compile(r'<.*?>',re.DOTALL).sub('',category1[0]).strip() + category[1] = re.compile(r'<.*?>',re.DOTALL).sub('',category1[1]).strip() + +# res2 = re.compile(r'iptcomED2K">',re.DOTALL).findall(res)[0] + + ed2k = re.compile(r'ed2k="([^"]*)" (subtitle_[^=]*="[^"]*"[^>]*)>([^<]*)',re.DOTALL).findall(res) + ed2k.extend( re.compile(r'ed2k="([^"]*)">([^<]*)',re.DOTALL).findall(res) ) + + content = re.compile(r'(.*?)',re.DOTALL).findall(res) + except: + return + + if content: + content = content[0] + content = re.compile(r'
',re.DOTALL).sub('\n',content) + content = re.compile(r'<.*?>',re.DOTALL).sub('',content) + content = re.compile(r'&.*?;',re.DOTALL).sub(' ',content) + content = re.compile(r'\n\s+',re.DOTALL).sub('\n',content) + content = content.strip() + else: + content='' + + if debug: + print title + print status + print brief + print pubtime[0],pubtime[1] + print category[0],category[1] + for x in ed2k: + print x + print content + + ed2kstr = '' + for x in ed2k: + ed2kstr += '`'.join(x)+'`' + tries=0 + while tries<3: + try: + if not dbfind(id,conn): + dbinsert(id,title,status,brief,pubtime,category,ed2kstr,content,conn) + else: + dbupdate(id,title,status,brief,pubtime,category,ed2kstr,content,conn) + break; + except: + tries += 1; + time.sleep(5); + continue; + + return pubtime[1] + +def dbcreate(): + c = conn.cursor() + c.execute('''create table verycd( + verycdid integer primary key, + title text, + status text, + brief text, + pubtime text, + updtime text, + category1 text, + category2 text, + ed2k text, + content text + )''') + conn.commit() + c.close() + +def dbinsert(id,title,status,brief,pubtime,category,ed2k,content,conn): + c = conn.cursor() + tries = 0 + while tries<10: + try: + c.execute('insert into verycd values(?,?,?,?,?,?,?,?,?,?)',\ + (id,title,status,brief,pubtime[0],pubtime[1],category[0],category[1],\ + ed2k,content)) + break + except: + tries += 1 + time.sleep(5) + continue + conn.commit() + c.close() + +def dbupdate(id,title,status,brief,pubtime,category,ed2k,content,conn): + tries = 0 + c = conn.cursor() + while tries<10: + try: + c.execute('update verycd set verycdid=?,title=?,status=?,brief=?,pubtime=?,\ + updtime=?,category1=?,category2=?,ed2k=?,content=? where verycdid=?',\ + (id,title,status,brief,pubtime[0],pubtime[1],category[0],category[1],\ + ed2k,content,id)) + break + except: + tries += 1 + time.sleep(5) + continue + conn.commit() + c.close() + +def dbfind(id,conn): + c = conn.cursor() + c.execute('select 1 from verycd where verycdid=?',(id,)) + c.close() + for x in c: + if 1 in x: + return True + else: + return False + +def dblist(): + c = conn.cursor() + c.execute('select * from verycd') + for x in c: + for y in x: + print y + +def usage(): + print '''Usage: + python fetchvc.py createdb + python fetchvc.py fetchall + python fetchvc.py fetch 1-1611 #fetch archive list + python fetchvc.py fetch 5633~5684 #fetch topics + python fetchvc.py fetch 5633 #fetch a topic + python fetchvc.py fetch q=keyword + python fetchvc.py list #list the database + python fetchvc.py feed #run every 30 min to keep up-to-date + python fetchvc.py hot + python fetchvc.py update #update first 20 pages, run on a daily basis''' + +#initialize thread pool +for i in range(MAXC): + t = Thread(target=thread_fetch) + t.setDaemon(True) + t.start() + +if __name__=='__main__': + + if len(sys.argv) == 1: + usage() + elif len(sys.argv) == 2: + if sys.argv[1] == 'createdb': + dbcreate() + elif sys.argv[1] == 'fetchall': + fetchall() + elif sys.argv[1] == 'update': + update(20) + elif sys.argv[1] == 'update1': + update(1) + elif sys.argv[1] == 'feed': + feed() + elif sys.argv[1] == 'hot': + hot() + elif sys.argv[1] == 'list': + dblist() + elif len(sys.argv) == 3: + if sys.argv[1] != 'fetch': + usage() + elif '~' in sys.argv[2]: + m = sys.argv[2].split('~') + for i in range(int(m[0]),int(m[1])+1): + q.put(i) + elif sys.argv[2].startswith("q="): + search(sys.argv[2][2:]) + elif sys.argv[2].startswith("n="): + normal(sys.argv[2][2:]) + elif sys.argv[2].startswith("r="): + request(sys.argv[2][2:]) + elif '-' in sys.argv[2]: + fetchall(sys.argv[2]) + else: + fetch(int(sys.argv[2]),debug=True) + + # wait all threads done + q.join() diff --git a/login.py b/login.py new file mode 100644 index 0000000..591ab06 --- /dev/null +++ b/login.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# sample +import urllib,urllib2,cookielib +import re + +cookie=cookielib.CookieJar() +opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie), urllib2.HTTPHandler) +urllib2.install_opener(opener) + +loginform = urllib2.urlopen('http://secure.verycd.com/signin/*/http://www.verycd.com/').read() + +#print loginform +fk = re.compile(r'id="fk" value="(.*)"').findall(loginform)[0] +print fk; +postdata=urllib.urlencode({'username':'simcdple', + 'password':'simcdple', + 'continueURI':'http://www.verycd.com/', + 'fk':fk, + 'login_submit':'登录', +}) +req = urllib2.Request( + url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/', + data = postdata +) +req.add_header('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6') +req.add_header('Accept','text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8') +result = urllib2.urlopen(req).read() +#print result +#print result.status +newatt = urllib2.urlopen('http://www.verycd.com/topics/2788317/').read() +print newatt diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..2dbe1ec --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,56 @@ +user www-data; +worker_processes 2; + +error_log /var/log/nginx/error.log; +pid /var/run/nginx.pid; + +events { + worker_connections 2048; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + autoindex on; + autoindex_exact_size off; + autoindex_localtime on; + + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$gzip_ratio"'; + + log_format download '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$http_range" "$sent_http_content_range"'; + + client_header_timeout 3m; + client_body_timeout 3m; + send_timeout 3m; + + client_header_buffer_size 1k; + large_client_header_buffers 4 4k; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 8k; + gzip_http_version 1.1; + gzip_comp_level 3; + gzip_types text/css text/xml text/plain application/x-javascript application/xml application/pdf application/x-perl application/x-tcl application/msword application/rtf application/vnd.ms-excel application/vnd.ms-powerpoint application/vnd.wap.xhtml+xml image/x-ms-bmp; + gzip_disable "MSIE [1-6] \."; + gzip_vary on; + + output_buffers 1 32k; + postpone_output 1460; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + keepalive_timeout 75 20; + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} diff --git a/nginx/simplecd b/nginx/simplecd new file mode 100644 index 0000000..55448f2 --- /dev/null +++ b/nginx/simplecd @@ -0,0 +1,28 @@ +# You may add here your +# server { +# ... +fastcgi_cache_path /var/www/simplecd/cache + levels=1:2 keys_zone=webpy:50m; +server { + listen 80; + server_name xen.simplecd.org; + access_log /var/log/nginx/xen.simplecd.org.access_log; + error_log /var/log/nginx/xen.simplecd.org.error_log warn; + root /var/www/simplecd/; + index index.html; + location / { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_script_name; + fastcgi_pass 127.0.0.1:9001; + fastcgi_cache webpy; + fastcgi_cache_key $server_addr$request_uri; + fastcgi_cache_valid any 1m; + fastcgi_hide_header Set-Cookie; + } + location /static/ { + if (-f $request_filename) { + rewrite ^/static/(.*)$ /static/$1 break; + } + } +} diff --git a/nginx/spawn-fcgi.sh b/nginx/spawn-fcgi.sh new file mode 100644 index 0000000..b517162 --- /dev/null +++ b/nginx/spawn-fcgi.sh @@ -0,0 +1,2 @@ +spawn-fcgi -u www-data -g www-data -d /var/www/simplecd/ -f /var/www/simplecd/code.py -a 127.0.0.1 -p 9001 -F 2 + diff --git a/pack.py b/pack.py new file mode 100644 index 0000000..43ff567 --- /dev/null +++ b/pack.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pack.py generate html pack +# author: observer +# email: jingchaohu@gmail.com +# blog: http://obmem.com +# last edit @ 2009.12.23 +import urllib +import re +import sqlite3 +import time +import os,sys +import shutil + +path = os.path.dirname(os.path.realpath(sys.argv[0])) +conn = sqlite3.connect(path+'/verycd.sqlite3.db') +conn.text_factory = str + +def pack(topath="/tmp/simplecd_htmlpack"): + try: + os.mkdir(topath) + except: + pass + shutil.copyfile(path+'/static/main_02.css',topath+'/main_02.css') + shutil.copyfile(path+'/static/common.js',topath+'/common.js') + c = conn.cursor() + c.execute("select verycdid from verycd order by updtime asc") + ids = c.fetchall() + baseurl = 'http://www.simplecd.org/?id=' + for id in ids: + url = baseurl + str(id[0]) + html = urllib.urlopen(url).read() + html.replace('/static/main_02.css','main_02.css') + html.replace('/static/common.js','common.js') + fname = str(id[0])+'.html' + open(topath+'/'+fname,'w').write(html) + c.close() + +if __name__ == '__main__': + pack() diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..32b9c2c --- /dev/null +++ b/readme.txt @@ -0,0 +1,28 @@ +https://code.google.com/p/simplecd/wiki/Deployment + +3. 简易架设攻略 +下载源码 + +cd /var/www +hg clone https://simplecd.googlecode.com/hg simplecd +cd simplecd +hg update dev-sqlite +注:分支建议采用dev-sqlite,这个和目前网站的代码最为相似 + +deployment分支继续不变,因为deployment分支代码简单看起来爽一点。 + +接下来做一些基本的配置 + +#创建数据库 +./fetchvc.py createdb + +#nginx的配置文件(请根据视频进行相应修改) +cp nginx/nginx.conf /etc/nginx/ +cp nginx/simplecd /etc/nginx/sites-available/ +ln -s /etc/nginx/sites-available/simplecd /etc/nginx/sites-enabled/simplecd + +#用spawn-fcgi开fcgi +nginx/spawn-fcgi.sh + +#开启nginx服务 +/etc/init.d/nginx start diff --git a/scdd.py b/scdd.py new file mode 100644 index 0000000..5e55032 --- /dev/null +++ b/scdd.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +#coding: utf-8 +# +# scdd.py daemon process +# +# author: observer +# email: jingchaohu@gmail.com +# blog: http://obmem.com +# last edit @ 2009.12.23 +import os,sys,time +import re +from daemon import Daemon +import sqlite3 +import fetchvc +from download import httpfetch +from Queue import Queue +from threading import Thread +from feed import feed + +class MyDaemon(Daemon): + def __init__(self,path,pid): + self.path = path + self.q = Queue() + Daemon.__init__(self,pid) + + def thread_fetch(self): + conn = sqlite3.connect(self.path+'/verycd.sqlite3.db') + conn.text_factory = str + while True: + topic = self.q.get() + if str(topic)=='feed': + open(self.path+'/static/feed.xml','w').write(feed(self.path,conn)) + self.q.task_done() + continue + try: + fetchvc.fetch(topic,conn) + except: + pass + self.q.task_done() + + def run(self): + for i in range(8): + t = Thread(target=self.thread_fetch) + t.setDaemon(True) + t.start() + + conn = sqlite3.connect(self.path+'/verycd.sqlite3.db') + conn.text_factory = str + while True: + try: + #feed + if time.mktime(time.gmtime())%60<10: + self.q.put('feed') + #check searchqueue every 10 secs + taskqueue = open(self.path+'/searchqueue','r').readlines() + print taskqueue,time.mktime(time.gmtime()),time.mktime(time.gmtime())%900 + open(self.path+'/searchqueue','w').write('') + for task in taskqueue: + url = 'http://www.verycd.com/search/folders/'+task + print 'fetching', url, '...' + res = httpfetch(url) + print '...fetching completed' + topics = re.compile(r'/topics/(\d+)',re.DOTALL).findall(res) + topics = set(topics) + for topic in topics: + self.q.put(topic) + if taskqueue == []: + time.sleep(10) + # read feed every 900 secs + if time.mktime(time.gmtime())%600<10: + url = 'http://www.verycd.com/sto/feed' + print 'fetching feed ...' + feeds = httpfetch(url) + topics = re.compile(r'/topics/(\d+)',re.DOTALL).findall(feeds) + topics = set(topics) + print topics + now = time.mktime(time.gmtime()) + for topic in topics: + self.q.put(topic) + # read hot everyday at gmt 19:00 + # read hot every 4 hours + timeofday = time.mktime(time.gmtime())%(86400/6) +# if timeofday>68400 and timeofday < 68410: + if time.mktime(time.gmtime())%(3600*4)<10: + url = 'http://www.verycd.com/' + print 'fetching homepage ...' + home = httpfetch(url) + hotzone = re.compile(r'热门资源.*?',re.DOTALL).search(home).group() + hot = re.compile(r']*>(《.*?》)[^<]*',re.DOTALL).findall(hotzone) + html = '

每日热门资源

\n' + for topic in hot: + print 'fetching hot topic',topic[0],'...' + self.q.put(topic[0]) + html += ' %s \n' % topic + open(self.path+'/static/hot.html','w').write(html) + # update 20 whole pages at gmt 19:10 + if timeofday>69000 and timeofday < 69010: + urlbase = 'http://www.verycd.com/sto/~all/page' + for i in range(1,20): + print 'fetching list',i,'...' + url = urlbase+str(i) + res = httpfetch(url) + res2 = re.compile(r'"topic-list"(.*?)"pnav"',re.DOTALL).findall(res) + if res2: + res2 = res2[0] + else: + continue + topics = re.compile(r'/topics/(\d+)',re.DOTALL).findall(res2) + topics = set(topics) + print topics + for topic in topics: + self.q.put(topic) + # update 1 pages@normal and 1 pages@request every 3600 secs + if time.mktime(time.gmtime())%3600<10: + url = 'http://www.verycd.com/orz/page1?stat=normal' + idx = httpfetch(url,needlogin=True) + ids = re.compile(r'/topics/(\d+)',re.DOTALL).findall(idx) + print ids[0] + for id in ids: + self.q.put(id) + url = 'http://www.verycd.com/orz/page1?stat=request' + idx = httpfetch(url,needlogin=True) + ids = re.compile(r'/topics/(\d+)',re.DOTALL).findall(idx) + print ids[0] + for id in ids: + self.q.put(id) + except: + time.sleep(10) + continue + + +if __name__ == "__main__": + path = os.path.dirname(os.path.realpath(sys.argv[0])) + daemon = MyDaemon(path=path,pid='/tmp/simplevc.pid') + if len(sys.argv) == 2: + if 'start' == sys.argv[1]: + daemon.start() + elif 'stop' == sys.argv[1]: + daemon.stop() + elif 'restart' == sys.argv[1]: + daemon.restart() + elif 'run' == sys.argv[1]: + daemon.run() + else: + print "Unknown command" + sys.exit(2) + sys.exit(0) + else: + print "usage: %s start|stop|restart" % sys.argv[0] + sys.exit(2) diff --git a/searchqueue b/searchqueue new file mode 100644 index 0000000..e69de29 diff --git a/static/about.html b/static/about.html new file mode 100644 index 0000000..aa4d051 --- /dev/null +++ b/static/about.html @@ -0,0 +1,15 @@ + + + + 关于SimpleCD + + +

关于SimpleCD

+

由于最近某国内耗剧烈,殃及了池鱼,大量BT站点被关,消息人士认为这一切是因为我们知道的太多了,最后连VeryCD都无法访问了,稍后虽然证实是虚惊一场,但是本人兴起了为VeryCD备份的念头,以防其再遭不测。这就是本站诞生的由来。

+

VeryCD倒掉之前的那段时间里,有先见之明的人士为其做了部分镜像来缅怀VeryCD,但是杯水车薪,75M的镜像只保留了大约100个链接,而且搜索功能什么的都是无法保存的

+

VeryCD恢复之后,我试着用wget和httrack工具为其备份,但是效果一般,而且也有无法搜索的问题。如此,下一步的改进计划出炉:用爬虫脚本爬VeryCD的资料,然后保存到数据库,根据数据库建站,于是就是本站目前的状况。

+

本站目前还处于开发和调试阶段,VeryCD的访问相当缓慢,所以爬虫脚本仍在努力地工作中,等到一切稳定下来以后,本站将会开源。

+

关于本人

+

土人一个,经营Blog一只(一直懒得调整Wordpress主题和RSSFeed,导致现在不伦不类的。),欢迎来踩。

+ + diff --git a/static/common.js b/static/common.js new file mode 100644 index 0000000..af4a565 --- /dev/null +++ b/static/common.js @@ -0,0 +1,236 @@ +function init(str){ + checkAll(str,true); +} +function checkAll(str,checked) { + var a = document.getElementsByName(str); + var n = a.length; + + for (var i = 0; i < n; i++) { + a[i].checked = checked; + } + em_size(str); +} +function em_size(str) { + var a = document.getElementsByName(str); +// var b = document.getElementsByName(str+"size"); + var n = a.length; + try { + var input_checkall = document.getElementById("checkall_"+str); + var size = 0; + input_checkall.checked = true ; + for (var i=0; i < n; i++) { + if (a[i].checked) { + var piecesArray = a[i].value.split( "|" ); +// b[i].innerHTML = gen_size(piecesArray[3]*1,3,1); + size += piecesArray[3]*1; + } else { + input_checkall.checked = false; + } + } + test = document.getElementById("size_"+str); + test.innerHTML = gen_size(size, 3, 1); + } catch (e) { + + } +} +function gen_size(val, li, sepa ) { + if (parseInt(val)<1) return 0; + sep = Math.pow(10, sepa); //小数点后的位数 + li = Math.pow(10, li); //开始截断的长度 + retval = val; + unit = 'Bytes'; + if (val >= li*1000000000) { + val = Math.round( val / (1099511627776/sep) ) / sep; + unit = 'TB'; + } else if (val >= li*1000000) { + val = Math.round( val / (1073741824/sep) ) / sep; + unit = 'GB'; + } else if (val >= li*1000) { + val = Math.round( val / (1048576/sep) ) / sep; + unit = 'MB'; + } else if (val >= li) { + val = Math.round( val / (1024/sep) ) / sep; + unit = 'KB'; + } + return val + unit; +} +function copy(str) { + var a = document.getElementsByName(str); + var n = a.length; + var ed2kcopy = ""; + for (var i = 0; i < n; i++) { + if(a[i].checked) { + ed2kcopy += a[i].value; + ed2kcopy += "\n"; + } + } + copyToClipboard(ed2kcopy); +} +function countlink(str){ + var r = 0; + a = document.getElementsByName(str); + n = a.length; + for ( var i = 0; i < n; i++ ){ + if ( a[i].checked) { + r += 1; + } + } + return r; +} +function copy2popup(str){ + var a = document.getElementsByName(str); + var n = a.length; + var ed2kcopy = ""; + for (var i = 0;i < n; i++) { + if (a[i].checked) { + ed2kcopy += a[i].value; + ed2kcopy += "
\n"; + } + } + return ed2kcopy; +} +function copyToClipboard(txt) { + if(window.clipboardData) { + window.clipboardData.clearData(); + window.clipboardData.setData("Text", txt); + } else if(navigator.userAgent.indexOf("Opera") != -1) { + window.location = txt; + } else if (window.netscape) { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + } catch (e) { + alert("被浏览器拒绝!\n请在浏览器地址栏输入'about:config'并回车\n然后将'signed.applets.codebase_principal_support'设置为'true'"); + } + var clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard); + if (!clip) + return; + var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable); + if (!trans) + return; + trans.addDataFlavor('text/unicode'); + var str = new Object(); + var len = new Object(); + var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString); + var copytext = txt; + str.data = copytext; + trans.setTransferData("text/unicode",str,copytext.length*2); + var clipid = Components.interfaces.nsIClipboard; + if (!clip) + return false; + clip.setData(trans,null,clipid.kGlobalClipboard); + } +} +function download(str, i, first) { + var a = document.getElementsByName(str); + var n = a.length; + + //尝试使用activex方式批量新增下载 + try { + var ed2k_links = ''; + var ax = new ActiveXObject("IE2EM.IE2EMUrlTaker"); + var emule_version = ax.GetEmuleVersion(); + if ('e' != emule_version.substr(0,1)) { + throw {errorCode:'eMule not Installed.'}; + } + for (var i = i; i < n; i++) { + if(a[i].checked) { + if (ed2k_links=='') { + ed2k_links = a[i].value; + } else { + ed2k_links += "\n"+a[i].value; + } + } + } + ax.SendUrl(ed2k_links, 'dd', document.location); + delete ax; + return; + } catch (e) {} + + if (!window.continueDown) { + //使用最旧的方法来批量新增下载 + for (var i = i; i < n; i++) { + if(a[i].checked) { + window.location=a[i].value; + if (first) + timeout = 6000; + else + timeout = 500; + i++; + window.setTimeout("download('"+str+"', "+i+", 0)", timeout); + break; + } + } + } else { + //使用稍微新一点的方法来批量新增下载 + for (var i = i; i < n; i++) { + if(a[i].checked) { + if(first){ + var k = i; + var current_link = a[k].nextSibling; + var multi_text = ''; + var tmp_counter = 0; + var comma = ''; + while(true){ + if(a[k].checked && current_link){//如果是有效节点并且被选中 + if(current_link.href){ + if(current_link.href.indexOf('ed2k') !== 0){ + current_link = current_link.nextSibling; + continue; + } + if(tmp_counter > 7){//收集超过若干个有效链接后,退出 + multi_text += '
…………'; + break; + } + var right_link = current_link; + tmp_counter++; + if (navigator.userAgent.toLowerCase().indexOf("msie")==-1) { + multi_text += comma+current_link.text; + }else{ + multi_text += comma+current_link.innerText; + } + comma = '
'; + } + + current_link = current_link.nextSibling; + }else{//未被选中,或往下没有相邻节点了,那么切换到下个父节点 + if(++k >= n){//如果父节点也到底了,那么退出 + break; + } + current_link = a[k].nextSibling; + } + } + downPopup(right_link,multi_text); + } + + continueDown(a[i].value); + //window.location=a[i].value; + if (first) + timeout = 6000; + else + timeout = 500; + i++; + window.setTimeout("download('"+str+"', "+i+", 0)", timeout); + break; + } + } + } +} +function showPopup(txt,t,l,w,h){ + var popUp = document.getElementById("popupcontent"); + + popUp.style.top = t + "px"; + popUp.style.left = l + "px"; + popUp.style.width = w + "px"; + popUp.style.height = h + "px"; + + popUp.innerHTML = txt;// + +//"
"; + +// var sbar = document.getElementById("statusbar"); +// sbar.style.marginTop = (parseInt(h)-40) + "px"; + popUp.style.visibility = "visible"; +} +function hidePopup(){ + var popUp = document.getElementById("popupcontent"); + popUp.style.visibility = "hidden"; +} diff --git a/static/hot.html b/static/hot.html new file mode 100644 index 0000000..3b531f1 --- /dev/null +++ b/static/hot.html @@ -0,0 +1,25 @@ +

每日热门资源

《未来战警》  + 《YYeTs人人影视2009年12月RMVB电影合辑》  + 《海豚湾》  + 《无主之地:内德博士的僵尸岛》  + 《拆弹部队》  + 《2012》  + 《2009年北美上映电影预告片合辑(按上映时间及时更新)-更新至2009年第51周(2009.12.18)》  + 《复仇》  + 《萨莉娜》  + 《别对我撒谎 第二季》  + 《虎!虎!虎!》  + 《灌篮高手十日后画册》  + 《野兽家园》  + 《下一站./幸福》  + 《余烬清风》  + 《生活大爆炸 第三季》  + 《疲惫不堪的生活》  + 《蜗居》  + 《深空失忆》  + 《三教九流大观》  + 《山楂树之恋》  + 《极度恐慌2:重生》  + 《同门》  + 《计算理论书籍合集》  diff --git a/static/main_02.css b/static/main_02.css new file mode 100644 index 0000000..4020e79 --- /dev/null +++ b/static/main_02.css @@ -0,0 +1,44 @@ +td {font-size:10pt; line-height:180%;} +.xt{font-size:10pt;line-height:180%; border-bottom:1px #bbbbbb dashed;font-family:"", "arial", "ms sans serif";} +.bt{font-size:10pt;line-height:180%; border-bottom:1px #bbbbbb solid;font-family:"", "arial", "ms sans serif";} +.inp{font-size:11.5pt; height: 28px;} +a:link {color: blue; text-decoration: none } +a:active {color: blue; text-decoration: underline} +a:visited {color: #c04dc0; text-decoration: none} +a:hover {color: blue; text-decoration: underline;position:relative;left:1px;top:1px} +a.cat:link {color: red; text-decoration: none } +a.cat:active {color: red; text-decoration: none} +a.cat:visited {color: red; text-decoration: none} +a.cat:hover {color: blue; text-decoration: underline;position:relative;left:1px;top:1px} +a.tt:link {color: black; text-decoration: none} +a.tt:active {color: blue; text-decoration: underline} +a.tt:visited {color: black; text-decoration: none;} +a.tt:hover {color: blue; text-decoration: underline;position:relative;left:1px;top:1px} +a.bl:link {color: blue; text-decoration: none} +a.bl:active {color: red; text-decoration: none} +a.bl:visited {color: blue; text-decoration: none} +a.bl:hover {color: red; text-decoration: underline;position:relative;left:1px;top:1px} +a.tl:link {color: #123b8d; text-decoration: none} +a.tl:active {color: blue; text-decoration: none} +a.tl:visited {color: #123b8d; text-decoration: none} +a.tl:hover {color: blue; text-decoration: underline;position:relative;left:1px;top:1px} +body {margin-top:0px;margin-right:0px;margin-bottom:0px;} +h1 {font-size:22px;line-height:180%;margin:0px;padding: 0px;color: #cc0000;} +p {font-size:10.5pt;line-height:180%;} +input{font-size:11.5pt;height:25px;} +div,form,img,ul,li,img{margin:0; padding:0; border:0;} +table{background-color:#ffffff;} .ed2kzone{background-color: #9FaFbF;border: 1px solid #CCC;} +.ed2ktitle{background:#AAA;color:#FFFFFF;font-weight:bold;} +.ed2ktitle a{color:#888;} +.ed2kheader{background:#c0d0e0;color:#000000} +.ed2kitem{background-color: #FFFFFF;} +.ed2kitemChecked{background-color: #FFFFCC;} +#popupcontent{ +position: relative; +visibility: hidden; +border:1px solid #CCC; +background-color:#F9F9F9; +border:1px solid #333; +padding:5px; +overflow:scroll; +} diff --git a/static/simplecd.png b/static/simplecd.png new file mode 100644 index 0000000..841d650 Binary files /dev/null and b/static/simplecd.png differ diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000..6e50c31 --- /dev/null +++ b/templates/error.html @@ -0,0 +1,5 @@ +$def with (err) + + +$err 出错啦 + diff --git a/templates/fin.html b/templates/fin.html new file mode 100644 index 0000000..02061cf --- /dev/null +++ b/templates/fin.html @@ -0,0 +1,42 @@ +$def with (req) + + + SimpleCD|精简版的VeryCD + + + + + + + + + + + + +
+ 怕了国内那些大爷了,所以为VeryCD做了个简单备份
+ +
+ + + + +
+

搜索"$req"已经提交队列,请等待服务器处理,如果空闲的话,5分钟左右就能处理好,请5分钟以后再次搜索同一关键字,说不定就有了。本页面将在十五秒钟之后自动跳转回主页,或者点此直接返回主页

+
+ + + diff --git a/templates/id.html b/templates/id.html new file mode 100644 index 0000000..8539331 --- /dev/null +++ b/templates/id.html @@ -0,0 +1,114 @@ +$def with (r) + +$code: + rec = r[0] + fl = r[1] + id = r[2] + name = 'topic'+id + + def emsize(link): + if len(link.split('|'))<4: + return 'Unknown'; + n = int(link.split('|')[3]) + if n < 1024: + return '%d Bytes' % n + elif n < 1024*1024: + n2 = n/1.0/1024 + return '%3.1f KB' % n2 + elif n < 1024*1024*1024: + n2 = n/1.0/1024/1024 + return '%3.1f MB' % n2 + elif n < 1024*1024*1024*1024: + n2 = n/1.0/1024/1024/1024 + return '%3.1f GB' % n2 + def format(content): + return content.replace('\n','
') + def emlink(ed2k): + links = ed2k.split('`') + rtn = ''' + + + + ''' + + i = 0 + while i+1 < len(links): + if links[i].startswith('ed2k') and 'ed2k://' in links[i+1]: + rtn += ''' + + + \n''' % (links[i],name,links[i],links[i],links[i+2],name,emsize(links[i])) + i += 3 + else: + rtn += ''' + + + ''' % (links[i],name,links[i],links[i],links[i+1],name,emsize(links[i])) + i += 2 + rtn += ''' + +
'''+'下面是用户共享的文件列表,推荐使用'.decode('utf-8')+'''eMule '''+'进行下载,您可以点击这些文件名进行下载'.decode('utf-8')+'''
%s%s
%s%s
''' % name + return rtn + + + + SimpleCD:让分享变得简单 $rec.title + + + + + + + +$if fl: +

复制下列链接,在emule中选择新建任务即可

+ $:fl +$else: + +
+ + +
+ +   + +
+ + + + + + + + + +
VeryCD Topic ID: $:rec.verycdid
$:rec.title | from VeryCD
状态:$:rec.status
摘要:$:rec.brief
发布时间:$:rec.pubtime
更新时间:$:rec.updtime
类别:$:rec.category1,$:rec.category2
+ $:emlink(rec.ed2k) +
This is a popup window!
+
+ + +
$:format(rec.content)
+
+ +
+ + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3c86204 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,121 @@ +$def with (vcp) +$code + vc=vcp[0] + pages=vcp[1] + arg=vcp[2] + if vcp[3] == None: + q='' + else: + q=vcp[3] + num=vcp[4] + if vcp[5]: + cat=vcp[5] + else: + cat='' + hot = vcp[6] + + + + SimpleCD|精简版的VeryCD + + + + + + + + + +
+ 本站源码 |  + 全站html打包下载 |  + 论坛 |  + 订阅更新 |  + 报告错误 |  + 关于 +
+ + + +
+ 怕了国内那些大爷了,所以为VeryCD做了个简单备份 
+ +
+ $if q == '': + + +
+ $:hot +
+ + +
+ 分类: + $if cat=='': + 全部 + $if cat=='': + $for catname in ['电影','剧集','音乐','游戏','动漫','综艺','软件','资料']: + $if cat==catname.decode('utf-8'): + $catname + $if cat==catname.decode('utf-8'): +
+ + + + + + + + $for r in vc: + + + + + + +
   最近更新的资源摘要更新时间VeryCD原链
$:r.title$:r.brief$:r.updtimeTopic$r.verycdid
+ + + + +
+ + 首页 + 上一页 + $for p in pages[3:]: + $p + 下一页 + 末页 + +
+ $if q != '' and num<=5: + + + + + +
+ 对搜索结果不满意?VeryCD上有更多结果?试试 + + +
+ + +