Import the files for the torrent role from puppet

This commit is contained in:
Pierre-Yves Chibon 2014-12-08 18:00:45 +00:00
parent 4cde2a4667
commit 6e4fddb509
25 changed files with 1186 additions and 0 deletions

View file

@ -0,0 +1,18 @@
/var/log/bittorrent/btseed.log {
notifempty
missingok
compress
postrotate
/sbin/service btseed condrestart 2>/dev/null >/dev/null || :
endscript
}
/var/log/bittorrent/bttrack.log {
notifempty
missingok
compress
postrotate
/sbin/service bttrack condrestart 2>/dev/null >/dev/null || :
endscript
}

View file

@ -0,0 +1,8 @@
SEEDDIR=/srv/torrent/btholding
SEEDOPTS="--max_upload_rate 350 --display_interval 300"
SEEDLOG=/var/log/bittorrent/btseed.log
TRACKPORT=6969
TRACKDIR=/srv/torrent/btholding
TRACKSTATEFILE=/srv/torrent/data/bttrack.dat
TRACKLOG=/var/log/bittorrent/bttrack.log
TRACKOPTS="--min_time_between_log_flushes 4.0 --show_names 1 --hupmonitor 1"

View file

@ -0,0 +1,51 @@
#!/bin/bash
statscmd=/usr/local/bin/torrent-data.py
btdata=/srv/torrent/data/bttrack.dat
outputdir=/srv/torrent/www/stats/
hourlydir=$outputdir/hourly
dailydir=$outputdir/daily
latestlink=$outputdir/current-stats.json
if ! [ -f $btdata ]; then
echo "cannot find $btdata"
exit 1
fi
if ! [ -d $outputdir ]; then
mkdir -p $outputdir
fi
if ! [ -d $hourlydir ]; then
mkdir -p $hourlydir
fi
if ! [ -d $dailydir ]; then
mkdir -p $dailydir
fi
if ! [ -f $statscmd ]; then
echo "Cannot find stats generating command $statscmd"
exit 1
fi
now=`date +%Y-%m-%d-%H:%M`
today=`date +%Y-%m-%d`
outputfile="$hourlydir/torrent-stats-$now.json"
dataloc=`mktemp`
\cp -f $btdata $dataloc
$statscmd $dataloc $outputfile
rm -f $dataloc
rm -f $latestlink
ln -s $outputfile $latestlink
if [ ! -f "$dailydir/torrent-stats-$today.json" ]; then
\cp -f $outputfile $dailydir/torrent-stats-$today.json
fi
/usr/sbin/tmpwatch -f 720 $hourlydir

View file

@ -0,0 +1,100 @@
# opentracker config file
#
# I) Address opentracker will listen on, using both, tcp AND udp family
# (note, that port 6969 is implicite if ommitted).
#
# If no listen option is given (here or on the command line), opentracker
# listens on 0.0.0.0:6969 tcp and udp.
#
# listen.tcp_udp 0.0.0.0
# listen.tcp_udp 192.168.0.1:80
# listen.tcp_udp 10.0.0.5:6969
#
# To only listen on tcp or udp family ports, list them this way:
#
# listen.tcp 0.0.0.0
# listen.udp 192.168.0.1:6969
#
# Note, that using 0.0.0.0 for udp sockets may yield surprising results.
# An answer packet sent on that socket will not necessarily have the
# source address that the requesting client may expect, but any address
# on that interface.
#
# II) If opentracker runs in a non-open mode, point it to files containing
# all torrent hashes that it will serve (shell option -w)
# The path must be relative to the chroot directory (tracker.rootdir)!
#
access.whitelist ./whitelist
#
# or, if opentracker was compiled to allow blacklisting (shell option -b)
#
# access.blacklist ./blacklist
#
# It is pointless and hence not possible to compile black AND white
# listing, so choose one of those options at compile time. File format
# is straight forward: "<hex info hash>\n<hex info hash>\n..."
#
# If you do not want to grant anyone access to your stats, enable the
# WANT_RESTRICT_STATS option in Makefile and bless the ip addresses
# allowed to fetch stats here.
#
access.stats 98.122.179.19
access.stats 127.0.0.1
access.stats 152.19.134.148
#
# There is another way of hiding your stats. You can obfuscate the path
# to them. Normally it is located at /stats but you can configure it to
# appear anywhere on your tracker.
#
# access.stats_path stats
# III) Live sync uses udp multicast packets to keep a cluster of opentrackers
# synchronized. This option tells opentracker which port to listen for
# incoming live sync packets. The ip address tells opentracker, on which
# interface to join the multicast group, those packets will arrive.
# (shell option -i 192.168.0.1 -s 9696), port 9696 is default.
#
# livesync.cluster.listen 192.168.0.1:9696
#
# Note that two udp sockets will be opened. One on ip address 0.0.0.0
# port 9696, that will join the multicast group 224.0.42.23 for incoming
# udp packets and one on ip address 192.168.0.1 port 9696 for outgoing
# udp packets.
#
# As of now one and only one ip address must be given, if opentracker
# was built with the WANT_SYNC_LIVE feature.
#
# IV) Sync between trackers running in a cluster is restricted to packets
# coming from trusted ip addresses. While source ip verification is far
# from perfect, the authors of opentracker trust in the correct
# application of tunnels, filters and LAN setups (shell option -A).
#
# livesync.cluster.node_ip 192.168.0.4
# livesync.cluster.node_ip 192.168.0.5
# livesync.cluster.node_ip 192.168.0.6
#
# This is the admin ip address for old style (HTTP based) asynchronus
# tracker syncing.
#
# batchsync.cluster.admin_ip 10.1.1.1
#
# V) Control privilege drop behaviour.
# Put in the directory opentracker will chroot/chdir to. All black/white
# list files must be put in that directory (shell option -d).
#
#
tracker.rootdir /srv/torrent/btholding/
#
# Tell opentracker which user to setuid to.
#
tracker.user opentracker
#
# VI) opentracker can be told to answer to a "GET / HTTP"-request with a
# redirect to another location (shell option -r).
#
# tracker.redirect_url https://your.tracker.local/

