]> git.ipfire.org Git - people/shoehn/ipfire.org.git/commitdiff
Add torrent and magnet links for all downloads.
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 26 Jul 2012 10:29:23 +0000 (12:29 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 26 Jul 2012 10:29:23 +0000 (12:29 +0200)
www/templates/modules/release-item.html
www/templates/tracker-torrents.html
www/translations/de_DE/LC_MESSAGES/webapp.po
www/translations/webapp.pot
www/webapp/__init__.py
www/webapp/backend/releases.py
www/webapp/handlers_tracker.py

index c0df5a0f91483e66d525117c61c4cdc845ed8de3..1c3dfcff2bb4f83d3612aa71cc45f138e67500ba 100644 (file)
                                                        {% for file in files[arch] %}
                                                                <tr>
                                                                        <td>
+                                                                               {% if file.torrent_hash %}
+                                                                                       <p class="pull-right">
+                                                                                               <a href="{{ file.magnet_link }}">
+                                                                                                       <i class="icon-magnet"></i>
+                                                                                               </a>
+                                                                                               <a href="http://tracker.ipfire.org/{{ file.torrent_hash }}/download">
+                                                                                                       <i class="icon-download-alt"></i>
+                                                                                               </a>
+                                                                                       </p>
+                                                                               {% end %}
                                                                                <a class="download-splash" href="{{ file.url }}">{{ _(file.desc) }}</a>
                                                                                <br>{{ _(file.rem) }}
                                                                        </td>
                                                        {% end %}
                                                </tbody>                                                        
                                        </table>
+
+                                       <p class="pull-right">
+                                               <strong>{{ _("Legend:") }}</strong>
+                                               <i class="icon-magnet"></i> {{ _("Magnet link") }},
+                                               <i class="icon-download-alt"></i> {{ _("Torrent download") }}
+                                       </p>
                                </div>
                        {% end %}
                </div>
index 3a6ec13ac8b8ddad0a1fa149aee19e101bc679a2..8364ee1bbd5837d086fc54aab3162dcbb7b8544d 100644 (file)
@@ -40,5 +40,4 @@
                                </tr>
                        {% end %}
                </table>
-
 {% end block %}
index 06c97274e4685b8fe6b160db5e92fc1352f0142c..74a128be32310c977781fedef262acf5fbf0cfcc 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-25 20:13+0200\n"
+"POT-Creation-Date: 2012-07-26 12:27+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -865,7 +865,19 @@ msgstr "Imagetyp"
 msgid "Size"
 msgstr "Größe"
 
-#: templates/modules/release-item.html:115
+#: templates/modules/release-item.html:121
+msgid "Legend:"
+msgstr "Legende:"
+
+#: templates/modules/release-item.html:122
+msgid "Magnet link"
+msgstr "Magnet-Link"
+
+#: templates/modules/release-item.html:123
+msgid "Torrent download"
+msgstr "Torrent-Download"
+
+#: templates/modules/release-item.html:131
 #: templates/modules/release-item-short.html:13
 msgid "There are no downloads available for this release."
 msgstr "Es gibt keine Downloads für dieses Release."
@@ -1281,65 +1293,73 @@ msgstr "Unbekannter Autor"
 msgid "%s to %s"
 msgstr "%s nach %s"
 
-#: webapp/backend/releases.py:39
+#: webapp/backend/releases.py:73
+msgid "Image for the armv5tel architecture"
+msgstr "Image für die armv5tel-Architektur"
+
+#: webapp/backend/releases.py:74
 msgid "Installable CD image"
 msgstr "Bootbares CD-Image"
 
-#: webapp/backend/releases.py:40
+#: webapp/backend/releases.py:75
 msgid "Torrent file"
 msgstr "Torrentdatei"
 
-#: webapp/backend/releases.py:41
+#: webapp/backend/releases.py:76
 msgid "Flash image"
 msgstr "Flash-Image"
 
-#: webapp/backend/releases.py:42
+#: webapp/backend/releases.py:77
 msgid "Alix image"
 msgstr "Alix-Image"
 
-#: webapp/backend/releases.py:43
+#: webapp/backend/releases.py:78
 msgid "USB FDD Image"
 msgstr "USB-FDD-Image"
 
-#: webapp/backend/releases.py:44
+#: webapp/backend/releases.py:79
 msgid "USB HDD Image"
 msgstr "USB-HDD-Image"
 
-#: webapp/backend/releases.py:45
+#: webapp/backend/releases.py:80
 msgid "Pregenerated Xen image"
 msgstr "Vorgefertigtes Xen-Image"
 
-#: webapp/backend/releases.py:51 webapp/backend/releases.py:88
+#: webapp/backend/releases.py:86 webapp/backend/releases.py:124
 msgid "Unknown image type"
 msgstr "Unbekanntes Imageformat"
 
-#: webapp/backend/releases.py:76
+#: webapp/backend/releases.py:111
+msgid "This image runs on many ARM-based boards"
+msgstr "Dieses Image läuft auf vielen ARM-basierten Boards"
+
+#: webapp/backend/releases.py:112
 msgid "Use this image to burn a CD and install IPFire from it."
 msgstr ""
 "Nutze dieses Image um eine CD zu erstellen und IPFire von dieser zu "
 "installieren."
 
-#: webapp/backend/releases.py:77
+#: webapp/backend/releases.py:113
 msgid "Download the CD image from the torrent network."
 msgstr "CD-Image mit aus dem Torrentnetzwerk laden"
 
-#: webapp/backend/releases.py:78
+#: webapp/backend/releases.py:114
 msgid "An image that is meant to run on embedded devices."
 msgstr "Ein Image, das für eingebettete Systeme optimiert ist."
 
-#: webapp/backend/releases.py:79
+#: webapp/backend/releases.py:115
 msgid "Flash image where a serial console is enabled by default."
 msgstr "Image, bei welchem die serielle Konsole eingeschaltet ist."
 
-#: webapp/backend/releases.py:80
+#: webapp/backend/releases.py:116
 msgid "Install IPFire from a floppy-formated USB key."
 msgstr "IPFire von einem Floppy-formatiertem USB-Stick installieren."
 
-#: webapp/backend/releases.py:81
+#: webapp/backend/releases.py:117
 msgid "If the floppy image doesn't work, use this image instead."
 msgstr "Wenn das Floppy-Image nicht funktioniert, dieses verwenden."
 
-#: webapp/backend/releases.py:82
+#: webapp/backend/releases.py:118
 msgid "A ready-to-run image for Xen."
 msgstr "Ein fertiges Image für Xen."
 
index 23a93995a08b751c23e0a583969590a101181b74..fcc3385d390234b531071e39f67bd8a3dfe75030 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-25 20:13+0200\n"
+"POT-Creation-Date: 2012-07-26 12:27+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -857,7 +857,19 @@ msgstr ""
 msgid "Size"
 msgstr ""
 
-#: templates/modules/release-item.html:115
+#: templates/modules/release-item.html:121
+msgid "Legend:"
+msgstr ""
+
+#: templates/modules/release-item.html:122
+msgid "Magnet link"
+msgstr ""
+
+#: templates/modules/release-item.html:123
+msgid "Torrent download"
+msgstr ""
+
+#: templates/modules/release-item.html:131
 #: templates/modules/release-item-short.html:13
 msgid "There are no downloads available for this release."
 msgstr ""
@@ -1273,63 +1285,71 @@ msgstr ""
 msgid "%s to %s"
 msgstr ""
 
-#: webapp/backend/releases.py:39
+#: webapp/backend/releases.py:73
+msgid "Image for the armv5tel architecture"
+msgstr ""
+
+#: webapp/backend/releases.py:74
 msgid "Installable CD image"
 msgstr ""
 
-#: webapp/backend/releases.py:40
+#: webapp/backend/releases.py:75
 msgid "Torrent file"
 msgstr ""
 
-#: webapp/backend/releases.py:41
+#: webapp/backend/releases.py:76
 msgid "Flash image"
 msgstr ""
 
-#: webapp/backend/releases.py:42
+#: webapp/backend/releases.py:77
 msgid "Alix image"
 msgstr ""
 
-#: webapp/backend/releases.py:43
+#: webapp/backend/releases.py:78
 msgid "USB FDD Image"
 msgstr ""
 
-#: webapp/backend/releases.py:44
+#: webapp/backend/releases.py:79
 msgid "USB HDD Image"
 msgstr ""
 
-#: webapp/backend/releases.py:45
+#: webapp/backend/releases.py:80
 msgid "Pregenerated Xen image"
 msgstr ""
 
-#: webapp/backend/releases.py:51 webapp/backend/releases.py:88
+#: webapp/backend/releases.py:86 webapp/backend/releases.py:124
 msgid "Unknown image type"
 msgstr ""
 
-#: webapp/backend/releases.py:76
+#: webapp/backend/releases.py:111
+msgid "This image runs on many ARM-based boards"
+msgstr ""
+
+#: webapp/backend/releases.py:112
 msgid "Use this image to burn a CD and install IPFire from it."
 msgstr ""
 
-#: webapp/backend/releases.py:77
+#: webapp/backend/releases.py:113
 msgid "Download the CD image from the torrent network."
 msgstr ""
 
-#: webapp/backend/releases.py:78
+#: webapp/backend/releases.py:114
 msgid "An image that is meant to run on embedded devices."
 msgstr ""
 
-#: webapp/backend/releases.py:79
+#: webapp/backend/releases.py:115
 msgid "Flash image where a serial console is enabled by default."
 msgstr ""
 
-#: webapp/backend/releases.py:80
+#: webapp/backend/releases.py:116
 msgid "Install IPFire from a floppy-formated USB key."
 msgstr ""
 
-#: webapp/backend/releases.py:81
+#: webapp/backend/releases.py:117
 msgid "If the floppy image doesn't work, use this image instead."
 msgstr ""
 
-#: webapp/backend/releases.py:82
+#: webapp/backend/releases.py:118
 msgid "A ready-to-run image for Xen."
 msgstr ""
 
index 69e03e3051883c611509603b6aded9399e62d2ab..e23b68fcb1d9e8e18621f5a3189bac4b456bbb2e 100644 (file)
@@ -149,6 +149,8 @@ class Application(tornado.web.Application):
                        (r"/a.*", TrackerAnnounceHandler),
                        (r"/scrape", TrackerScrapeHandler),
                        (r"/torrent/([0-9a-f]+)", TrackerDetailHandler),
+                       (r"/([0-9a-f]{40})", TrackerDetailHandler),
+                       (r"/([0-9a-f]{40})/download", TrackerDownloadHandler),
                ] + static_handlers)
 
                # boot.ipfire.org
