[Script] Cron to delete Torrents and Files over certain age

Suggest, post, or discuss plugins for Deluge
guardmedia
New User
New User
Posts: 7
Joined: Thu Jul 07, 2011 3:01 pm

[Script] Cron to delete Torrents and Files over certain age

Post by guardmedia »

UPDATE: Final working code!

This is a script that can be run from the command line by executing "python script.py" where the file is saved as 'script.py'. It parses the deluge daemon for torrents, find which ones are over a certain age, removes the torrent, and optionally deletes the files. This can be a useful addition for FlexGet on a seedbox, where the seedbox will always have new content and will automatically purge the old after a set amount of time. Please be sure to set the following variables:

cliconnect = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 5 # Remove torrents older than this this time (in days)
is_interactive = True # Set this to True to allow direct output or set to False for cron
do_remove_data = True # Set to True to delete torrent data as well, false to leave it

The code is below, simply edit the variables and paste it into an file. chmod +x, and run it with python.

Code: Select all

#!/usr/bin/python

from deluge.log import LOG as log
from deluge.ui.client import client
import deluge.component as component
from twisted.internet import reactor, defer
import time

############
# Change the following
cliconnect = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 5 # Remove torrents older than this this time (in days)
is_interactive = True # Set this to True to allow direct output or set to False for cron
do_remove_data = True # Set to True to delete torrent data as well, false to leave it

###############
# Do not edit below this line!

oldcount = 0
skipcount = 0
seedcount = 0
errorcount = 0
torrent_ids = []

def printSuccess(dresult, is_success, smsg):
    global is_interactive
    if is_interactive:
        if is_success:
            print "[+]", smsg
        else:
            print "[i]", smsg

def printError(emsg):
    global is_interactive
    if is_interactive:
        print "[e]", emsg

def endSession(esresult):
    if esresult:
        print esresult
        reactor.stop()
    else:
        client.disconnect()
        printSuccess(None, False, "Client disconnected.")
        reactor.stop()

def printReport(rresult):
    if errorcount > 0:
        printError(None, "Failed! Number of errors: %i" % (errorcount))
    else:
        if oldcount > 0:
            printSuccess(None, True, "Removed %i torrents -- Skipped %i torrents -- Seeding %i torrents" % (oldcount, skipcount, seedcount))
        else:
            printSuccess(None, True, "No old torrents! -- Skipped %i torrents -- Seeding %i torrents" % (skipcount, seedcount))
    endSession(None)

def on_torrents_status(torrents):
    global filtertime
    tlist=[]
    for torrent_id, status in torrents.items():
        if status["save_path"] == seeddir:
            global seedcount
            seedcount += 1
        else:
            unixtime = "%s" % (status["time_added"])
            numunixtime = int(unixtime[:-2])
            humantime = time.ctime(numunixtime)
            if numunixtime < filtertime:
                global do_remove_data
                global oldcount
                oldcount += 1
                successmsg = " Removed %s:  %s from %s" % (humantime, status["name"], status["save_path"])
                errormsg = "Error removing %s" % (status["name"])
                tlist.append(client.core.remove_torrent(torrent_id, do_remove_data).addCallbacks(printSuccess, printError, callbackArgs = (True, successmsg), errbackArgs = (errormsg)))
            else:
                global skipcount
                skipcount += 1
                printSuccess(None, False, " Skipping %s: %s from %s" % (humantime, status["name"], status["save_path"]))
    defer.DeferredList(tlist).addCallback(printReport)

def on_session_state(result):
    client.core.get_torrents_status({"id": result}, ["name","time_added","save_path",]).addCallback(on_torrents_status)

def on_connect_success(result):
    printSuccess(None, True, "Connection was successful!")
    global timedifference
    global filtertime
    curtime = time.time()
    filtertime = curtime - (timedifference * 24 * 60 * 60)
    printSuccess(None, False, "Current unix time is %i" % (curtime))
    printSuccess(None, False, "Filtering torrents older than %s" % (time.ctime(int(filtertime))))
    client.core.get_session_state().addCallback(on_session_state)

cliconnect.addCallbacks(on_connect_success, endSession, errbackArgs=("Connection failed: check settings and try again."))

reactor.run()


Original posts and development below this line
----------------------------------------------

Hello,

I am trying to write a python script to parse torrents over 1 week old, that are not in a specified directory, and to remove the torrent and delete the files. But, I can't get the torrent and files to be removed although it appears to complete the script successfully! Any ideas?

I am using client.core.remove_torrent(torrent_id, remove_data = True) to remove the files, where torrent_id is a valid id.