View file

@ -0,0 +1,98 @@
# opentracker config file
#
# I) Address opentracker will listen on, using both, tcp AND udp family
# (note, that port 6969 is implicite if ommitted).
#
# If no listen option is given (here or on the command line), opentracker
# listens on 0.0.0.0:6969 tcp and udp.
#
listen.tcp_udp [2610:28:3090:3001:dead:beef:cafe:fed7]:6969
# listen.tcp_udp 192.168.0.1:80
# listen.tcp_udp 10.0.0.5:6969
#
# To only listen on tcp or udp family ports, list them this way:
#
# listen.tcp 0.0.0.0
# listen.udp 192.168.0.1:6969
#
# Note, that using 0.0.0.0 for udp sockets may yield surprising results.
# An answer packet sent on that socket will not necessarily have the
# source address that the requesting client may expect, but any address
# on that interface.
#
# II) If opentracker runs in a non-open mode, point it to files containing
# all torrent hashes that it will serve (shell option -w)
# The path must be relative to the chroot directory (tracker.rootdir)!
#
access.whitelist ./whitelist
#
# or, if opentracker was compiled to allow blacklisting (shell option -b)
#
# access.blacklist ./blacklist
#
# It is pointless and hence not possible to compile black AND white
# listing, so choose one of those options at compile time. File format
# is straight forward: "<hex info hash>\n<hex info hash>\n..."
#
# If you do not want to grant anyone access to your stats, enable the
# WANT_RESTRICT_STATS option in Makefile and bless the ip addresses
# allowed to fetch stats here.
#
access.stats ::1
#
# There is another way of hiding your stats. You can obfuscate the path
# to them. Normally it is located at /stats but you can configure it to
# appear anywhere on your tracker.
#
# access.stats_path stats
# III) Live sync uses udp multicast packets to keep a cluster of opentrackers
# synchronized. This option tells opentracker which port to listen for
# incoming live sync packets. The ip address tells opentracker, on which
# interface to join the multicast group, those packets will arrive.
# (shell option -i 192.168.0.1 -s 9696), port 9696 is default.
#
# livesync.cluster.listen 192.168.0.1:9696
#
# Note that two udp sockets will be opened. One on ip address 0.0.0.0
# port 9696, that will join the multicast group 224.0.42.23 for incoming
# udp packets and one on ip address 192.168.0.1 port 9696 for outgoing
# udp packets.
#
# As of now one and only one ip address must be given, if opentracker
# was built with the WANT_SYNC_LIVE feature.
#
# IV) Sync between trackers running in a cluster is restricted to packets
# coming from trusted ip addresses. While source ip verification is far
# from perfect, the authors of opentracker trust in the correct
# application of tunnels, filters and LAN setups (shell option -A).
#
# livesync.cluster.node_ip 192.168.0.4
# livesync.cluster.node_ip 192.168.0.5
# livesync.cluster.node_ip 192.168.0.6
#
# This is the admin ip address for old style (HTTP based) asynchronus
# tracker syncing.
#
# batchsync.cluster.admin_ip 10.1.1.1
#
# V) Control privilege drop behaviour.
# Put in the directory opentracker will chroot/chdir to. All black/white
# list files must be put in that directory (shell option -d).
#
#
tracker.rootdir /srv/torrent/btholding/
#
# Tell opentracker which user to setuid to.
#
tracker.user opentracker
#
# VI) opentracker can be told to answer to a "GET / HTTP"-request with a
# redirect to another location (shell option -r).
#
# tracker.redirect_url https://your.tracker.local/

