2

I need to write a script that determines whether or not uTorrent is currently downloading something. I'd rather it just be downloading, but if I cannot differentiate between downloading and uploading then it would be better than nothing.

One possible way would be check the whether any files ending in .!ut are locked - but I'm hoping for something a little more elegant.

My weapon of choice is VBScript but I'm happy to use the command line if needs be.

1

2 Answers 2

2

Here is a bit of VBScript which will connect to a uTorrent webserver and determine if anything is downloading. It'll present a pop-up window with an appropriate message.

Some points to note:

  • You need to enable "Web UI" within the uTorrent preferences and set a username and password.
  • You need to then modify utServer, utUSERNAME and utPASSWORD in this code to the correct location and credentials to log in. The example value in utSERVER in the code connects to the local machine (127.0.0.1) on port 8080.
  • uTorrent requires an initial call to get a valid token for all subsequent requests. This is stored in utToken. You don't need to worry about it, just call Request_uTorrent with the correct URL and it'll work out whether or not a token request/refresh is required.
  • An "active download" is considered to be one that has an ETA of greater than 0 seconds and isn't seeding. If the ETA is infinite then it's considered not downloading.
  • Parsing JSON in VBScript is possible but isn't easy (source). This code uses some string manipulation to get the results as a CSV and then parses it that way.
  • This code isn't very good at handling errors caused when uTorrent isn't running or not accepting Web UI connections. The current solution is to use the brutal On Error Resume Next but this should really be cleaned up.
  • The code can get stuck in a loop if it doesn't have a token, requests one, gets one, makes the first request again and (for some reason) the token is invalid. I'm not sure how possible it is, but since anything could easily (and accidentally) modify utToken it could happen. You may wish to modify this so that if the token fails a second time then it bails.
  • Token extracting is an all or nothing affair. If it fails then the code exits. This isn't astoundingly helpful when debugging.

Save the following as check_downloading.vbs and double-click to run:

Option Explicit

' Configuration settings
' utSERVER = The IP address and port of the uTorrent server
' utUSERNAME = The username
' utPASSWORD = The password

Const utSERVER = "127.0.0.1:8080"
Const utUSERNAME = "yourusername"
Const utPASSWORD = "yourpassword"
Dim utToken ' Required for the uTorrent token

' == Code starts here ==

If Is_Downloading = True Then
    Msgbox "Something is downloading"
Else
    Msgbox "Nothing is downloading"
End If
WScript.Quit

' Is_Downloading
' Connects to uTorrent and checks to see if anything is currently downloading.
' Returns True if there is. Note: A file with an infinite ETA is considered not
' downloading.

Function Is_Downloading
    Dim sContent, sItem, sLines, token

    ' Get a list of the torrents from uTorrent
    sContent = Request_uTorrent("list=1")

    ' Parsing JSON isn't the easiest in VBScript, so we make some
    ' simple changes to the output and it looks like comma seperated
    ' values.

    For Each sItem In Split(sContent, VbLf)
        If Left(sItem, 2) = "[""" Then
            ' Remove spaces and the first ["
            sItem = Trim(sItem) : sItem = Right(sItem, Len(sItem)-1)
            ' Remove the ends of lines finishing with either ]], or ],
            If Right(sItem, 3) = "]]," Then sItem = Left(sItem, Len(sItem)-3)
            If Right(sItem, 2) = "]," Then sItem = Left(sItem, Len(sItem)-2)

            ' Extract the values from the line
            token = Process_CSV_Line(sItem)

            ' Format of the token array is:
            '   0 = HASH (string),
            '   1 = STATUS* (integer),
            '   2 = NAME (string),
            '   3 = SIZE (integer in bytes),
            '   4 = PERCENT PROGRESS (integer in per mils),
            '   5 = DOWNLOADED (integer in bytes),
            '   6 = UPLOADED (integer in bytes),
            '   7 = RATIO (integer in per mils),
            '   8 = UPLOAD SPEED (integer in bytes per second),
            '   9 = DOWNLOAD SPEED (integer in bytes per second),
            '   10 = ETA (integer in seconds),
            '   11 = LABEL (string),
            '   12 = PEERS CONNECTED (integer),
            '   13 = PEERS IN SWARM (integer),
            '   14 = SEEDS CONNECTED (integer),
            '   15 = SEEDS IN SWARM (integer),
            '   16 = AVAILABILITY (integer in 1/65535ths),
            '   17 = TORRENT QUEUE ORDER (integer),
            '   18 = REMAINING (integer in bytes)

            ' The ETA (token 10) can have three values:
            '   -1 = The download has stalled (reported as "infinite" in the UI)
            '    0 = The download has completed.
            '   >0 = The number of seconds left.
            '
            ' However, the ETA also includes seeding so we need to also ensure that the percentage
            ' complete is less than 1000 (100%).

            If IsNumeric(token(10)) And CLng(token(10)) > 0 And IsNumeric(token(4)) And CLng(token(4)) < 1000 Then
                Is_Downloading = True
                Exit Function
            End If  
        End If
    Next
    Is_Downloading = False