index b03f21f695e08b3984355f7117d6838c4307c8cf..6701f7551b0cdf66d45a472b660f7aae53c7bb50 100644 (file)
@@ -4,8 +4,11 @@ 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
@@ -23,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):
@@ -36,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"),
@@ -59,7 +93,7 @@ class File(object):
                        "alix"          : 41,
                        "usbfdd"        : 31,
                        "usbhdd"        : 30,
-                       "arm"       : 40,
+                       "armv5tel"  : 40,
                        "xen"           : 50,
                }
                
@@ -73,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."),
@@ -111,7 +146,27 @@ class File(object):
                        if arch in self.basename:
                                return arch
 
-               return "N/A" 
+               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):
@@ -135,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 \
-                                       AND loadable = 'Y' AND NOT filetype = 'torrent'", self.id)
+                       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
@@ -163,12 +218,6 @@ class Release(object):
        def path(self):
                return self.__data.get("path")
 
-       @property
-       def torrent_hash(self):
-               h = self.__data.get("torrent_hash")
-               if h:
-                       return h.lower()
-
        def get_file(self, type):
                for file in self.files:
                        if file.type == type:
@@ -186,46 +235,16 @@ class Release(object):
 
                return sha1.hexdigest()
 
-       def __guess_filetype(self, filename):
-               if filename.endswith(".iso"):
-                       return "iso"
-
-               if filename.endswith(".torrent"):
-                       return "torrent"
-
-               if "xen" in filename:
-                       return "xen"
-
-               if "sources" in filename:
-                       return "source"
-
-               if "usb-fdd" in filename:
-                       return "usbfdd"
-
-               if "usb-hdd" in filename:
-                       return "usbhdd"
-
-               if "arm" in filename:
-                       return "arm"
-
-               if "scon" in filename:
-                       return "alix"
-
-               if filename.endswith(".img.gz"):
-                       return "flash"
-
-               return "unknown"
-
        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 = [f.filename for f in self.files]
+               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:
@@ -248,10 +267,47 @@ class Release(object):
 
                        filehash = self.__file_hash(filename)
                        filesize = os.path.getsize(filename)
-                       filetype = self.__guess_filetype(filename)
 
-                       self.db.execute("""INSERT INTO files(releases, filename, filesize, filetype, sha1)
-                               VALUES(%s, %s, %s, %s, %s)""", self.id, _filename, filesize, filetype, filehash)
+                       # 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):
@@ -302,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()
index c57dc7fbd6c03234c7d035579a99f5a13d87ce81..184ea5052e5bdedc417e81e516d6b05f88e73de6 100644 (file)
@@ -48,6 +48,17 @@ class TrackerDetailHandler(BaseHandler):
                self.render("tracker-torrent-detail.html", release=release, torrent=torrent)
 
 
+class TrackerDownloadHandler(BaseHandler):
+       def get(self, torrent_hash):
+               file = self.releases.get_filename_for_torrent_hash(torrent_hash)
+
+               if not file:
+                       raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % hash)
+
+               # Redirect the user to the download redirector.
+               self.redirect("http://downloads.ipfire.org/%s.torrent" % file)
+
+
 #class TrackerTorrentsHandler(BaseHandler):
 #      @property
 #      def tracker(self):