]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blobdiff - www/webapp/backend/releases.py
Add torrent and magnet links for all downloads.
[people/shoehn/ipfire.org.git] / www / webapp / backend / releases.py
index 8dc7e6c3db7a3a4999a9cd1e9edce28fed984708..6701f7551b0cdf66d45a472b660f7aae53c7bb50 100644 (file)
@@ -1,8 +1,14 @@
 #!/usr/bin/python
 
+import hashlib
 import logging
+import os
+import re
+import urllib
 import urlparse
 
+import tracker
+
 from databases import Databases
 from misc import Singleton
 from settings import Settings
@@ -20,7 +26,37 @@ class File(object):
 
        @property
        def type(self):
-               return self.__data.get("filetype")
+               filename = self.filename
+
+               if filename.endswith(".iso"):
+                       return "iso"
+
+               elif filename.endswith(".torrent"):
+                       return "torrent"
+
+               elif "xen" in filename:
+                       return "xen"
+
+               elif "sources" in filename:
+                       return "source"
+
+               elif "usb-fdd" in filename:
+                       return "usbfdd"
+
+               elif "usb-hdd" in filename:
+                       return "usbhdd"
+
+               elif "armv5tel" in filename:
+                       return "armv5tel"
+
+               elif "scon" in filename:
+                       return "alix"
+
+               elif filename.endswith(".img.gz"):
+                       return "flash"
+
+               else:
+                       return "unknown"
 
        @property
        def url(self):
@@ -33,6 +69,7 @@ class File(object):
                _ = lambda x: x
 
                descriptions = {
+                       "armv5tel"      : _("Image for the armv5tel architecture"),
                        "iso"           : _("Installable CD image"),
                        "torrent"       : _("Torrent file"),
                        "flash"         : _("Flash image"),
@@ -56,6 +93,7 @@ class File(object):
                        "alix"          : 41,
                        "usbfdd"        : 31,
                        "usbhdd"        : 30,
+                       "armv5tel"  : 40,
                        "xen"           : 50,
                }
                
