#!/usr/bin/env python
#
# Copyright (C) 2005 British Broadcasting Corporation and Kamaelia Contributors(1)
# All Rights Reserved.
#
# You may only modify and redistribute this under the terms of any of the
# following licenses(2): Mozilla Public License, V1.1, GNU General
# Public License, V2.0, GNU Lesser General Public License, V2.1
#
# (1) Kamaelia Contributors are listed in the AUTHORS file and at
# http://kamaelia.sourceforge.net/AUTHORS - please extend this file,
# not this notice.
# (2) Reproduced in the COPYING file, and at:
# http://kamaelia.sourceforge.net/COPYING
# Under section 3.5 of the MPL, we are using this text since we deem the MPL
# notice inappropriate for this file. As per MPL/GPL/LGPL removal of this
# notice is prohibited.
#
# Please contact us via: kamaelia-list-owner@lists.sourceforge.net
# to discuss alternative licensing.
# -------------------------------------------------------------------------
"""\
=========================================
GUI for Ryans Lothians Bittorrent Package
=========================================
A 3D GUI for bittorrent downloading using Ryan Lothians Kamaelia
Bittorrent package. Features torrent fetching from URLs,
starting/stopping of torrents and showing torrent information.
To work properly, an installation of Bittorrent 4.20.8 or higher is
required.
"""
import Axon
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from Kamaelia.UI.OpenGL.Vector import Vector
from Kamaelia.UI.OpenGL.Button import Button
from Kamaelia.UI.OpenGL.ArrowButton import ArrowButton
from Kamaelia.UI.OpenGL.ProgressBar import ProgressBar
from Kamaelia.UI.OpenGL.Container import Container
from Kamaelia.UI.OpenGL.SkyGrassBackground import SkyGrassBackground
from Kamaelia.UI.OpenGL.Label import Label
from Kamaelia.UI.OpenGL.PygameWrapper import PygameWrapper
from Kamaelia.UI.OpenGL.Movement import WheelMover, PathMover, LinearPath
from Kamaelia.UI.Pygame.Ticker import Ticker
from Kamaelia.Protocol.Torrent.TorrentIPC import *
from BitTorrent.bencode import bdecode
import random
import os
import time
import sys
class TorrentOpenGLGUI(Axon.AdaptiveCommsComponent.AdaptiveCommsComponent):
Inboxes = {
"inbox":"Receive messages from TorrentPatron",
"control":"receive shutdown messages",
"nav":"To receive commands from navigation buttons",
"start": "Start commands from buttons",
"stop": "Stop commands from buttons",
"show_info": "Info commands from buttons",
"hide_info": "Hide torrent info",
"torrent_url": "For reception of torrent file URLs",
"torrent_file": "For reception of torrent files",
}
Outboxes = {
"outbox":"Send Messages to TorrentPatron",
"signal" : "",
"fetcher" : "Send URLs to SimpleHttpClient",
"mover_signal": "Send command to WheelMover",
"mover_switch": "Control WheelMover",
"infomover_commands": "Send commands to info container mover",
"torrent_info": "For sending info about a torrent"
}
def __init__(self, **argd):
super(TorrentOpenGLGUI, self).__init__()
self.torrents = []
self.torrent_from_id = {}
self.torrent_to_id = {}
self.torrent_progress_comms = {}
self.requested_files = []
self.started_torrents = []
def main(self):
self.initUIComponents()
self.loadLocalTorrentFiles()
while 1:
yield 1
while self.dataReady("inbox"):
msg = self.recv("inbox")
# print msg
if isinstance(msg, TIPCNewTorrentCreated):
torrent = self.started_torrents.pop(0)
self.torrent_from_id[msg.torrentid] = torrent
self.torrent_to_id[torrent] = msg.torrentid
elif isinstance(msg, TIPCTorrentStartFail) or isinstance(msg, TIPCTorrentAlreadyDownloading):
self.started_torrents.pop(0)
elif isinstance(msg, TIPCTorrentStatusUpdate):
self.send(msg.statsdictionary.get("fractionDone","0"), self.torrent_progress_comms[ self.torrent_from_id[msg.torrentid]] )
while self.dataReady("torrent_url"):
url = self.recv("torrent_url")
filename = os.path.basename(url)
self.requested_files.append(filename)
self.send(url, "fetcher")
while self.dataReady("torrent_file"):
torrentfile = self.recv("torrent_file")
# save received file
filename = self.requested_files.pop()
fobj = open(os.getcwd()+"/"+filename, 'w')
fobj.write(torrentfile)
fobj.close()
# add torrent
self.addTorrent(filename, torrentfile)
while self.dataReady("nav"):
msg = self.recv("nav")
if msg == "UP":
self.send("NEXT", "mover_switch")
if msg == "DOWN":
self.send("PREVIOUS", "mover_switch")
while self.dataReady("start"):
torrent = self.recv("start")
self.send(torrent)
self.started_torrents.append(torrent)
while self.dataReady("stop"):
torrent = self.recv("stop")
try:
self.send( TIPCCloseTorrent(torrentid=self.torrent_to_id[torrent]) )
self.send(0.0, self.torrent_progress_comms[torrent])
except KeyError:
pass
while self.dataReady("show_info"):
torrent = self.recv("show_info")
self.showInfo(torrent)
while self.dataReady("hide_info"):
msg = self.recv("hide_info")
self.hideInfo()
while self.dataReady("control"):
cmsg = self.recv("control")
if isinstance(cmsg, Axon.Ipc.shutdownMicroprocess):
# dirty way to terminate program
sys.exit(0)
def initUIComponents(self):
# listen to shutdown events
ogl_display = OpenGLDisplay().getDisplayService()[0]
self.link( (ogl_display, "signal"), (self, "control") )
# init mover
self.mover = WheelMover(radius=15, center=(0,0,-25), steps=500, slots=40).activate()
self.link( (self, "mover_signal"), (self.mover, "notify") )
self.link( (self, "mover_switch"), (self.mover, "switch") )
self.background = SkyGrassBackground(size=(5000,5000,0), position=(0,0,-90)).activate()
# create & link nav buttons
self.up_button = ArrowButton(size=(1,1,0.3), position=(7,5,-15), msg="UP").activate()
self.down_button = ArrowButton(size=(1,1,0.3), position=(7,-5,-15), rotation=(0,0,180), msg="DOWN").activate()
self.link( (self.up_button, "outbox"), (self, "nav") )
self.link( (self.down_button, "outbox"), (self, "nav") )
# init info display
self.infoticker = Ticker(text_height=21, render_right=250, render_bottom=500, background_colour=(250,250,200), text_colour=(0,0,0), outline_colour=(255,255,255)).activate()
self.tickerwrapper = PygameWrapper(wrap=self.infoticker, size=(2.4,4.0,0.3)).activate()
self.hideinfo_button = Button(caption="Hide", fontsize=30).activate()
infocontents = {
self.tickerwrapper : { "position":(0,0,0) },
self.hideinfo_button : { "position":(0,-2.4,0) },
}
self.infocontainer = Container(contents=infocontents, position=(-10, 10, -16)).activate()
infopath = LinearPath([(-10, 10, -16), (-3,0,-8)], 100)
self.infomover = PathMover(infopath, False).activate()
self.link( (self.infomover, "outbox"), (self.infocontainer, "position") )
self.link( (self, "infomover_commands"), (self.infomover, "inbox") )
self.link( (self, "torrent_info"), (self.infoticker, "inbox") )
self.link( (self.hideinfo_button, "outbox"), (self, "hide_info") )
self.send("Stop", "infomover_commands")
def loadLocalTorrentFiles(self):
print "Loading local torrent files..."
cwd = os.getcwd()
files = os.listdir(cwd)
for f in files:
if f.endswith(".torrent"):
print "- ",f
fobj = open(f)
torrent = fobj.read()
fobj.close()
self.addTorrent(f, torrent)
def addTorrent(self, title, torrent):
self.torrents.append(torrent)
start = Button(size=(0.8,0.5,0.3), caption="Start", fontsize=35, msg=torrent).activate()
info = Button(size=(0.8,0.5,0.3), caption="Info", fontsize=35, msg=torrent).activate()
stop = Button(size=(0.8,0.5,0.3), caption="Stop", fontsize=35, msg=torrent).activate()
colour = [ int(random.randint(100,255)) for i in range(3) ]
progress = ProgressBar(size=(3.2,0.5,0.3), barcolour=colour).activate()
label = Label( size=(6.0, 0.3, 0.3), caption=title, fontsize=26, bgcolour=colour).activate()
self.link( (start, "outbox"), (self, "start") )
self.link( (stop, "outbox"), (self, "stop") )
self.link( (info, "outbox"), (self, "show_info") )
container_elements = {
progress : { "position":(-0.4,-0.3,0) },
start : { "position":(1.8,-0.3,0) },
stop : { "position":(2.7,-0.3,0) },
info : { "position":(3.6,-0.3,0) },
label : { "position":(1, 0.3, 0) },
}
container = Container(contents=container_elements, position=(0,0,-10)).activate()
req = { "APPEND_CONTROL":True,
"objectid": id(container),
"control": (container,"position")
}
self.send(req, "mover_signal")
# create & link box for progress update
progbox = self.addOutbox("progress")
self.torrent_progress_comms[torrent] = progbox
self.link( (self, progbox), (progress, "progress") )
def showInfo(self, torrent):
decoded = bdecode(torrent)
info = decoded.get("info", None)
if info is not None:
name = info.get("name", "??")
length = info.get("length", "??")
date = decoded.get("creation date", "??")
creator = decoded.get("created by", "??")
announce = decoded.get("announce", "??")
comment = decoded.get("comment", "??")
infotuple = (name, comment, length, date, creator)
infostring = "Name:%s\nComment:%s\nLength:%s\nCreation_Date:%s\nCreated_by:%s\n ----- \n" % infotuple
# move info container in front of everthig
self.send("Forward", "infomover_commands")
self.send("Play", "infomover_commands")
self.send(infostring, "torrent_info")
def hideInfo(self):
self.send("Backward", "infomover_commands")
self.send("Play", "infomover_commands")
if __name__ == "__main__":
from Kamaelia.Chassis.Graphline import Graphline
from Kamaelia.Util.Console import ConsoleReader
from Kamaelia.UI.PygameDisplay import PygameDisplay
from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay
from Kamaelia.Protocol.HTTP.HTTPClient import SimpleHTTPClient
from Kamaelia.Protocol.Torrent.TorrentPatron import TorrentPatron
ogl_display = OpenGLDisplay(limit_fps=100).activate()
OpenGLDisplay.setDisplayService(ogl_display)
# override pygame display service
PygameDisplay.setDisplayService(ogl_display)
Graphline(
reader = ConsoleReader(prompt="Enter torrent location:", eol=""),
httpclient = SimpleHTTPClient(),
gui = TorrentOpenGLGUI(),
backend = TorrentPatron(),
linkages = {
("gui", "outbox") : ("backend", "inbox"),
("reader", "outbox") : ("gui", "torrent_url"),
("gui", "fetcher") : ("httpclient", "inbox"),
("httpclient", "outbox") : ("gui", "torrent_file"),
("backend", "outbox"): ("gui", "inbox")
}
).run()
# Licensed to the BBC under a Contributor Agreement: THF