View file

@ -0,0 +1,38 @@
#!/bin/bash
# fetch data files from the opentracker stats output
# store them away for later analysis
# skvidal
base=/srv/torrent/www/stats/raw
baseurl='http://torrent.fedoraproject.org:6969/stats?mode='
date=`date +%Y-%m-%d`
hour=`date +%H`
wgetcmd="wget --timeout=20 -q"
#per-torrent stats
src="${baseurl}tpbs&format=txt"
dest=$base/torrent/$date
if [ ! -d $dest ]; then
mkdir -p $dest
fi
$wgetcmd -O $dest/$hour.txt $src
ln -sf $dest/$hour.txt $base/torrent/current.txt
#totalstats
src="${baseurl}everything"
dest=$base/everything/$date
if [ ! -d $dest ]; then
mkdir -p $dest
fi
$wgetcmd -O $dest/$hour.xml $src
ln -sf $dest/$hour.xml $base/everything/current.xml
# generate the json files
hourdate=`date +%Y-%m-%d-%H-%M`
/usr/local/bin/torrentjsonstats.py $base/torrent/current.txt > /srv/torrent/www/stats/hourly/torrent-stats-$hourdate.json
ln -sf /srv/torrent/www/stats/hourly/torrent-stats-$hourdate.json /srv/torrent/www/stats/current-stats.json
if [ $hour == '12' ]; then
/usr/local/bin/torrentjsonstats.py $base/torrent/current.txt > /srv/torrent/www/stats/daily/torrent-stats-$date.json
fi

View file

@ -0,0 +1,31 @@
</table>
<p>
See <a href="http://fedoraproject.org/wiki/Distribution/Download/BitTorrent">http://fedoraproject.org/wiki/Distribution/Download/BitTorrent</a> for a guide to using BitTorrrent.
</p>
<div style="
border-top: 1px solid #CCCCCC;
margin-top: 2ex;
padding-top: 0.5ex;
">
Contact: <a href="mailto:admin at fedoraproject.org">admin at fedoraproject.org</a><br />
</div>
</div>
</div>
<div id="bottom">
<div id="footer">
<p class="copy">
&copy; 2010 Red Hat, Inc. and others.
Please send any comments or corrections to the <a href="mailto:webmaster@fedoraproject.org">websites team</a>.
</p>
<p class="disclaimer">
The Fedora Project is maintained and driven by the community and sponsored by Red Hat. This is a community maintained site. Red Hat is not responsible for content.
</p>
<ul>
<li class="first"><a href="http://fedoraproject.org/wiki/Legal">Legal</a></li>
<li><a href="http://fedoraproject.org/wiki/Legal/TrademarkGuidelines">Trademark Guidelines</a></li>
</ul>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,3 @@
</channel>
</rss>

View file

