# Syncs files to client workspace via deluge # Copyright (C) 2015 Forrest S # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # torrents should be stored under P4TORRENT, and should be created automatically # with the trigger provided #!/usr/bin/python import os import errno import base64 import time from deluge.ui.client import client from twisted.internet import reactor, defer, task from deluge.log import setupLogger setupLogger() import sys # from twisted.python import log # log.startLogging(sys.stdout) try: import P4 as p4 except ImportError, e: import p4 DELUGE_USER="user" DELUGE_PASSWORD="password" def run(serverport,torrent_storage_root, files, changelist=None, revision=None, run_sync=True): torrent_files, errs = get_torrent_files(serverport,torrent_storage_root, files, changelist, revision) if errs: print '\n'.join(errs) return if torrent_files: add_torrents(torrent_files, run_sync) return def get_torrent_files(serverport,torrent_storage_root, relative_files, changelist=None, revision=None): errs = [] try: p4c = connect_to_p4(serverport,ntries = 120,delay = 10) except p4.P4Exception, ex: msg = "Failed to connect to P4 Server:", str(ex) errs += [msg] return None, errs cwd = os.getcwd() files = [] for f in relative_files: files+= [os.path.normpath(os.path.join(cwd, f))] # grab destination for filename from p4server where = p4c.run_where(files) depot_paths = [(f['depotFile'], f['path']) for f in where] torrent_files = [] # depot path: //depot/path/to/file # path: /homes/user/workspace/path/to/file for depot_path, path in depot_paths: if depot_path.startswith("//"): depot_path = depot_path[2:] # torrent storage should be in nfs root torrent_storage_path = os.path.join(torrent_storage_root,depot_path) head, tail = os.path.split(torrent_storage_path) # reconstruct path to torrent torrent_storage_path = os.path.join(head,tail,tail+".torrent") if changelist: torrent_storage_path+="@"+str(changelist) if revision: torrent_storage_path+="#"+str(revision) # find most recent revision if not changelist and not revision: head, _ =os.path.split(torrent_storage_path) try: dirlist = os.listdir(head) except OSError, e: msg = "" if e.errno == errno.EEXIST: msg = "Could not find torrent for file located at :", path else: msg = "unknown error: "+str(e) errs+= [msg] continue dirlist.sort() torrent_storage_path = os.path.join(head,dirlist[-1]) # deluge requires tuples (path, b64encoded torrent file, options) try: with open(torrent_storage_path) as f: data = base64.b64encode(f.read()) torrent_files+= [(torrent_storage_path, data, {"download_location": os.path.dirname(path),"auto_managed":True}, os.path.basename(path)) ] except IOError, e: msg = "" if e.errno == errno.EEXIST: if changelist: msg = "Could not find torrent for changelist number", str(changelist) if revision: msg = "Could not find torrent for revision number ", str(revision) else: msg = "unknown error: " + str(e) errs+=[msg] continue return torrent_files, errs def connect_to_p4(serverport,ntries,delay): p4c = p4.P4() p4c.port = serverport p4c.cwd = os.getcwd() ex = None while ntries >= 0: try: p4c.connect() return p4c except p4.P4Exception, ex: ntries -=1 time.sleep(delay) else: raise ex def add_torrents(torrent_info, run_sync=True): def on_connect_success(result): torrent_ids = [] torrent_list = [f[0] for f in torrent_info] print "Adding the following torrent files: \n", '\n'.join(torrent_list) for torrent in torrent_info: torrent_id = client.core.add_torrent_file(torrent[0], torrent[1], torrent[2]) torrent_ids.append(torrent_id) deferred_list = defer.DeferredList(torrent_ids) return deferred_list def remove_finished(statuses, torrent_list): for state in statuses: if state[0]: if "Downloading" in state[1]["state"]: torrent_list.remove(state[1]["hash"]) print "Added", state[1]['name'] return torrent_list def query_status(torrent_list): statuses = [] for torrent in torrent_list: if torrent: status = client.core.get_torrent_status(torrent, ["name", "state", "hash"]) statuses.append(status) deferred_list = defer.DeferredList(statuses) deferred_list.addCallback(remove_finished, torrent_list) return deferred_list def shutdown(s): client.disconnect() reactor.stop() def on_connect_fail(result): print "Failed to connect to Deluge" print result reactor.stop() def looping_call(torrent_list): if not torrent_list: shutdown(None) defer = query_status(torrent_list) return defer # loops until torrent completes def wait_until_finished(torrent_ids): torrent_list = [f[1] for f in torrent_ids] for torrent in torrent_list: if not torrent: torrent_list.remove(torrent) loop = task.LoopingCall(looping_call, torrent_list) loop.start(2) # connect to deluge deferred_object = client.connect(username=DELUGE_USER,password=DELUGE_PASSWORD) deferred_object.addCallback(on_connect_success) deferred_object.addErrback(on_connect_fail) if run_sync: deferred_object.addCallback(wait_until_finished) else: deferred_object.addCallback(shutdown) reactor.run() if __name__ == '__main__': import argparse parser = argparse.ArgumentParser(description="p4torrent command") parser.add_argument('files',metavar="F", nargs='+', help="files to torrent") parser.add_argument('--port', "-p", help="Overrides any P4PORT setting with the specified protocol:host:port.") parser.add_argument('--torrent_location', "-tl", help="path to torrent dir") parser.add_argument('--user','-u',help="Deluge username") parser.add_argument('--password', '-P', help="Deluge password") parser.add_argument('--sync',action="store_true", dest="sync", default=True, help="Adds files synchronously [DEFAULT]") parser.add_argument('--async', action="store_false", dest="sync", help="Adds files asynchronously") group = parser.add_mutually_exclusive_group() group.add_argument('--changelist','-c', type=int, help='Changelist number') group.add_argument('--revision','-r', type=int, help='Revision Number') args = parser.parse_args() if args.user: DELUGE_USER=args.user if args.password: DELUGE_USER=args.password port = os.getenv('P4PORT') if args.port: port=args.port if not port: port="perforce:1666" torrent_location = os.getenv('P4TORRENT') if args.torrent_location: torrent_location = args.torrent_location run(serverport=port, torrent_storage_root=torrent_location,files= args.files, changelist=args.changelist,revision=args.revision, run_sync=args.sync)