BTPropertyPage.py v0.1.1 @ idontsmoke.co.uk

Written by

Python source code for the BitTorrent Property Page for Nautilus project. Back to the project page »

#!/usr/bin/python

# Copyright (C) 2004 Paul Sowden
# This code is released under the terms of the LGPL

import gtk, gobject, urllib, time
import nautilus
import BitTorrent.bencode
from types import *

COL_NAME = 0
COL_SIZE = 1

class BTPropertyPage(nautilus.PropertyPageProvider):
    def __init__(self):
        pass

    def get_property_pages(self, files):

        if len(files) != 1: return
        file = files[0]

        if file.get_uri_scheme() != 'file': return
        if not file.is_mime_type('application/x-bittorrent'): return

        filename = urllib.unquote(file.get_uri()[7:])
        torrent_file = open(filename).read(0x200000) # < 2 MB
        torrent_info = BitTorrent.bencode.bdecode(torrent_file)

        self.property_label = gtk.Label('BitTorrent')
        self.property_label.show()

        self.vbox = gtk.VBox(False, 12)
        self.vbox.set_border_width(12)

        info = []
        info.append(('Tracker URL', torrent_info['announce']))

        # optional fields
        if torrent_info.has_key('created by'): info.append(('Created By', torrent_info['created by']))
        if torrent_info.has_key('creation date'): info.append(('Created On', self.format_date(torrent_info['creation date'])))
        if torrent_info.has_key('comment'): info.append(('Comment', torrent_info['comment']))

        self.vbox.pack_start(
            self.create_frame('Info', self.create_info_table(info)), False, False)

        # d/l consists of multiple files
        if torrent_info['info'].has_key('files'):
            files = []
            totalsize = 0
            for file in torrent_info['info']['files']:
                fileloc = files
                if len(file['path']) > 1:
                    for dir in file['path'][:-1]:
                        for entry in fileloc:
                            if entry[0] == dir:
                                fileloc = entry[1]
                                break
                        else:
                            fileloc.append((dir, []))
                            fileloc = fileloc[-1][1]
                fileloc.append((file['path'][-1], file['length']))
                totalsize += file['length']

            store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT)
            for file in files: self.populate_treestore(store, None, file)

            treeview, scrollview = self.create_treeview(store)

            fileinfo = []
            fileinfo.append(('Suggested Folder', torrent_info['info']['name']))
            fileinfo.append(('Total Size', self.format_size(totalsize)))
            #fileinfo.append(('# Pieces', torrent_info['info']['piece length']))

            vbox = gtk.VBox(False, 12)
            vbox.pack_start(self.create_info_table(fileinfo), False, False)
            vbox.pack_start(scrollview)

            self.vbox.pack_start(self.create_frame('Files', vbox, 1.0, 1.0))
        else:
            fileinfo = []
            fileinfo.append(('Suggested Name', torrent_info['info']['name']))
            fileinfo.append(('Size', self.format_size(torrent_info['info']['length'])))
            #fileinfo.append(('# Pieces', torrent_info['info']['piece length']))

            # optional fields
            if torrent_info['info'].has_key('md5sum'): fileinfo.append(('MD5 Checksum', torrent_info['info']['md5sum']))

            self.vbox.pack_start(self.create_frame('File', self.create_info_table(fileinfo)))

        self.vbox.show_all()

        return nautilus.PropertyPage("NautilusPython::bittorrent",
                        self.property_label, self.vbox),

    def format_size(self, size):
        if size < 1024: return '%d B' % size
        elif size < 1048576: return '%.1f KiB' % (size / 1024.0)
        elif size < 1073741824: return '%.1f MiB' % (size / 1048576.0)
        else: return '%.1f GiB' % (size / 1073741824.0)

    def format_date(self, datetime):
        return time.strftime('%A, %B %d %Y at %H:%M:%S', time.localtime(datetime))

    def populate_treestore(self, store, parentiter, entry):
        childiter = store.insert_before(parentiter, None)
        store.set_value(childiter, COL_NAME, entry[0])
        if type(entry[1]) is ListType:
            # directory
            store.set_value(childiter, COL_SIZE, len(entry[1]))
            for childentry in entry[1]:
                self.populate_treestore(store, childiter, childentry)
        else:
            #file
            store.set_value(childiter, COL_SIZE, entry[1])

    def size_cell_data_function (self, column, cell, model, iter, user_data=None):
        size = model.get_value(iter, COL_SIZE)

        if model.iter_has_child(iter): text = '%d items' % size
        else: text = self.format_size(size)

        cell.set_property('text', text)

    def create_treeview(self, model):
        treeview = gtk.TreeView(model)

        # first column
        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn("File", renderer, text=COL_NAME)
        treeview.append_column(column)

        # second column 
        renderer = gtk.CellRendererText()
        renderer.set_property('xalign', 1.0)
        column = gtk.TreeViewColumn("Size", renderer, text=COL_SIZE)
        column.set_cell_data_func(renderer, self.size_cell_data_function)
        treeview.append_column(column)

        # Create scrollbars around the view.
        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.add(treeview)

        return treeview, scrolled

    def create_frame(self, labeltext, view, xscale=0.0, yscale=0.0):
        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_NONE)

        label = gtk.Label()
        label.set_use_markup(True)
        label.set_markup('<b>%s</b>' % labeltext)
        frame.set_label_widget(label)

        alignment = gtk.Alignment(xscale=xscale, yscale=yscale)
        alignment.set_padding(0, 0, 12, 0)
        alignment.add(view)
        view.set_border_width(6)

        frame.add(alignment)
        return frame

    def create_info_table(self, rows):
        table = gtk.Table(rows=len(rows), columns=2)
        table.set_row_spacings(6)
        table.set_col_spacings(12)

        i = 0
        for row in rows:
            label = gtk.Label('%s:' % row[0])
            alignment = gtk.Alignment()
            alignment.add(label)
            table.attach(alignment, 0, 1, i, i+1, gtk.FILL)

            data = gtk.Label(row[1])
            alignment = gtk.Alignment()
            alignment.add(data)
            table.attach(alignment, 1, 2, i, i+1, gtk.FILL)

            i += 1
        return table