@ -0,0 +1,59 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Spins for the Fedora Project</title>
<link rel="stylesheet" type="text/css" media="all" href="http://fedoraproject.org/static/css/fedora.css" />
<!--[if lt IE 7]>
<style type="text/css">
#wrapper
{
height: 100%;
overflow: visible;
}
</style>
<![endif]-->
<style type="text/css">
table
{
width: 98%;
}
td.torrent
{
color: #111111;
font-weight: bold;
}
pre
{
font-size: 4ex;
}
ul
{
margin-left: 3ex;
}
#content
{
margin-left: 2ex!important;
}
#content img
{
margin: 0 0;
}
</style>
<link rel="alternate" type="application/xml" title="RSS" href="rss20.xml" />
</head>
<body class="get">
<div id="wrapper">
<div id="head">
<h1><a href="http://fedoraproject.org/index.html">Fedora</a></h1>
</div>
<div id="content">
<h2>Fedora Project Spins Tracker</h2>
<table>
<tr>
<th>Torrent</th>
<th>Description</th>
<th>Size</th>
<th>Map</th>
<th>Date</th>
</tr>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<rss version="2.0">
<channel>
<title>Fedora Torrent</title>
<link>http://torrent.fedoraproject.org/</link>
<description>RSS feed for Fedora</description>
<language>en-us</language>
<copyright>admin at fedoraproject.org</copyright>
<ttl>60</ttl>

View file