@@ -69,6 +107,7 @@ class File(object):
                _ = lambda x: x
        
                remarks = {
+                       "armv5tel"      : _("This image runs on many ARM-based boards"),
                        "iso"           : _("Use this image to burn a CD and install IPFire from it."),
                        "torrent"       : _("Download the CD image from the torrent network."),
                        "flash"         : _("An image that is meant to run on embedded devices."),
@@ -91,6 +130,44 @@ class File(object):
        def filename(self):
                return self.__data.get("filename")
 
+       @property
+       def basename(self):
+               return os.path.basename(self.filename)
+
+       @property
+       def size(self):
+               return self.__data.get("filesize")
+
+       @property
+       def arch(self):
+               known_arches = ("i586", "arm")
+
+               for arch in known_arches:
+                       if arch in self.basename:
+                               return arch
+
+               return "N/A"
+
+       @property
+       def torrent_hash(self):
+               return self.__data.get("torrent_hash", None)
+
+       @property
+       def magnet_link(self):
+               # Don't return anything if we have no torrent hash.
+               if self.torrent_hash is None:
+                       return
+
+               s = "magnet:?xt=urn:btih:%s" % self.torrent_hash
+
+               #s += "&xl=%d" % self.size
+               s += "&dn=%s" % urllib.quote(self.basename)
+
+               # Add our tracker.
+               s += "&tr=http://tracker.ipfire.org:6969/announce"
+
+               return s
+
 
 class Release(object):
        @property
@@ -113,10 +190,10 @@ class Release(object):
        @property
        def files(self):
                if not self.__files:
-                       files = self.db.query("SELECT id FROM files WHERE releases = %s \
+                       files = self.db.query("SELECT id, filename FROM files WHERE releases = %s \
                                        AND loadable = 'Y'", self.id)
 
-                       self.__files = [File(self, f.id) for f in files]
+                       self.__files = [File(self, f.id) for f in files if not f.filename.endswith(".torrent")]
                        self.__files.sort(lambda a, b: cmp(a.prio, b.prio))
 
                return self.__files
@@ -138,16 +215,100 @@ class Release(object):
                return self.__data.get("date")
 
        @property
-       def torrent_hash(self):
-               h = self.__data.get("torrent_hash")
-               if h:
-                       return h.lower()
+       def path(self):
+               return self.__data.get("path")
 
        def get_file(self, type):
                for file in self.files:
                        if file.type == type:
                                return file
 
+       def __file_hash(self, filename):
+               sha1 = hashlib.sha1()
+
+               with open(filename) as f:
+                       buf_size = 1024
+                       buf = f.read(buf_size)
+                       while buf:
+                               sha1.update(buf)
+                               buf = f.read(buf_size)
+
+               return sha1.hexdigest()
+
+       def scan_files(self, basepath="/srv/mirror0"):
+               if not self.path:
+                       return
+
+               path = os.path.join(basepath, self.path)
+               if not os.path.exists(path):
+                       return
+
+               files = self.db.query("SELECT filename FROM files WHERE releases = %s", self.id)
+               files = [f.filename for f in files]
+
+               # Make files that do not exists not loadable.
+               for filename in files:
+                       _filename = os.path.join(basepath, filename)
+                       if not os.path.exists(_filename):
+                               self.db.execute("UPDATE files SET loadable='N' WHERE filename = %s", filename)
+
+               for filename in os.listdir(path):
+                       filename = os.path.join(path, filename)
+
+                       if os.path.isdir(filename):
+                               continue
+
+                       _filename = re.match(".*(releases/.*)", filename).group(1)
+                       if _filename in files:
+                               continue
+
+                       if filename.endswith(".md5"):
+                               continue
+
+                       filehash = self.__file_hash(filename)
+                       filesize = os.path.getsize(filename)
+
+                       # Check if there is a torrent download available for this file:
+                       torrent_hash = ""
+                       torrent_file = "%s.torrent" % filename
+                       if os.path.exists(torrent_file):
+                               torrent_hash = self.torrent_read_hash(torrent_file)
+
+                       self.db.execute("INSERT INTO files(releases, filename, filesize, \
+                               sha1, torrent_hash) VALUES(%s, %s, %s, %s, %s)",
+                               self.id, _filename, filesize, filehash, torrent_hash)
+
+               # Search for all files that miss a torrent hash.
+               files = self.db.query("SELECT id, filename FROM files \
+                       WHERE releases = %s AND torrent_hash IS NULL", self.id)
+
+               for file in files:
+                       path = os.path.join(basepath, file.filename)
+
+                       torrent_file = "%s.torrent" % path
+                       if os.path.exists(torrent_file):
+                               torrent_hash = self.torrent_read_hash(torrent_file)
+
+                               self.db.execute("UPDATE files SET torrent_hash = %s WHERE id = %s",
+                                       torrent_hash, file.id)
+
+       def torrent_read_hash(self, filename):
+               f = None
+               try:
+                       f = open(filename, "rb")
+
+                       metainfo = tracker.bdecode(f.read())
+                       metainfo = tracker.bencode(metainfo["info"])
+
+                       hash = hashlib.sha1()
+                       hash.update(metainfo)
+
+                       return hash.hexdigest()
+
+               finally:
+                       if f:
+                               f.close()
+
 
 class Releases(object):
        __metaclass__ = Singleton
@@ -197,6 +358,15 @@ class Releases(object):
 
                return [Release(r.id) for r in releases]
 
+       def get_filename_for_torrent_hash(self, torrent_hash):
+               file = self.db.get("SELECT filename FROM files WHERE torrent_hash = %s LIMIT 1",
+                       torrent_hash)
+
+               if not file:
+                       return
+
+               return file.filename
+
 
 if __name__ == "__main__":
        r = Releases()