Transmission (bittorrent client) RPC API for Ruby on Rails (gem)
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.

README.md

Trans::Api

Trans::Api is an ruby implementation for Transmission RPC (bittorrent client). Based on RPC spec 13328 https://trac.transmissionbt.com/browser/trunk/extras/rpc-spec.txt

Build Status

It required for the Transmission RPC to run the 'remote access':

OSX:

Transmission > Preferences > Remote (tab) > Enable remote access

Platforms (tested)

This gem is (build and) tested with:

  • OSX: Lion, Mountain Lion, Mavericks, Yosemite, El Capitan
  • Linux: Debian, Ubuntu
  • Ruby: 1.9.3, 2.x
  • Rails: 3.2.x, 4.x
  • Transmission > 2.73

Roadmap

  • Version (0.0.1)

    • Initial project
  • Version (0.0.2)

    • Session object include: 'blocklist', 'port-test'
    • Torrent object include 'torrent-start-now', 'queue-move-top/up/down/bottom'
    • Torrent object 'delete_all!' explicit torrent references
    • Torrent object 'waitfor' helper to check for lambda after/before calling it's chained cousin
  • Version (0.0.3)

    • Querying name before add (duplicate detect), not hammering torrent-add (due to a transmission BUG)
    • Added some File internal fields (total and completed transfer)
  • Version (0.0.4)

    • Added Session.alt_speed_time_day_options, returns a list of values to set alt_speed_time_day
    • Added Session.reload!, that reconnects to the client (for example using alternate configs)
    • Added Session.update_blocklist!, updating the current set blocklist
  • Version (0.0.5)

    • Added Session.connected?, checks if there is a valid connection to the client
    • Fixed Torrent.waitfor chained method arguements
    • Added magnet support
  • Version (0.0.6)

    • Fixed various deployment/ build issues

Changelog (call changes)

  • Version (0.0.3)

    • Torrent.add_metainfo(base64, filename, options={}) -> requires a filename parameter

Known Issues

The Transmission RPC call 'torrent-remove' (implemented as torrent.delete! and Torrent::delete_all!) will crash the daemon! This is NOT a known Transmission issue.