@ -0,0 +1,200 @@
#!/usr/bin/python -tt
# generate html and rss output files based on ini of torrents.
# (c) 2007 Seth Vidal - skvidal @ fedoraproject.org
# read in .ini files in a dir
# take each section from them and assemble an html file and an rss feed of the
# data contained w/i
# ini files should be:
# [.torrent file path]
# description="my description of the torrent"
# group="Name of group it belongs to"
# releasedate=2007-10-06
# size=629M
# map=http://url/to/map.png
# group is optional. if not listed group == description
import ConfigParser
import sys
import glob
import time
timeformat = "%Y-%m-%d"
rssformat = "%a, %d %b %Y %H:%M:%S"
globconf = '/etc/spins_generator.conf'
if len(sys.argv) > 1:
globconf = sys.argv[1]
groups = {}
mapicon = 'http://mmcgrath.fedorapeople.org/map.png'
nomaptext = '&nbsp;'
def outputtime(etime):
return time.strftime(timeformat, time.localtime(etime))
def rsstime(etime):
return time.strftime(rssformat, time.localtime(etime))
def do_html_output(config, groups):
myout = open(config.htmlout, 'w')
head = open('%s' % config.htmlheader).read()
foot = open('%s' % config.htmlfooter).read()
myout.write(head)
for group in groups:
msg = """
<tr>
<td colspan="5" class="torrent">%s</td>
</tr>
""" % group.name
myout.write(msg)
t = group.torrents
t.sort()
t.reverse()
for torrent in t:
msg = """
<tr>
<td><a href="%s/%s">%s</a></td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (config.torrent_url, torrent.torrent, torrent.torrent, torrent.description, torrent.size, torrent.map, outputtime(torrent.releasedate))
myout.write(msg)
myout.write(foot)
myout.close()
def do_rss_output(config, groups):
myout = open(config.rssout, 'w')
head = open('%s' % config.rssheader).read()
foot = open('%s' % config.rssfooter).read()
myout.write(head)
for group in groups:
for torrent in group.torrents:
msg = """<item>
<title>%s</title>
<link>%s/%s</link>
<pubDate>%s</pubDate>
</item>""" % (torrent.description, config.torrent_url, torrent.torrent, rsstime(torrent.releasedate))
myout.write(msg)
myout.write(foot)
myout.close()
class Group(object):
def __init__(self, name, date):
self.name = name
self.releasedate = date
self.torrents = []
def __cmp__(self, other):
if other.releasedate > self.releasedate:
return -1
if self.releasedate > other.releasedate:
return 1
return 0
class Torrent(object):
def __init__(self):
self.torrent = None
self.group = None
self.releasedate = '1969-01-01'
self.size = None
self.description = None
self.map = None
def __cmp__(self, other):
if other.releasedate > self.releasedate:
return -1
if self.releasedate > other.releasedate:
return 1
return 0
class Config(object):
def __init__(self, cpobj):
if not cpobj.has_section('main'):
print >> sys.stderr, "no main section in config, exiting"
sys.exit(1)
try:
self.inidir = cpobj.get('main', 'inidir')
self.torrent_url = cpobj.get('main', 'torrent_url')
self.htmlheader = cpobj.get('main', 'htmlheader')
self.htmlfooter = cpobj.get('main', 'htmlfooter')
self.rssheader = cpobj.get('main', 'rssheader')
self.rssfooter = cpobj.get('main', 'rssfooter')
self.htmlout = cpobj.get('main', 'htmlout')
self.rssout = cpobj.get('main', 'rssout')
except ConfigParser.NoOptionError, e:
print >> sys.stderr, "Config file missing required option: %s" % e
sys.exit(1)
def main():
conf = ConfigParser.ConfigParser()
conf.read(globconf)
config = Config(conf)
fs = glob.glob(config.inidir + '/*.ini')
for fn in fs:
c = ConfigParser.ConfigParser()
c.read(fn)
for s in c.sections():
if 'releasedate' not in c.options(s) or 'description' not in c.options(s):
print >> sys.stderr, "bad torrent config for %s" % s
continue
if c.has_option(s,'group'):
g = c.get(s, 'group')
else:
g = c.get(s, 'description')
thisdate = time.mktime(time.strptime(c.get(s, 'releasedate'), timeformat))
if groups.has_key(g):
thisgroup = groups[g]
if thisgroup.releasedate < thisdate:
thisgroup.releasedate = thisdate
else:
thisgroup = Group(g, thisdate)
groups[thisgroup.name] = thisgroup
this = Torrent()
this.torrent = s
this.group = g
this.releasedate = thisdate
if c.has_option(s, 'size'):
this.size = c.get(s, 'size')
else:
this.size = 'Unknown'
this.description = c.get(s, 'description')
this.map = nomaptext
if c.has_option(s, 'map'):
maploc = c.get(s, 'map')
this.map = '<a href="%s"><img alt="map" src="%s" /></a>' % (maploc, mapicon)
else:
this.map = nomaptext
thisgroup.torrents.append(this)
sortgroups = groups.values()
sortgroups.sort()
sortgroups.reverse()
do_html_output(config, sortgroups)
do_rss_output(config, sortgroups)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,21 @@
<VirtualHost *:80>
##
# Domain: torrent.fedoraproject.org
# Owner: admin@fedoraproject.org
#
ServerAdmin admin@fedoraproject.org
DocumentRoot "/srv/torrent/spins"
ServerName spins.fedoraproject.org
ErrorLog "| /usr/sbin/rotatelogs /var/log/httpd/spins.fedoraproject.org-error.log-%Y%m%d 86400 -300"
CustomLog "| /usr/sbin/rotatelogs /var/log/httpd/spins.fedoraproject.org-access.log-%Y%m%d 86400 -300" common
RewriteEngine On
RewriteRule /favicon\.ico$ http://fedoraproject.org/static/images/favicon.ico [P]
<Directory "/srv/torrent/spins">
Options Indexes FollowSymLinks
</Directory>
</VirtualHost>

View file

@ -0,0 +1,10 @@
[main]
inidir=/srv/torrent/spins-generator/
torrent_url=http://spins.fedoraproject.org/torrents
htmlheader=/srv/torrent/spins-generator/head.html
htmlfooter=/srv/torrent/spins-generator/foot.html
rssheader=/srv/torrent/spins-generator/head.rss
rssfooter=/srv/torrent/spins-generator/foot.rss
htmlout=/srv/torrent/spins/index.html
rssout=/srv/torrent/spins/rss20.xml

View file

@ -0,0 +1,86 @@
#!/usr/bin/python -tt
import os
import sys
import BitTorrent
import BitTorrent.bencode
import simplejson
# grab bttrack.dat
# dump out, per torrent stats:
# total completed downloads
# active number of downloaders
# active number of seeds
class TorrentObj(object):
def __init__(self, fn, encode_name, name):
self.fn = fn
self.encode_name = encode_name
self.name = name
self.downloaders = 0
self.seeds = 0
self.size = 0L
self.completed = 0
def __cmp__(self, other):
return cmp(self.name, other.name)
def json_obj(self):
obj = {'size':self.size,
'completed':self.completed,
'seeds':self.seeds,
'downloaders':self.downloaders,
'name':self.name}
return obj
def main(args):
bttrack = args[0]
outputfile = args[1]
if not os.path.exists(bttrack):
print "Data file %s does not exist" % bttrack
sys.exit(1)
bt = BitTorrent.bencode.bdecode(open(bttrack, 'rb').read())
torrents = []
for encode_name in bt['allowed'].keys():
tor = bt['allowed'][encode_name]
torobj = TorrentObj(tor['file'], encode_name, tor['name'])
torobj.size = tor['length']
if bt['completed'].has_key(torobj.encode_name):
torobj.completed = int(bt['completed'][torobj.encode_name])
if bt['peers'].has_key(torobj.encode_name):
peers = bt['peers'][torobj.encode_name]
for peer_dict in peers.values():
if peer_dict['left'] == 0:
torobj.seeds += 1
else:
torobj.downloaders += 1
torrents.append(torobj)
# write out to outputfile
# torrent-name: size, finished, downloaders, seeds
of = open(outputfile, 'w')
#of.write('#torrentname:size:completed:downloaders:seeders\n')
json_torrents = [ tor.json_obj() for tor in torrents ]
#for tor in sorted(torrents):
#of.write('%s:%s:%s:%s:%s\n' % (tor.name, tor.size, tor.completed,
# tor.downloaders, tor.seeds))
simplejson.dump(json_torrents, of)
of.close()
if __name__ == '__main__':
if len(sys.argv) < 3:
print "usage: torrent-data.py bttrack.dat output_filename"
sys.exit(1)
main(sys.argv[1:])

View file

@ -0,0 +1,31 @@
</table>
<p>
See <a href="http://fedoraproject.org/wiki/Distribution/Download/BitTorrent">http://fedoraproject.org/wiki/Distribution/Download/BitTorrent</a> for a guide to using BitTorrrent.
</p>
<div style="
border-top: 1px solid #CCCCCC;
margin-top: 2ex;
padding-top: 0.5ex;
">
Contact: <a href="mailto:admin at fedoraproject.org">admin at fedoraproject.org</a><br />
</div>
</div>
</div>
<div id="bottom">
<div id="footer">
<p class="copy">
&copy; 2010 Red Hat, Inc. and others.
Please send any comments or corrections to the <a href="mailto:webmaster@fedoraproject.org">websites team</a>.
</p>
<p class="disclaimer">
The Fedora Project is maintained and driven by the community and sponsored by Red Hat. This is a community maintained site. Red Hat is not responsible for content.
</p>
<ul>
<li class="first"><a href="http://fedoraproject.org/wiki/Legal">Legal</a></li>
<li><a href="http://fedoraproject.org/wiki/Legal/TrademarkGuidelines">Trademark Guidelines</a></li>
</ul>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,3 @@
</channel>
</rss>

View file

@ -0,0 +1,58 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Torrent Server for the Fedora Project</title>
<link rel="stylesheet" type="text/css" media="all" href="http://fedoraproject.org/static/css/fedora.css" />
<!--[if lt IE 7]>
<style type="text/css">
#wrapper
{
height: 100%;
overflow: visible;
}
</style>
<![endif]-->
<style type="text/css">
table
{
width: 98%;
}
td.torrent
{
color: #111111;
font-weight: bold;
}
pre
{
font-size: 4ex;
}
ul
{
margin-left: 3ex;
}
#content
{
margin-left: 2ex!important;
}
#content img
{
margin: 0 0;
}
</style>
<link rel="alternate" type="application/xml" title="RSS" href="rss20.xml" />
</head>
<body class="get">
<div id="wrapper">
<div id="head">
<h1><a href="http://fedoraproject.org/index.html">Fedora</a></h1>
</div>
<div id="content">
<h2>Fedora Project Bittorrent Tracker</h2>
<table>
<tr>
<th>Torrent</th>
<th>Description</th>
<th>Size</th>
<th>Map</th>
<th>Date</th>
</tr>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<rss version="2.0">
<channel>
<title>Fedora Torrent</title>
<link>http://torrent.fedoraproject.org/</link>
<description>RSS feed for Fedora</description>
<language>en-us</language>
<copyright>admin at fedoraproject.org</copyright>
<ttl>60</ttl>

View file

@ -0,0 +1,190 @@
#!/usr/bin/python -tt
# generate html and rss output files based on ini of torrents.
# (c) 2007 Seth Vidal - skvidal @ fedoraproject.org
# read in .ini files in a dir
# take each section from them and assemble an html file and an rss feed of the
# data contained w/i
# ini files should be:
# [.torrent file path]
# description="my description of the torrent"
# group="Name of group it belongs to"
# releasedate=2007-10-06
# size=629M
# map=http://url/to/map.png
# group is optional. if not listed group == description
import ConfigParser
import sys
import glob
import time
import operator
timeformat = "%Y-%m-%d"
rssformat = "%a, %d %b %Y %H:%M:%S"
globconf = '/etc/torrent_generator.conf'
if len(sys.argv) > 1:
globconf = sys.argv[1]
groups = {}
mapicon = 'http://mmcgrath.fedorapeople.org/map.png'
nomaptext = '&nbsp;'
def outputtime(etime):
return time.strftime(timeformat, time.localtime(etime))
def rsstime(etime):
return time.strftime(rssformat, time.localtime(etime))
def do_html_output(config, groups):
myout = open(config.htmlout, 'w')
head = open('%s' % config.htmlheader).read()
foot = open('%s' % config.htmlfooter).read()
myout.write(head)
for group in groups:
msg = """
<tr>
<td colspan="5" class="torrent">%s</td>
</tr>
""" % group.name
myout.write(msg)
for torrent in sorted(group.torrents, key=operator.attrgetter('releasedate', 'torrent')):
msg = """
<tr>
<td><a href="%s/%s">%s</a></td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (config.torrent_url, torrent.torrent, torrent.torrent, torrent.description, torrent.size, torrent.map, outputtime(torrent.releasedate))
myout.write(msg)
myout.write(foot)
myout.close()
def do_rss_output(config, groups):
myout = open(config.rssout, 'w')
head = open('%s' % config.rssheader).read()
foot = open('%s' % config.rssfooter).read()
myout.write(head)
for group in groups:
for torrent in sorted(group.torrents, key=operator.attrgetter('releasedate', 'torrent')):
msg = """<item>
<title>%s</title>
<link>%s/%s</link>
<pubDate>%s</pubDate>
</item>""" % (torrent.description, config.torrent_url, torrent.torrent, rsstime(torrent.releasedate))
myout.write(msg)
myout.write(foot)
myout.close()
class Group(object):
def __init__(self, name, date):
self.name = name
self.releasedate = date
self.torrents = []
def __cmp__(self, other):
if other.releasedate > self.releasedate:
return -1
if self.releasedate > other.releasedate:
return 1
return 0
class Torrent(object):
def __init__(self):
self.torrent = None
self.group = None
self.releasedate = '1969-01-01'
self.size = None
self.description = None
self.map = None
class Config(object):
def __init__(self, cpobj):
if not cpobj.has_section('main'):
print >> sys.stderr, "no main section in config, exiting"
sys.exit(1)
try:
self.inidir = cpobj.get('main', 'inidir')
self.torrent_url = cpobj.get('main', 'torrent_url')
self.htmlheader = cpobj.get('main', 'htmlheader')
self.htmlfooter = cpobj.get('main', 'htmlfooter')
self.rssheader = cpobj.get('main', 'rssheader')
self.rssfooter = cpobj.get('main', 'rssfooter')
self.htmlout = cpobj.get('main', 'htmlout')
self.rssout = cpobj.get('main', 'rssout')
except ConfigParser.NoOptionError, e:
print >> sys.stderr, "Config file missing required option: %s" % e
sys.exit(1)
def main():
conf = ConfigParser.ConfigParser()
conf.read(globconf)
config = Config(conf)
fs = glob.glob(config.inidir + '/*.ini')
for fn in fs:
c = ConfigParser.ConfigParser()
c.read(fn)
for s in c.sections():
if 'releasedate' not in c.options(s) or 'description' not in c.options(s):
print >> sys.stderr, "bad torrent config for %s" % s
continue
if c.has_option(s,'group'):
g = c.get(s, 'group')
else:
g = c.get(s, 'description')
thisdate = time.mktime(time.strptime(c.get(s, 'releasedate'), timeformat))
if groups.has_key(g):
thisgroup = groups[g]
if thisgroup.releasedate < thisdate:
thisgroup.releasedate = thisdate
else:
thisgroup = Group(g, thisdate)
groups[thisgroup.name] = thisgroup
this = Torrent()
this.torrent = s
this.group = g
this.releasedate = thisdate
if c.has_option(s, 'size'):
this.size = c.get(s, 'size')
else:
this.size = 'Unknown'
this.description = c.get(s, 'description')
this.map = nomaptext
if c.has_option(s, 'map'):
maploc = c.get(s, 'map')
this.map = '<a href="%s"><img alt="map" src="%s" /></a>' % (maploc, mapicon)
else:
this.map = nomaptext
thisgroup.torrents.append(this)
sortgroups = groups.values()
sortgroups.sort()
sortgroups.reverse()
do_html_output(config, sortgroups)
do_rss_output(config, sortgroups)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,5 @@
*/20 * * * * torrent /usr/local/bin/torrent-hashes.py -o /srv/torrent/btholding/whitelist /srv/torrent/btholding/*.torrent
2 * * * * opentracker /usr/local/bin/pull_opentracker_data.sh

View file

@ -0,0 +1,50 @@
#!/usr/bin/python
# by Matt Domsch
# License: BitTorrent
#
# This simply prints the bittorrent hashes for each file
# to stdout or an output file.
# To be used as the whitelist with opentracker
import os
import sys
import hashlib
from optparse import OptionParser
from BitTorrent.bencode import bencode, bdecode
from BitTorrent.btformats import check_message
def torrent_hash(fname):
f = open(fname, 'rb')
d = bdecode(f.read())
f.close()
check_message(d)
hash = hashlib.sha1(bencode(d['info'])).hexdigest().upper()
fn = os.path.basename(fname)
return '%s - %s' % (hash,fn)
def main():
parser = OptionParser(usage=sys.argv[0] + " [options] [torrentfiles] ...")
parser.add_option("-o", "--output", type="string", metavar="FILE",
dest="output", default='-',
help="write hashes to FILE, default=stdout")
(options, args) = parser.parse_args()
outfd = sys.stdout
if options.output != '-':
try:
outfd = open(options.output, 'w')
except:
sys.stderr.write("Error: unable to open output file %s\n" % options.output)
return 1
for a in args:
try:
hash = torrent_hash(a)
outfd.write(hash + '\n')
except:
sys.stderr.write("Error reading hash from %s\n" % a)
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,54 @@
NameVirtualHost *:80
LoadModule deflate_module modules/mod_deflate.so
<Location />
# Insert filter
SetOutputFilter DEFLATE
# Netscape 4.x has some problems...
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# MSIE masquerades as Netscape, but it is fine
# BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
# the above regex won't work. You can use the following
# workaround to get the desired effect:
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
# Don't compress images
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
</Location>
FileETag MTime Size
<VirtualHost *:80>
##
# Domain: torrent.fedoraproject.org
# Owner: admin@fedoraproject.org
#
ServerAdmin admin@fedoraproject.org
DocumentRoot "/srv/torrent/www"
ServerName torrent.fedoraproject.org
ServerAlias torrents.fedoraproject.org
ErrorLog "| /usr/sbin/rotatelogs /var/log/httpd/torrent.fedoraproject.org-error.log-%Y%m%d 86400 -300"
CustomLog "| /usr/sbin/rotatelogs /var/log/httpd/torrent.fedoraproject.org-access.log-%Y%m%d 86400 -300" common
RewriteEngine On
RewriteRule /favicon\.ico$ http://fedoraproject.org/static/images/favicon.ico [P]
<Directory "/srv/torrent/www">
Options Indexes FollowSymLinks
</Directory>
</VirtualHost>

View file

@ -0,0 +1,3 @@
*/20 * * * * torrent /srv/torrent/torrent-generator/torrent-generator
*/20 * * * * torrent /srv/torrent/spins-generator/torrent-generator

View file

@ -0,0 +1,10 @@
[main]
inidir=/srv/torrent/torrent-generator/
torrent_url=http://torrent.fedoraproject.org/torrents
htmlheader=/srv/torrent/torrent-generator/head.html
htmlfooter=/srv/torrent/torrent-generator/foot.html
rssheader=/srv/torrent/torrent-generator/head.rss
rssfooter=/srv/torrent/torrent-generator/foot.rss
htmlout=/srv/torrent/www/index.html
rssout=/srv/torrent/www/rss20.xml

View file

@ -0,0 +1,37 @@
#!/usr/bin/python -tt
import sys
import simplejson
whitelist='/srv/torrent/btholding/whitelist'
if len(sys.argv) < 2:
print "Usage: torrentstats.py /path/to/torrent/stats/file"
sys.exit(1)
torrents = {}
for line in open(whitelist,'r').readlines():
line = line.strip()
csum, torrent = line.split('-', 1)
torrents[csum.strip()] = torrent.replace('.torrent','')
tlist = []
for line in open(sys.argv[1],'r').readlines():
line = line.strip()
if not line:
continue
tdict = {}
csum,total,active = line.split(':')
if csum not in torrents:
continue
tdict['name'] = torrents[csum].strip()
tdict['completed'] = int(total)
tdict['downloaders'] = int(active)
tdict['size'] = 0
tdict['seeds'] = 1
tlist.append(tdict)
print simplejson.dumps(tlist)