Code: Select all

#!/usr/bin/python

from deluge.log import LOG as log
from deluge.ui.client import client
import deluge.component as component
from twisted.internet import reactor
import time

############
# Change the following
d = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 604800 # Remove torrents older than this this time (in seconds)


###############
# Do not edit below this line!

oldcount = 0
skipcount = 0
seedcount = 0
errorcount = 0

def on_connect_success(result):
    print "[+] Connection was successful!"
    global timedifference
    curtime = time.time()
    print "[i] Current unix time is %i" % (curtime)
    filtertime = curtime - timedifference
    print "[i] Filtering torrents older than %s" % (time.ctime(int(filtertime)))
    torrents = []
    def on_session_state(result):
        def on_torrents_status(torrents):
            for torrent_id, status in torrents.items():
                if status["save_path"] == seeddir:
                    global seedcount
                    seedcount += 1
                else:
                    unixtime = "%s" % (status["time_added"])
                    numunixtime = int(unixtime[:-2])
                    humantime = time.ctime(numunixtime)
                    if numunixtime < filtertime:
                        global oldcount
                        oldcount += 1
                        if client.core.remove_torrent(torrent_id, remove_data = True):
                            print "[+] Removed %s: %s from %s" % (humantime, status["name"], status["save_path"])
                        else:
                            global errorcount
                            errorcount += 1
                            print "[e] Error removing %s" % (status["name"])
                    else:
                        global skipcount
                        skipcount += 1
            if oldcount > 0:
                print "[+] Removed %i torrents -- Skipped %i torrents -- Seeding %i torrents" % (oldcount, skipcount, seedcount)
            else:
                print "[i] No old torrents! -- Skipped %i torrents -- Seeding %i torrents" % (skipcount, seedcount)
            if errorcount > 0:
                print "[e] Failed! Number of errors: %i" % (errorcount)
            else:
                print "[+] Success!"
            client.disconnect()
            reactor.stop()
        client.core.get_torrents_status({"id": result}, ["name","time_added","save_path",]).addCallback(on_torrents_status)
    client.core.get_session_state().addCallback(on_session_state)

d.addCallback(on_connect_success)

def on_connect_fail(result):
    print "[e] Connection failed!"
    print "[i] result:", result

d.addErrback(on_connect_fail)

reactor.run()
Last edited by guardmedia on Fri Jul 22, 2011 6:26 am, edited 2 times in total.
CSB
Leecher
Leecher
Posts: 66
Joined: Fri Dec 03, 2010 1:55 am

Re: Cron to delete Torrents and Files over certain age

Post by CSB »

So this print is executed:
print "[+] Removed %s: %s from %s" % (humantime, status["name"], status["save_path"])
But the torrents aren't removed?
guardmedia
New User
New User
Posts: 7
Joined: Thu Jul 07, 2011 3:01 pm

Re: Cron to delete Torrents and Files over certain age

Post by guardmedia »

Exactly. And, when running the daemon with debug output, it doesn't even show any of the torrentmanager.remove debug output. As if the command never even runs.
CSB
Leecher
Leecher
Posts: 66
Joined: Fri Dec 03, 2010 1:55 am

Re: Cron to delete Torrents and Files over certain age

Post by CSB »

guardmedia wrote:Exactly. And, when running the daemon with debug output, it doesn't even show any of the torrentmanager.remove debug output. As if the command never even runs.
Not sure what version you're using, but in 1.3.2 at least, core.remove_torrent doesn't have remove_data as an optional argument, so you can call it by simply client.core.remove_torrent(torrent_id, True). That typically doesn't matter, and I don't think it should matter here.

That being said, client.core.remove_torrent returns a Deferred, since it's an asynchronous function, so instead of testing the Deferred, try adding a callback.
guardmedia
New User
New User
Posts: 7
Joined: Thu Jul 07, 2011 3:01 pm

Re: Cron to delete Torrents and Files over certain age

Post by guardmedia »

Ah, thanks for the tip! I am new to both python and twisted... Seems that I managed to get it working, but with one additional quirk. The lines client.disconnect() and reactor.stop() are executed before remove_torrent ever has a chance to execute! The revised script below works, but needs to be ended manually (a la Ctrl-C). Perhaps I could set this on a timer to close, but that seems messy.

Code: Select all

#!/usr/bin/python

from deluge.log import LOG as log
from deluge.ui.client import client
import deluge.component as component
from twisted.internet import reactor, defer
import time

############
# Change the following
d = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 604800 # Remove torrents older than this this time (in seconds)


###############
# Do not edit below this line!

oldcount = 0
skipcount = 0
seedcount = 0
errorcount = 0

def printData(dresult):
    if dresult:
        print "Command executed with result: ", dresult
    else:
       import sys
       sys.stderr.write(str(failure))

def on_connect_success(result):
    print "[+] Connection was successful!"
    global timedifference
    curtime = time.time()
    print "[i] Current unix time is %i" % (curtime)
    filtertime = curtime - timedifference
    print "[i] Filtering torrents older than %s" % (time.ctime(int(filtertime)))
    torrents = []
    def on_session_state(result):
        def on_torrents_status(torrents):
            for torrent_id, status in torrents.items():
                if status["save_path"] == seeddir:
                    global seedcount
                    seedcount += 1
                else:
                    unixtime = "%s" % (status["time_added"])
                    numunixtime = int(unixtime[:-2])
                    humantime = time.ctime(numunixtime)
                    if numunixtime < filtertime:
                        global oldcount
                        oldcount += 1
                        if client.core.remove_torrent(torrent_id, remove_data = True).addCallback(printData):
                            print "[+] Removed %s: %s from %s" % (humantime, status["name"], status["save_path"])
                        else:
                            global errorcount
                            errorcount += 1
                            print "[e] Error removing %s" % (status["name"])
                    else:
                        global skipcount
                        skipcount += 1
            if oldcount > 0:
                print "[+] Removed %i torrents -- Skipped %i torrents -- Seeding %i torrents" % (oldcount, skipcount, seedcount)
            else:
                print "[i] No old torrents! -- Skipped %i torrents -- Seeding %i torrents" % (skipcount, seedcount)
            if errorcount > 0:
                print "[e] Failed! Number of errors: %i" % (errorcount)
            else:
                print "[+] Success!"
#            client.disconnect()
#            reactor.stop()
        client.core.get_torrents_status({"id": result}, ["name","time_added","save_path",]).addCallback(on_torrents_status)
    client.core.get_session_state().addCallback(on_session_state)

d.addCallback(on_connect_success)

def on_connect_fail(result):
    print "[e] Connection failed!"
    print "[i] result:", result

d.addErrback(on_connect_fail)

reactor.run()
Cas
Top Bloke
Top Bloke
Posts: 3679
Joined: Mon Dec 07, 2009 6:04 am
Location: Scotland

Re: Cron to delete Torrents and Files over certain age

Post by Cas »

You cannot have an if statement for a callback as it will always be true (unless there is a problem with the method)

I think this is the way you should do it (not tested):

Code: Select all

                    if numunixtime < filtertime:
                        global oldcount
                        oldcount += 1
                        def printData(dresult):
                            if dresult:
                                print "Command executed with result: ", dresult
                                print "[+] Removed %s: %s from %s" % (humantime, status["name"], status["save_path"])
                            else:
                               import sys
                               sys.stderr.write(str(failure))
                               global errorcount
                                errorcount += 1
                                print "[e] Error removing %s" % (status["name"])
                        client.core.remove_torrent(torrent_id, remove_data = True).addCallback(printData)
                    else:
                        global skipcount
                        skipcount += 1
guardmedia
New User
New User
Posts: 7
Joined: Thu Jul 07, 2011 3:01 pm

Re: Cron to delete Torrents and Files over certain age

Post by guardmedia »

Thanks, that makes sense. Here is my reformatted code based on what you posted:

Code: Select all

#!/usr/bin/python

from deluge.log import LOG as log
from deluge.ui.client import client
import deluge.component as component
from twisted.internet import reactor, defer
import time

############
# Change the following
d = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 432000 # Remove torrents older than this this time (in seconds)


###############
# Do not edit below this line!

oldcount = 0
skipcount = 0
seedcount = 0
errorcount = 0

def on_connect_success(result):
    print "[+] Connection was successful!"
    global timedifference
    curtime = time.time()
    print "[i] Current unix time is %i" % (curtime)
    filtertime = curtime - timedifference
    print "[i] Filtering torrents older than %s" % (time.ctime(int(filtertime)))
    torrents = []
    def on_session_state(result):
        def on_torrents_status(torrents):
            for torrent_id, status in torrents.items():
                if status["save_path"] == seeddir:
                    global seedcount
                    seedcount += 1
                else:
                    unixtime = "%s" % (status["time_added"])
                    numunixtime = int(unixtime[:-2])
                    humantime = time.ctime(numunixtime)
                    if numunixtime < filtertime:
                        global oldcount
                        oldcount += 1
                        def printData(dresult):
                            print " Command executed with result: ", dresult
                            if dresult:
                                print "[+] Removed %s: %s from %s" % (humantime, status["name"], status["save_path"])
                            else:
                                import sys
                                sys.stderr.write(str(failure))
                                global errorcount
                                errorcount += 1
                                print "[e] Error removing %s" % (status["name"])
                        client.core.remove_torrent(torrent_id, remove_data = True).addCallback(printData)
                    else:
                        global skipcount
                        skipcount += 1
            if oldcount > 0:
                print "[+] Removed %i torrents -- Skipped %i torrents -- Seeding %i torrents" % (oldcount, skipcount, seedcount)
            else:
                print "[i] No old torrents! -- Skipped %i torrents -- Seeding %i torrents" % (skipcount, seedcount)
            if errorcount > 0:
                print "[e] Failed! Number of errors: %i" % (errorcount)
            else:
                print "[+] Success!"
            client.disconnect()
            reactor.stop()
        client.core.get_torrents_status({"id": result}, ["name","time_added","save_path",]).addCallback(on_torrents_status)
    client.core.get_session_state().addCallback(on_session_state)

d.addCallback(on_connect_success)

def on_connect_fail(result):
    print "[e] Connection failed!"
    print "[i] result:", result

d.addErrback(on_connect_fail)

reactor.run()
However the client.disconnect() and reactor.stop() commands still need to be commented out, or they will execute before remove_torrent runs, and the torrents are still present in file and in deluge. Here is the output without those two lines commented:

Code: Select all

[+] Connection was successful!
[i] Current unix time is 1311275387
[i] Filtering torrents older than Sat Jul 16 15:09:47 2011
[+] Removed 10 torrents -- Skipped 24 torrents -- Seeding 51 torrents
[+] Success!

By the way, thanks for all the help -- hopefully this will be useful to others, and perhaps I can rewrite this into a plugin.
Cas
Top Bloke
Top Bloke
Posts: 3679
Joined: Mon Dec 07, 2009 6:04 am
Location: Scotland

Re: Cron to delete Torrents and Files over certain age

Post by Cas »

The problem is that client.disconnect() and reactor.stop() are the next steps in your code. What you need is to wait for all the callbacks (remember they are asynchronous) to return then print the summary and call disconnect and stop.
CSB
Leecher
Leecher
Posts: 66
Joined: Fri Dec 03, 2010 1:55 am

Re: Cron to delete Torrents and Files over certain age

Post by CSB »

Code: Select all

#!/usr/bin/python

from deluge.log import LOG as log
from deluge.ui.client import client
import deluge.component as component
from twisted.internet import reactor, defer
import time

############
# Change the following
d = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 432000 # Remove torrents older than this this time (in seconds)


###############
# Do not edit below this line!

oldcount = 0
skipcount = 0
seedcount = 0
errorcount = 0

def on_connect_success(result):
    print "[+] Connection was successful!"
    global timedifference
    curtime = time.time()
    print "[i] Current unix time is %i" % (curtime)
    filtertime = curtime - timedifference
    print "[i] Filtering torrents older than %s" % (time.ctime(int(filtertime)))
    torrents = []
    def on_session_state(result):
        def on_torrents_status(torrents):
        	dlist = []
            for torrent_id, status in torrents.items():
                if status["save_path"] == seeddir:
                    global seedcount
                    seedcount += 1
                else:
                    unixtime = "%s" % (status["time_added"])
                    numunixtime = int(unixtime[:-2])
                    humantime = time.ctime(numunixtime)
                    if numunixtime < filtertime:
                        global oldcount
                        oldcount += 1
                        def printData(dresult):
                            print " Command executed with result: ", dresult
                            if dresult:
                                print "[+] Removed %s: %s from %s" % (humantime, status["name"], status["save_path"])
                            else:
                                import sys
                                sys.stderr.write(str(failure))
                                global errorcount
                                errorcount += 1
                                print "[e] Error removing %s" % (status["name"])
                        dlist.append(client.core.remove_torrent(torrent_id, remove_data = True).addCallback(printData))
                    else:
                        global skipcount
                        skipcount += 1

            def printReport():
				if oldcount > 0:
					print "[+] Removed %i torrents -- Skipped %i torrents -- Seeding %i torrents" % (oldcount, skipcount, seedcount)
				else:
					print "[i] No old torrents! -- Skipped %i torrents -- Seeding %i torrents" % (skipcount, seedcount)
				if errorcount > 0:
					print "[e] Failed! Number of errors: %i" % (errorcount)
				else:
					print "[+] Success!"
	            client.disconnect()
	            reactor.stop()

			defer.DeferredList(dlist).addCallback(printReport)
	        
        client.core.get_torrents_status({"id": result}, ["name","time_added","save_path",]).addCallback(on_torrents_status)
    client.core.get_session_state().addCallback(on_session_state)

d.addCallback(on_connect_success)

def on_connect_fail(result):
    print "[e] Connection failed!"
    print "[i] result:", result

d.addErrback(on_connect_fail)

reactor.run()
Try that.

The callback on the DeferredList will be called when all of the calls have completed.
Let me know if you have any questions.
guardmedia
New User
New User
Posts: 7
Joined: Thu Jul 07, 2011 3:01 pm

Re: Cron to delete Torrents and Files over certain age

Post by guardmedia »

Thanks! That sparked a though, and prompted a re-write. Here is the new code, working wonderfully! Cleaned it up a bit too and added a few more options. Will update the original post for other users to reference.

Code: Select all

#!/usr/bin/python

from deluge.log import LOG as log
from deluge.ui.client import client
import deluge.component as component
from twisted.internet import reactor, defer
import time

############
# Change the following
cliconnect = client.connect(host='127.0.0.1',port=xxxx,username="xxxx",password="xxxx")
seeddir = "xxxx" # Directory to ignore for torrents to remain seeding
timedifference = 5 # Remove torrents older than this this time (in days)
is_interactive = True # Set this to True to allow direct output or set to False for cron
do_remove_data = True # Set to True to delete torrent data as well, false to leave it

###############
# Do not edit below this line!

oldcount = 0
skipcount = 0
seedcount = 0
errorcount = 0
torrent_ids = []

def printSuccess(dresult, is_success, smsg):
    global is_interactive
    if is_interactive:
        if is_success:
            print "[+]", smsg
        else:
            print "[i]", smsg

def printError(emsg):
    global is_interactive
    if is_interactive:
        print "[e]", emsg

def endSession(esresult):
    if esresult:
        print esresult
        reactor.stop()
    else:
        client.disconnect()
        printSuccess(None, False, "Client disconnected.")
        reactor.stop()

def printReport(rresult):
    if errorcount > 0:
        printError(None, "Failed! Number of errors: %i" % (errorcount))
    else:
        if oldcount > 0:
            printSuccess(None, True, "Removed %i torrents -- Skipped %i torrents -- Seeding %i torrents" % (oldcount, skipcount, seedcount))
        else:
            printSuccess(None, True, "No old torrents! -- Skipped %i torrents -- Seeding %i torrents" % (skipcount, seedcount))
    endSession(None)

def on_torrents_status(torrents):
    global filtertime
    tlist=[]
    for torrent_id, status in torrents.items():
        if status["save_path"] == seeddir:
            global seedcount
            seedcount += 1
        else:
            unixtime = "%s" % (status["time_added"])
            numunixtime = int(unixtime[:-2])
            humantime = time.ctime(numunixtime)
            if numunixtime < filtertime:
                global do_remove_data
                global oldcount
                oldcount += 1
                successmsg = " Removed %s:  %s from %s" % (humantime, status["name"], status["save_path"])
                errormsg = "Error removing %s" % (status["name"])
                tlist.append(client.core.remove_torrent(torrent_id, do_remove_data).addCallbacks(printSuccess, printError, callbackArgs = (True, successmsg), errbackArgs = (errormsg)))
            else:
                global skipcount
                skipcount += 1
                printSuccess(None, False, " Skipping %s: %s from %s" % (humantime, status["name"], status["save_path"]))
    defer.DeferredList(tlist).addCallback(printReport)

def on_session_state(result):
    client.core.get_torrents_status({"id": result}, ["name","time_added","save_path",]).addCallback(on_torrents_status)

def on_connect_success(result):
    printSuccess(None, True, "Connection was successful!")
    global timedifference
    global filtertime
    curtime = time.time()
    filtertime = curtime - (timedifference * 24 * 60 * 60)
    printSuccess(None, False, "Current unix time is %i" % (curtime))
    printSuccess(None, False, "Filtering torrents older than %s" % (time.ctime(int(filtertime))))
    client.core.get_session_state().addCallback(on_session_state)

cliconnect.addCallbacks(on_connect_success, endSession, errbackArgs=("Connection failed: check settings and try again."))

reactor.run()
Thanks again to both of you!
Post Reply