Due to a Transmission bug (https://trac.transmissionbt.com/ticket/5614) duplicate torrents are accepted by the RPC call. The GUI will eventually crash the daemon, when interacting with these duplicate files (or instances). Torrent.add_file/ add_metainfo queries for duplicates to omit this bug.

On rapid RPC calls the client will ignore the request, and respond with successfull. For example rapid Torrent.delete! will respond with status successfull, but it remains active. You can use a blocked call via waitfor (chained) to make sure the action was completed.

Installation

Add this line to your application's Gemfile:

gem 'trans-api', github: "dblommesteijn/trans-api"

And then execute:

$ bundle

Configuration

Define a configuration for your connection (initialize script)

CONFIG = { host: "localhost", port: 9091, user: "admin", pass: "admin", path: "/transmission/rpc" }

Setup default configuration (initialize script)

Trans::Api::Client.config = CONFIG

Define default torrent fields (bulk requests) NOTE: connection is slow when running many torrents with a large amount of fields (transmission rpc issue). On requesting an additional info field from the torrent object, a new call is made to the RPC (and stored withing the object).

Trans::Api::Torrent.default_fields = [ :id, :status, :name ]

Example

Trans::Api::Torrent.default_fields = [ :id, :status, :name ]
# loads the torrent object of id 1 with fields: :id, :status, :name
id = 1
torrent = Trans::Api::Torrent.find id
# calls the rpc to receive files from the defined torrent
torrent.files

Usage

Trans api can be used in two ways:

  1. Connect (raw class)
tc = Trans::Api::Connect.new CONFIG
torrents = tc.torrent_get([:id, :name, :status])
  1. Mapped objects (torrent, file, session classes)

    examples below.

Requesting Torrent Info

Get all registered torrents

Trans::Api::Torrent.all

Get a specific torrent by transmission id NOTE: transmission assigns random ids to torrents on daemon start

id = 1
Trans::Api::Torrent.find id

Torrent static calls

Start all (start all transfers)

Trans::Api::Torrent.start_all

Stop all (stop all transfers)

Trans::Api::Torrent.stop_all

Delete all (tranmission daemon will crash on rapid call)

torrents = Trans::Api::Torrent.all
# assign explicit torrent objects for removal
Trans::Api::Torrent.delete_all torrents

Add torrent file

file = File.open('some file here')
options = {paused: true}
Trans::Api::Torrent.add_file file, options

Add torrent file (via base64)

file = File.open('some file here')
file_name = File.basename(file, ".*") # required >= 0.0.3/ master
options = {paused: true}
base64_file_contents = Base64.encode64 file.read
Trans::Api::Torrent.add_metainfo base64_file_contents, file_name, options

Add magnet URI

magnet_link = "magnet:?xt=urn:btih:42ae58b8f59bd19fe97d6ca6fd884b2e9666a4d1&dn=debian-6.0.6-amd64-CD-2.iso&tr=http%3A%2F%2Fbttracker.debian.org%3A6969%2Fannounce"
torrent = Trans::Api::Torrent.add_magnet(magnet_link, paused: true)

Get all fields

Trans::Api::Torrent.ACCESSOR_FIELDS

Get all Mutable fields

Trans::Api::Torrent.MUTATOR_FIELDS

Torrent instance actions

Get a torrent object

id = 1
torrent = Trans::Api::Torrent.find id

Save (store changed values)

torrent.save!

Start (activate torrent for transfer)

torrent.start!

Start Now (not sure what's the difference to start!, it's a different API call)

torrent.start_now!

Reset (reload all torrent fields, including later requested ones)

torrent.reset!

Stop (stop torrent transfer)

torrent.stop!

File Objects (returns a list of Trans::Api::File objects)

torrent.files_objects

Status names (get the status name of the torrent)

torrent.status_name

Verify (recheck downloaded files)

torrent.verify!

Reannounce Torrent

torrent.reannounce!

Delete (tranmission daemon will crash on rapid call)

torrent.delete! {delete_local_data: true}

Waitfor (automatic delayed responce after/before chained method is called)

Blocking busy waiting for lambda to return true

# waitfor status name not equals stopped after calling start!
optional = :after
torrent.waitfor( lambda{|t| t.status_name != :stopped}, optional ).start!

# waitfor status name equals stopped after calling stop!
torrent.waitfor( lambda{|t| t.status_name == :stopped}, optional ).stop!
# NOTE: waitfor can be used for blocking without a chained method (optional = :before only)

# via the Torrent object `reset_exception` we can verify if the torrent is probably removed
torrent.waitfor(lambda{|t| t.last_error[:error] == "reset_exception"}).delete!(delete_local_data: true)

NOTE: defined torrent accessor fields are defined as instance methods to the Torrent object

Session Info

Get session object (singleton)

session = Trans::Api::Session.instance

Get available fields (returns symbols of get/set fields)

session.fields

Get all fields and values

session.fields_and_values

Reset (reload object, request information and not saving changes)

session.reset!

Reload (reload the client connection, and configuration)

session.reload!

Enable set and update blocklist

# set an url
session.blocklist_url = "http://list.iblocklist.com/?list=bt_level3&fileformat=p2p&archiveformat=gz"
# enable blocklist
session.blocklist_enable = true
# save changes
session.save!
# force update
begin
  session.update_blocklist!
rescue Exception => e
  # handle http exceptions here!
end

NOTE: defined session fields are defined as instance methods to the Session object

File Info

Getting files from a torrent (file cannot be used standalone, it's an helper class)

id = 1
torrent = Trans::Api::Torrent.find id
torrent.files_objects.each do |file|
	# manipulate or stat the file here! (unwant, want)
end
# save the torrent (internal changes to files_objects are saved as well)
torrent.save!

File name

file.name

File mark unwant (mark for ignore, not download)

file.unwant

File mark want (mark for download)

file.want

File wanted? (marked for download)

file.wanted?

File stat

file.stat

NOTE: changed preferences (want, unwant) set options on the linked Torrent object, after saving torrent (torrent.save!) file mutations are stored.

Running Tests

Run the unittest embedded with the project from the commandline. Configure the CONFIG variable with an escaped json to provide configuration for your transmission client.

  • WARNING: torrents and files will be destroyed by these tests *
# example format: define CONFIG in escaped json
$ CONFIG="{\"host\":\"localhost\",\"port\":1234,\"user\":\"youruser\",\"pass\":\"yourpass\",\"path\":\"/transmission/rpc\"}"

Run 'test/unit/trans_connect.rb' to test the intermediate layer between the RPC API and wrappers.

$ CONFIG="{\"host\":\"localhost\",\"port\":1234,\"user\":\"youruser\",\"pass\":\"yourpass\",\"path\":\"/transmission/rpc\"}" ruby -I test test/unit/trans_connect.rb

Run 'test/unit/trans_session_object.rb' to test the working of Trans::Api::Session.

# run unit test session
$ CONFIG="{\"host\":\"localhost\",\"port\":1234,\"user\":\"youruser\",\"pass\":\"yourpass\",\"path\":\"/transmission/rpc\"}" ruby -I test test/unit/trans_session_object.rb

Run 'test/unit/trans_torrent_object.rb' to test the working of Trans::Api::Torrent.

# run unit test session
$ CONFIG="{\"host\":\"localhost\",\"port\":1234,\"user\":\"youruser\",\"pass\":\"yourpass\",\"path\":\"/transmission/rpc\"}" ruby -I test test/unit/trans_torrent_object.rb

NOTE: test test_torrent_rapid_delete will fail because of an issue with rapid calling.

Vagrant

Besides running your existing client with Transmission you can test on an isolated virtual machine via Vagrant.

Download Dependencies

Download and follow the installation instructions.

Boot the Test VM

Download and run the testing Debian Linux distribution with a test Transmission client.

$ cd /location/of/trans-api
# download and start a testing box (will take some time)
$ vagrant up testing

Configure the CONFIG variable to target the VM

# use this CONFIG string to connect to the testing instance
$ CONFIG="{\"host\":\"localhost\",\"port\":19091,\"user\":\"admin\",\"pass\":\"adm1n\",\"path\":\"/transmission/rpc\"}"
# run tests like described above

NOTE: don't forget to HALT (and DESTROY) the VM when done!

# power down VM
$ vagrant halt testing
# power down and destroy files
$ vagrant destroy testing