End Function

' Process_CSV_Line
' Given a string, split it up into an array using the comma as the delimiter. Take into account
' that a comma inside a quote should be ignored.

Function Process_CSV_Line(sString)    
    Redim csv(0)
    Dim iIndex : iIndex = 0
    Dim bInQuote, i : bInQuote = False

    For i = 1 To Len(sString)
        Dim sChar : sChar = Mid(sString, i, 1)
        If sChar = """" Then
            bInQuote = Not bInQuote
            sChar = ""
        End If
        If sChar = "," And Not bInQuote Then
            iIndex = iIndex + 1
            Redim Preserve csv(iIndex)
            csv(iIndex) = ""
        Else
            csv(iIndex) = csv(iIndex) & sChar
        End If
    Next
    Process_CSV_Line = csv
End Function

' Request_uTorrent
' Given a URL, append the token and download the page from uTorrent

Function Request_uTorrent(sURL)
    Dim sAddress

    If utToken <> "" Then
        ' We have a token
        sAddress = "http://" & utSERVER & "/gui/?" & sURL & "&token=" & utToken
    ElseIf sURL <> "token.html" Then
        Call Get_uTorrent_Token
        Request_uTorrent = Request_uTorrent(sURL)
        Exit Function
    Else
        sAddress = "http://" & utSERVER & "/gui/token.html"
    End If

    ' Error handling is required in case uTorrent isn't running. This approach works, but could be much better.
    On Error Resume Next
    Dim oWeb : Set oWeb = CreateObject("MSXML2.XMLHTTP")
    oWeb.Open "GET", sAddress, False, utUSERNAME, utPASSWORD
    oWeb.Send
    Request_uTorrent = oWeb.ResponseText
    On Error Goto 0

    Set oWeb = Nothing

    ' If we get an "invalid request" then the token is out of date
    If Request_uTorrent = vbcrlf & "invalid request" Then
        Call Get_uTorrent_Token
        Request_uTorrent = Request_uTorrent(sURL)
        Exit Function
    End If
End Function

' Get_uTorrent_Token
' Connects to token.html on the uTorrent webserver to get a token that enables
' further API calls to be made. Called automatically by Request_uTorrent although
' can be called manually if performance is critical (reduces the calls by 1).

Sub Get_uTorrent_Token
    utToken = ""
    Dim sResponse : sResponse = Request_uTorrent("token.html")
    Dim re : Set re = New RegExp
    re.IgnoreCase = True
    re.Global = True
    re.Pattern = "<div.+?>(.+?)<\/div>"
    Dim m : Set m = re.Execute(sResponse)
    If m.Count > 0 Then
        utToken = m(0).SubMatches(0)
    Else
        ' Unable to extract token. Bail.
        WScript.Quit
    End If

    Set m = Nothing
    Set re = Nothing
End Sub
1

To elaborate on iglvzx's comment, you can use uTorrent's Web API to get a list of active torrents. To use the API, all you have to do is enable it in the settings. Then, it's a simple HTTP GET call - can be to local host if your script runs on the same machine.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .