do-plugins team mailing list archive
-
do-plugins team
-
Mailing list archive
-
Message #01397
[Merge] lp:~kriomant/do-plugins/transmission into lp:do-plugins
kriomant has proposed merging lp:~kriomant/do-plugins/transmission into lp:do-plugins.
Requested reviews:
Do Plugins Team (do-plugins)
Transmission plug-in.
Allows to control Transmission torrent client using XML-RPC API. Supports starting and stopping torrent downloads, choosing files to download, setting speed limits.
--
https://code.launchpad.net/~kriomant/do-plugins/transmission/+merge/30966
Your team Do Plugins Team is requested to review the proposed merge of lp:~kriomant/do-plugins/transmission into lp:do-plugins.
=== modified file '.bzrignore'
--- .bzrignore 2010-06-29 08:19:20 +0000
+++ .bzrignore 2010-07-26 16:47:47 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
Makefile.in
Makefile
*.pc
@@ -23,3 +24,11 @@
*.addin.xml
*.gmo
m4/intltool.m4
+=======
+Emesene.addin.xml
+Transmission/Makefile
+Transmission/Makefile.in
+Transmission/Transmission.pidb
+Transmission/bin
+Transmission/Resources/Transmission.addin.xml
+>>>>>>> MERGE-SOURCE
=== modified file 'DoPlugins.mds'
--- DoPlugins.mds 2009-11-08 23:58:01 +0000
+++ DoPlugins.mds 2010-07-26 16:47:47 +0000
@@ -78,6 +78,7 @@
<Entry build="True" name="Baconator" configuration="Debug" />
<Entry build="True" name="Chromium" configuration="Debug" />
<Entry build="True" name="GNOME-Calculator" configuration="Debug" />
+ <Entry build="True" name="Transmission" configuration="Debug" />
</Configuration>
<Configuration name="Release" ctype="CombineConfiguration">
<Entry build="True" name="Rhythmbox" configuration="Release" />
@@ -157,6 +158,7 @@
<Entry build="True" name="Baconator" configuration="Release" />
<Entry build="True" name="Chromium" configuration="Release" />
<Entry build="True" name="GNOME-Calculator" configuration="Release" />
+ <Entry build="True" name="Transmission" configuration="Release" />
</Configuration>
</Configurations>
<StartMode startupentry="Rhythmbox" single="True">
@@ -237,6 +239,7 @@
<Execute type="None" entry="Baconator" />
<Execute type="None" entry="Chromium" />
<Execute type="None" entry="GNOME-Calculator" />
+ <Execute type="None" entry="Transmission" />
</StartMode>
<MonoDevelop.ChangeLogAddIn.ChangeLogInfo policy="UpdateNearestChangeLog" />
<Entries>
@@ -317,5 +320,6 @@
<Entry filename="Baconator/Baconator.mdp" />
<Entry filename="Chromium/Chromium.mdp" />
<Entry filename="GNOME-Calculator/GNOME-Calculator.mdp" />
+ <Entry filename="Transmission/Transmission.mdp" />
</Entries>
</Combine>
\ No newline at end of file
=== modified file 'Makefile.am'
--- Makefile.am 2009-11-08 23:58:01 +0000
+++ Makefile.am 2010-07-26 16:47:47 +0000
@@ -70,6 +70,7 @@
TinyUrl \
Tracker \
Translate \
+ Transmission \
Tomboy \
Vinagre \
VirtualBox \
=== added directory 'Transmission'
=== added file 'Transmission/Makefile.am'
--- Transmission/Makefile.am 1970-01-01 00:00:00 +0000
+++ Transmission/Makefile.am 2010-07-26 16:47:47 +0000
@@ -0,0 +1,40 @@
+include $(top_srcdir)/build.rules.mk
+
+ASSEMBLY=Transmission
+
+FILES = \
+ gtk-gui/Transmission.TransmissionConfig.cs \
+ gtk-gui/generated.cs \
+ src/Config/TransmissionConfig.cs \
+ src/ITorrentEntry.cs \
+ src/JsonCollectionImporter.cs \
+ src/Utils.cs \
+ src/TorrentDirectoryItem.cs \
+ src/TorrentFileItem.cs \
+ src/TorrentFileSetPriorityAction.cs \
+ src/TorrentItem.cs \
+ src/TorrentItemSource.cs \
+ src/TorrentAbstractLimitSpeedAction.cs \
+ src/TorrentLimitDownloadSpeedAction.cs \
+ src/TorrentLimitUploadSpeedAction.cs \
+ src/TorrentMarkForDownloadAction.cs \
+ src/TorrentStartAction.cs \
+ src/TorrentStopAction.cs \
+ src/TorrentUnmarkForDownloadAction.cs \
+ src/TorrentVerifyAction.cs \
+ src/TorrentOperateAction.cs \
+ src/TransmissionAPI.cs \
+ src/TransmissionPlugin.cs
+
+RESOURCES = \
+ Resources/Transmission.addin.xml \
+ gtk-gui/gui.stetic \
+ Resources/icons/transmission.png
+
+REFERENCES = \
+ System \
+ System.Core \
+ lib/Jayrock.Json.dll \
+ $(DO_PLATFORM_LINUX_LIBS) \
+ $(DO_UNIVERSE_LIBS) \
+ $(GTK_SHARP_20_LIBS)
=== added directory 'Transmission/Resources'
=== added file 'Transmission/Resources/Transmission.addin.xml.in'
--- Transmission/Resources/Transmission.addin.xml.in 1970-01-01 00:00:00 +0000
+++ Transmission/Resources/Transmission.addin.xml.in 2010-07-26 16:47:47 +0000
@@ -0,0 +1,37 @@
+<Addin
+ id="Transmission"
+ namespace="Do"
+ version="0.1"
+ name="Transmission"
+ description="Control Transmission torrent client."
+ author="Mikhail Trishchenkov"
+ category="Community"
+ defaultEnabled="true"
+ url="http://do.davebsd.com/wiki/Transmission_Plugin"
+ >
+
+ <Runtime>
+ <Import assembly="Transmission.dll"/>
+ </Runtime>
+
+ <!--Localizer type="Gettext" catalog="gnome-do-plugins" location="@expanded_datadir@/locale" /-->
+
+ <Dependencies>
+ <Addin id="Universe" version="1.0" />
+ </Dependencies>
+
+ <Extension path="/Do/ItemSource">
+ <ItemSource type="Transmission.TorrentItemSource" />
+ </Extension>
+ <Extension path="/Do/Action">
+ <Action type="Transmission.TorrentStopAction" />
+ <Action type="Transmission.TorrentStartAction" />
+ <Action type="Transmission.TorrentVerifyAction" />
+ <Action type="Transmission.TorrentLimitDownloadSpeedAction" />
+ <Action type="Transmission.TorrentLimitUploadSpeedAction" />
+ <Action type="Transmission.TorrentFileSetPriorityAction" />
+ <Action type="Transmission.TorrentMarkForDownloadAction" />
+ <Action type="Transmission.TorrentUnmarkForDownloadAction" />
+ <Action type="Transmission.TorrentOperateAction" />
+ </Extension>
+</Addin>
=== added directory 'Transmission/Resources/icons'
=== added file 'Transmission/Resources/icons/transmission.png'
Binary files Transmission/Resources/icons/transmission.png 1970-01-01 00:00:00 +0000 and Transmission/Resources/icons/transmission.png 2010-07-26 16:47:47 +0000 differ
=== added file 'Transmission/Transmission.mdp'
--- Transmission/Transmission.mdp 1970-01-01 00:00:00 +0000
+++ Transmission/Transmission.mdp 2010-07-26 16:47:47 +0000
@@ -0,0 +1,64 @@
+<Project name="Transmission" fileversion="2.0" DefaultNamespace="Transmission" language="C#" targetFramework="3.5" ctype="DotNetProject">
+ <Configurations active="Debug">
+ <Configuration name="Debug" ctype="DotNetProjectConfiguration">
+ <Output directory="bin/Debug" assembly="Transmission" />
+ <Build debugmode="True" target="Library" />
+ <Execution consolepause="True" runwithwarnings="True" runtime="MsNet" />
+ <CodeGeneration compiler="Mcs" warninglevel="4" optimize="False" unsafecodeallowed="False" generateoverflowchecks="False" definesymbols="DEBUG" generatexmldocumentation="False" ctype="CSharpCompilerParameters" />
+ </Configuration>
+ <Configuration name="Release" ctype="DotNetProjectConfiguration">
+ <Output directory="bin/Release" assembly="Transmission" />
+ <Build debugmode="False" target="Library" />
+ <Execution consolepause="True" runwithwarnings="True" runtime="MsNet" />
+ <CodeGeneration compiler="Mcs" warninglevel="4" optimize="False" unsafecodeallowed="False" generateoverflowchecks="False" generatexmldocumentation="False" ctype="CSharpCompilerParameters" />
+ </Configuration>
+ </Configurations>
+ <Contents>
+ <File subtype="Directory" buildaction="Compile" name="src" />
+ <File subtype="Directory" buildaction="Compile" name="Resources" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/Transmission.addin.xml" />
+ <File subtype="Code" buildaction="EmbedAsResource" name="gtk-gui/gui.stetic" />
+ <File subtype="Code" buildaction="Compile" name="gtk-gui/generated.cs" />
+ <File subtype="Code" buildaction="Compile" name="gtk-gui/Transmission.TransmissionConfig.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/Config/TransmissionConfig.cs" />
+ <File subtype="Directory" buildaction="Compile" name="Resources/icons" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-add.png" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-remove.png" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-revisions.png" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-share.png" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-start.png" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-stop.png" />
+ <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-web.png" />
+ <File subtype="Code" buildaction="Compile" name="src/ITorrentEntry.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/JsonCollectionImporter.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentDirectoryItem.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentFileItem.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentFileSetPriorityAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentItem.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentItemSource.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentAbstractLimitSpeedAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentLimitDownloadSpeedAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentMarkForDownloadAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentStartAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentStopAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentUnmarkForDownloadAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentVerifyAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TransmissionAPI.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TransmissionPlugin.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/Utils.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentLimitUploadSpeedAction.cs" />
+ <File subtype="Code" buildaction="Compile" name="src/TorrentOperateAction.cs" />
+ </Contents>
+ <References>
+ <ProjectReference type="Gac" localcopy="True" refto="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ <ProjectReference type="Gac" localcopy="True" refto="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ <ProjectReference type="Gac" localcopy="True" refto="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <ProjectReference type="Gac" localcopy="True" refto="Mono.Addins, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
+ <ProjectReference type="Gac" localcopy="True" refto="Do.Platform, Version=0.9.0.0, Culture=neutral" />
+ <ProjectReference type="Gac" localcopy="True" refto="Do.Universe, Version=0.9.0.0, Culture=neutral" />
+ <ProjectReference type="Gac" localcopy="True" refto="Do.Platform.Linux, Version=0.9.0.0, Culture=neutral" />
+ <ProjectReference type="Assembly" localcopy="True" specificVersion="False" refto="lib/Jayrock.Json.dll" />
+ </References>
+ <GtkDesignInfo gettextClass="Mono.Addins.AddinManager.CurrentLocalizer" />
+</Project>
\ No newline at end of file
=== added directory 'Transmission/gtk-gui'
=== added file 'Transmission/gtk-gui/Transmission.TransmissionConfig.cs'
--- Transmission/gtk-gui/Transmission.TransmissionConfig.cs 1970-01-01 00:00:00 +0000
+++ Transmission/gtk-gui/Transmission.TransmissionConfig.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,161 @@
+// ------------------------------------------------------------------------------
+// <autogenerated>
+// This code was generated by a tool.
+//
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </autogenerated>
+// ------------------------------------------------------------------------------
+
+namespace Transmission {
+
+
+ public partial class TransmissionConfig {
+
+ private Gtk.Alignment alignment108;
+
+ private Gtk.Table table1;
+
+ private Gtk.HBox hbox1;
+
+ private Gtk.Entry address_entry;
+
+ private Gtk.Label remote_port_label;
+
+ private Gtk.Entry port_entry;
+
+ private Gtk.Label label1;
+
+ private Gtk.Label label2;
+
+ private Gtk.Label label3;
+
+ private Gtk.Entry password_entry;
+
+ private Gtk.Entry user_name_entry;
+
+ protected virtual void Build() {
+ Stetic.Gui.Initialize(this);
+ // Widget Transmission.TransmissionConfig
+ Stetic.BinContainer.Attach(this);
+ this.Name = "Transmission.TransmissionConfig";
+ // Container child Transmission.TransmissionConfig.Gtk.Container+ContainerChild
+ this.alignment108 = new Gtk.Alignment(0.5F, 0F, 1F, 0F);
+ this.alignment108.Name = "alignment108";
+ // Container child alignment108.Gtk.Container+ContainerChild
+ this.table1 = new Gtk.Table(((uint)(3)), ((uint)(2)), false);
+ this.table1.Name = "table1";
+ this.table1.RowSpacing = ((uint)(6));
+ this.table1.ColumnSpacing = ((uint)(6));
+ // Container child table1.Gtk.Table+TableChild
+ this.hbox1 = new Gtk.HBox();
+ this.hbox1.Name = "hbox1";
+ this.hbox1.Spacing = 6;
+ // Container child hbox1.Gtk.Box+BoxChild
+ this.address_entry = new Gtk.Entry();
+ this.address_entry.CanFocus = true;
+ this.address_entry.Name = "address_entry";
+ this.address_entry.IsEditable = true;
+ this.address_entry.InvisibleChar = 'â';
+ this.hbox1.Add(this.address_entry);
+ Gtk.Box.BoxChild w1 = ((Gtk.Box.BoxChild)(this.hbox1[this.address_entry]));
+ w1.Position = 0;
+ // Container child hbox1.Gtk.Box+BoxChild
+ this.remote_port_label = new Gtk.Label();
+ this.remote_port_label.Name = "remote_port_label";
+ this.remote_port_label.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("Port");
+ this.hbox1.Add(this.remote_port_label);
+ Gtk.Box.BoxChild w2 = ((Gtk.Box.BoxChild)(this.hbox1[this.remote_port_label]));
+ w2.Position = 1;
+ w2.Expand = false;
+ w2.Fill = false;
+ // Container child hbox1.Gtk.Box+BoxChild
+ this.port_entry = new Gtk.Entry();
+ this.port_entry.CanFocus = true;
+ this.port_entry.Name = "port_entry";
+ this.port_entry.IsEditable = true;
+ this.port_entry.WidthChars = 5;
+ this.port_entry.MaxLength = 5;
+ this.port_entry.InvisibleChar = 'â';
+ this.hbox1.Add(this.port_entry);
+ Gtk.Box.BoxChild w3 = ((Gtk.Box.BoxChild)(this.hbox1[this.port_entry]));
+ w3.Position = 2;
+ w3.Expand = false;
+ this.table1.Add(this.hbox1);
+ Gtk.Table.TableChild w4 = ((Gtk.Table.TableChild)(this.table1[this.hbox1]));
+ w4.LeftAttach = ((uint)(1));
+ w4.RightAttach = ((uint)(2));
+ w4.YOptions = ((Gtk.AttachOptions)(4));
+ // Container child table1.Gtk.Table+TableChild
+ this.label1 = new Gtk.Label();
+ this.label1.Name = "label1";
+ this.label1.Xalign = 0F;
+ this.label1.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("_Address");
+ this.label1.UseUnderline = true;
+ this.table1.Add(this.label1);
+ Gtk.Table.TableChild w5 = ((Gtk.Table.TableChild)(this.table1[this.label1]));
+ w5.XOptions = ((Gtk.AttachOptions)(4));
+ w5.YOptions = ((Gtk.AttachOptions)(4));
+ // Container child table1.Gtk.Table+TableChild
+ this.label2 = new Gtk.Label();
+ this.label2.Name = "label2";
+ this.label2.Xalign = 0F;
+ this.label2.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("_Password");
+ this.label2.UseUnderline = true;
+ this.table1.Add(this.label2);
+ Gtk.Table.TableChild w6 = ((Gtk.Table.TableChild)(this.table1[this.label2]));
+ w6.TopAttach = ((uint)(2));
+ w6.BottomAttach = ((uint)(3));
+ w6.XOptions = ((Gtk.AttachOptions)(4));
+ w6.YOptions = ((Gtk.AttachOptions)(4));
+ // Container child table1.Gtk.Table+TableChild
+ this.label3 = new Gtk.Label();
+ this.label3.Name = "label3";
+ this.label3.Xalign = 0F;
+ this.label3.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("_Login");
+ this.label3.UseUnderline = true;
+ this.table1.Add(this.label3);
+ Gtk.Table.TableChild w7 = ((Gtk.Table.TableChild)(this.table1[this.label3]));
+ w7.TopAttach = ((uint)(1));
+ w7.BottomAttach = ((uint)(2));
+ w7.XOptions = ((Gtk.AttachOptions)(4));
+ w7.YOptions = ((Gtk.AttachOptions)(4));
+ // Container child table1.Gtk.Table+TableChild
+ this.password_entry = new Gtk.Entry();
+ this.password_entry.CanFocus = true;
+ this.password_entry.Name = "password_entry";
+ this.password_entry.IsEditable = true;
+ this.password_entry.Visibility = false;
+ this.password_entry.InvisibleChar = 'â';
+ this.table1.Add(this.password_entry);
+ Gtk.Table.TableChild w8 = ((Gtk.Table.TableChild)(this.table1[this.password_entry]));
+ w8.TopAttach = ((uint)(2));
+ w8.BottomAttach = ((uint)(3));
+ w8.LeftAttach = ((uint)(1));
+ w8.RightAttach = ((uint)(2));
+ w8.YOptions = ((Gtk.AttachOptions)(4));
+ // Container child table1.Gtk.Table+TableChild
+ this.user_name_entry = new Gtk.Entry();
+ this.user_name_entry.CanFocus = true;
+ this.user_name_entry.Name = "user_name_entry";
+ this.user_name_entry.IsEditable = true;
+ this.user_name_entry.InvisibleChar = 'â';
+ this.table1.Add(this.user_name_entry);
+ Gtk.Table.TableChild w9 = ((Gtk.Table.TableChild)(this.table1[this.user_name_entry]));
+ w9.TopAttach = ((uint)(1));
+ w9.BottomAttach = ((uint)(2));
+ w9.LeftAttach = ((uint)(1));
+ w9.RightAttach = ((uint)(2));
+ w9.YOptions = ((Gtk.AttachOptions)(4));
+ this.alignment108.Add(this.table1);
+ this.Add(this.alignment108);
+ if ((this.Child != null)) {
+ this.Child.ShowAll();
+ }
+ this.Hide();
+ this.user_name_entry.Changed += new System.EventHandler(this.OnUserNameEntryChanged);
+ this.password_entry.Changed += new System.EventHandler(this.OnPasswordEntryChanged);
+ }
+ }
+}
=== added file 'Transmission/gtk-gui/generated.cs'
--- Transmission/gtk-gui/generated.cs 1970-01-01 00:00:00 +0000
+++ Transmission/gtk-gui/generated.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,82 @@
+// ------------------------------------------------------------------------------
+// <autogenerated>
+// This code was generated by a tool.
+//
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </autogenerated>
+// ------------------------------------------------------------------------------
+
+namespace Stetic {
+
+
+ internal class Gui {
+
+ private static bool initialized;
+
+ internal static void Initialize(Gtk.Widget iconRenderer) {
+ if ((Stetic.Gui.initialized == false)) {
+ Stetic.Gui.initialized = true;
+ }
+ }
+ }
+
+ internal class BinContainer {
+
+ private Gtk.Widget child;
+
+ private Gtk.UIManager uimanager;
+
+ public static BinContainer Attach(Gtk.Bin bin) {
+ BinContainer bc = new BinContainer();
+ bin.SizeRequested += new Gtk.SizeRequestedHandler(bc.OnSizeRequested);
+ bin.SizeAllocated += new Gtk.SizeAllocatedHandler(bc.OnSizeAllocated);
+ bin.Added += new Gtk.AddedHandler(bc.OnAdded);
+ return bc;
+ }
+
+ private void OnSizeRequested(object sender, Gtk.SizeRequestedArgs args) {
+ if ((this.child != null)) {
+ args.Requisition = this.child.SizeRequest();
+ }
+ }
+
+ private void OnSizeAllocated(object sender, Gtk.SizeAllocatedArgs args) {
+ if ((this.child != null)) {
+ this.child.Allocation = args.Allocation;
+ }
+ }
+
+ private void OnAdded(object sender, Gtk.AddedArgs args) {
+ this.child = args.Widget;
+ }
+
+ public void SetUiManager(Gtk.UIManager uim) {
+ this.uimanager = uim;
+ this.child.Realized += new System.EventHandler(this.OnRealized);
+ }
+
+ private void OnRealized(object sender, System.EventArgs args) {
+ if ((this.uimanager != null)) {
+ Gtk.Widget w;
+ w = this.child.Toplevel;
+ if (((w != null) && typeof(Gtk.Window).IsInstanceOfType(w))) {
+ ((Gtk.Window)(w)).AddAccelGroup(this.uimanager.AccelGroup);
+ this.uimanager = null;
+ }
+ }
+ }
+ }
+
+ internal class ActionGroups {
+
+ public static Gtk.ActionGroup GetActionGroup(System.Type type) {
+ return Stetic.ActionGroups.GetActionGroup(type.FullName);
+ }
+
+ public static Gtk.ActionGroup GetActionGroup(string name) {
+ return null;
+ }
+ }
+}
=== added file 'Transmission/gtk-gui/gui.stetic'
--- Transmission/gtk-gui/gui.stetic 1970-01-01 00:00:00 +0000
+++ Transmission/gtk-gui/gui.stetic 2010-07-26 16:47:47 +0000
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="utf-8"?>
+<stetic-interface>
+ <configuration>
+ <images-root-path>..</images-root-path>
+ <target-gtk-version>2.12</target-gtk-version>
+ </configuration>
+ <import>
+ <widget-library name="Do.Platform.Linux, Version=0.9.0.0, Culture=neutral" />
+ <widget-library name="../bin/Debug/Transmission.dll" internal="true" />
+ </import>
+ <widget class="Gtk.Bin" id="Transmission.TransmissionConfig" design-size="444 300">
+ <property name="MemberName" />
+ <property name="Visible">False</property>
+ <child>
+ <widget class="Gtk.Alignment" id="alignment108">
+ <property name="MemberName" />
+ <property name="Yscale">0</property>
+ <property name="Yalign">0</property>
+ <child>
+ <widget class="Gtk.Table" id="table1">
+ <property name="MemberName" />
+ <property name="NRows">3</property>
+ <property name="NColumns">2</property>
+ <property name="RowSpacing">6</property>
+ <property name="ColumnSpacing">6</property>
+ <child>
+ <widget class="Gtk.HBox" id="hbox1">
+ <property name="MemberName" />
+ <property name="Spacing">6</property>
+ <child>
+ <widget class="Gtk.Entry" id="address_entry">
+ <property name="MemberName" />
+ <property name="CanFocus">True</property>
+ <property name="IsEditable">True</property>
+ <property name="InvisibleChar">â</property>
+ </widget>
+ <packing>
+ <property name="Position">0</property>
+ <property name="AutoSize">True</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Label" id="remote_port_label">
+ <property name="MemberName">remote_port_label</property>
+ <property name="LabelProp" translatable="yes">Port</property>
+ </widget>
+ <packing>
+ <property name="Position">1</property>
+ <property name="AutoSize">True</property>
+ <property name="Expand">False</property>
+ <property name="Fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Entry" id="port_entry">
+ <property name="MemberName">port_entry</property>
+ <property name="CanFocus">True</property>
+ <property name="IsEditable">True</property>
+ <property name="WidthChars">5</property>
+ <property name="MaxLength">5</property>
+ <property name="InvisibleChar">â</property>
+ </widget>
+ <packing>
+ <property name="Position">2</property>
+ <property name="AutoSize">False</property>
+ <property name="Expand">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="LeftAttach">1</property>
+ <property name="RightAttach">2</property>
+ <property name="AutoSize">True</property>
+ <property name="YOptions">Fill</property>
+ <property name="XExpand">True</property>
+ <property name="XFill">True</property>
+ <property name="XShrink">False</property>
+ <property name="YExpand">False</property>
+ <property name="YFill">True</property>
+ <property name="YShrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Label" id="label1">
+ <property name="MemberName" />
+ <property name="Xalign">0</property>
+ <property name="LabelProp" translatable="yes">_Address</property>
+ <property name="UseUnderline">True</property>
+ </widget>
+ <packing>
+ <property name="AutoSize">True</property>
+ <property name="XOptions">Fill</property>
+ <property name="YOptions">Fill</property>
+ <property name="XExpand">False</property>
+ <property name="XFill">True</property>
+ <property name="XShrink">False</property>
+ <property name="YExpand">False</property>
+ <property name="YFill">True</property>
+ <property name="YShrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Label" id="label2">
+ <property name="MemberName" />
+ <property name="Xalign">0</property>
+ <property name="LabelProp" translatable="yes">_Password</property>
+ <property name="UseUnderline">True</property>
+ </widget>
+ <packing>
+ <property name="TopAttach">2</property>
+ <property name="BottomAttach">3</property>
+ <property name="AutoSize">True</property>
+ <property name="XOptions">Fill</property>
+ <property name="YOptions">Fill</property>
+ <property name="XExpand">False</property>
+ <property name="XFill">True</property>
+ <property name="XShrink">False</property>
+ <property name="YExpand">False</property>
+ <property name="YFill">True</property>
+ <property name="YShrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Label" id="label3">
+ <property name="MemberName" />
+ <property name="Xalign">0</property>
+ <property name="LabelProp" translatable="yes">_Login</property>
+ <property name="UseUnderline">True</property>
+ </widget>
+ <packing>
+ <property name="TopAttach">1</property>
+ <property name="BottomAttach">2</property>
+ <property name="AutoSize">True</property>
+ <property name="XOptions">Fill</property>
+ <property name="YOptions">Fill</property>
+ <property name="XExpand">False</property>
+ <property name="XFill">True</property>
+ <property name="XShrink">False</property>
+ <property name="YExpand">False</property>
+ <property name="YFill">True</property>
+ <property name="YShrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Entry" id="password_entry">
+ <property name="MemberName" />
+ <property name="CanFocus">True</property>
+ <property name="IsEditable">True</property>
+ <property name="Visibility">False</property>
+ <property name="InvisibleChar">â</property>
+ <signal name="Changed" handler="OnPasswordEntryChanged" />
+ </widget>
+ <packing>
+ <property name="TopAttach">2</property>
+ <property name="BottomAttach">3</property>
+ <property name="LeftAttach">1</property>
+ <property name="RightAttach">2</property>
+ <property name="AutoSize">True</property>
+ <property name="YOptions">Fill</property>
+ <property name="XExpand">True</property>
+ <property name="XFill">True</property>
+ <property name="XShrink">False</property>
+ <property name="YExpand">False</property>
+ <property name="YFill">True</property>
+ <property name="YShrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="Gtk.Entry" id="user_name_entry">
+ <property name="MemberName" />
+ <property name="CanFocus">True</property>
+ <property name="IsEditable">True</property>
+ <property name="InvisibleChar">â</property>
+ <signal name="Changed" handler="OnUserNameEntryChanged" />
+ </widget>
+ <packing>
+ <property name="TopAttach">1</property>
+ <property name="BottomAttach">2</property>
+ <property name="LeftAttach">1</property>
+ <property name="RightAttach">2</property>
+ <property name="AutoSize">True</property>
+ <property name="YOptions">Fill</property>
+ <property name="XExpand">True</property>
+ <property name="XFill">True</property>
+ <property name="XShrink">False</property>
+ <property name="YExpand">False</property>
+ <property name="YFill">True</property>
+ <property name="YShrink">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</stetic-interface>
\ No newline at end of file
=== added directory 'Transmission/lib'
=== added file 'Transmission/lib/Jayrock.Json.dll'
Binary files Transmission/lib/Jayrock.Json.dll 1970-01-01 00:00:00 +0000 and Transmission/lib/Jayrock.Json.dll 2010-07-26 16:47:47 +0000 differ
=== added file 'Transmission/lib/Jayrock.Json.pdb'
Binary files Transmission/lib/Jayrock.Json.pdb 1970-01-01 00:00:00 +0000 and Transmission/lib/Jayrock.Json.pdb 2010-07-26 16:47:47 +0000 differ
=== added directory 'Transmission/src'
=== added directory 'Transmission/src/Config'
=== added file 'Transmission/src/Config/TransmissionConfig.cs'
--- Transmission/src/Config/TransmissionConfig.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/Config/TransmissionConfig.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Mono.Addins;
+
+using Gtk;
+
+using Do.Platform;
+
+namespace Transmission
+{
+
+ [System.ComponentModel.Category("File")]
+ [System.ComponentModel.ToolboxItem(true)]
+ public partial class TransmissionConfig : Gtk.Bin
+ {
+ public static string home_path = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
+ public static string settings_path = System.IO.Path.Combine (home_path, ".config/transmission/settings.json");
+
+ static IPreferences prefs;
+
+ public TransmissionConfig()
+ {
+ Build();
+ RefreshView();
+ }
+
+ private void RefreshView()
+ {
+ address_entry.Text = Address;
+ port_entry.Text = Port.ToString();
+ user_name_entry.Text = UserName;
+ password_entry.Text = Password;
+ }
+
+ static TransmissionConfig()
+ {
+ prefs = Services.Preferences.Get<TransmissionConfig>();
+ }
+
+ public static string Address
+ {
+ get { return prefs.Get<string>("Address", "127.0.0.1"); }
+ set { prefs.Set<string> ("Address", value); }
+ }
+
+ public static int Port
+ {
+ get { return prefs.Get<int>("Port", TransmissionAPI.DEFAULT_PORT); }
+ set { prefs.Set<int> ("Port", value); }
+ }
+
+ public static string UserName
+ {
+ get { return prefs.Get<string>("UserName", ""); }
+ set { prefs.Set<string> ("UserName", value); }
+ }
+
+ public static string Password
+ {
+ get { return prefs.Get<string>("Password", ""); }
+ set { prefs.Set<string> ("Password", value); }
+ }
+
+ protected virtual void OnAddressEntryChanged (object sender, System.EventArgs e)
+ {
+ Address = address_entry.Text;
+ TransmissionPlugin.ResetConnection();
+ }
+
+ protected virtual void OnUserNameEntryChanged (object sender, System.EventArgs e)
+ {
+ UserName = user_name_entry.Text;
+ TransmissionPlugin.ResetConnection();
+ }
+
+ protected virtual void OnPasswordEntryChanged (object sender, System.EventArgs e)
+ {
+ Password = password_entry.Text;
+ TransmissionPlugin.ResetConnection();
+ }
+
+ protected virtual void OnPortEntryChanged (object sender, System.EventArgs e)
+ {
+ Port = int.Parse(port_entry.Text);
+ TransmissionPlugin.ResetConnection();
+ }
+
+ }
+}
=== added file 'Transmission/src/ITorrentEntry.cs'
--- Transmission/src/ITorrentEntry.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/ITorrentEntry.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace Transmission {
+
+ public interface ITorrentEntry {
+ // Owner torrent.
+ TorrentItem Torrent { get; }
+
+ // Path on FS.
+ string Path { get; }
+
+ // Get all files under this entry (recursively).
+ // For files return file itself.
+ IEnumerable<TorrentFileItem> GetFiles();
+ }
+}
=== added file 'Transmission/src/JsonCollectionImporter.cs'
--- Transmission/src/JsonCollectionImporter.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/JsonCollectionImporter.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,117 @@
+namespace Transmission
+{
+ #region Imports
+
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Jayrock.Json;
+ using Jayrock.Json.Conversion;
+ using Jayrock.Json.Conversion.Converters;
+
+ #endregion
+
+ /// <summary>
+ /// An abstract base class for importer implementations that can import
+ /// a concrete collection instance from a JSON array.
+ /// </summary>
+
+
+ public abstract class CollectionImporterBase : ImporterBase
+ {
+ private readonly Type _elementType;
+
+
+ public CollectionImporterBase(Type outputType, Type elementType) :
+ base(outputType)
+ {
+ if (elementType == null) throw new ArgumentNullException("elementType");
+
+ _elementType = elementType;
+ }
+
+
+ public Type ElementType
+ {
+ get { return _elementType; }
+ }
+
+
+ protected override object ImportFromArray(ImportContext context, JsonReader reader)
+ {
+ if (context == null) throw new ArgumentNullException("context");
+ if (reader == null) throw new ArgumentNullException("reader");
+
+
+ object collection = CreateCollection();
+
+
+ reader.ReadToken(JsonTokenClass.Array);
+
+
+ ImportElements(collection, context, reader);
+
+
+ if (reader.TokenClass != JsonTokenClass.EndArray)
+ throw new Exception("Implementation error.");
+
+
+ reader.Read();
+ return collection;
+ }
+
+
+ protected abstract object CreateCollection();
+ protected abstract void ImportElements(object collection, ImportContext context, JsonReader reader);
+ }
+
+
+ /// <summary>
+ /// An importer for importing a collection of elements from a JSON array.
+ /// </summary>
+
+
+ public class CollectionImporter<Collection, Element> : CollectionImporterBase
+ where Collection : ICollection<Element>, new()
+ {
+ public CollectionImporter() :
+ base(typeof(Collection), typeof(Element)) { }
+
+
+ protected override object CreateCollection()
+ {
+ return new Collection();
+ }
+
+
+ protected override void ImportElements(object collection, ImportContext context, JsonReader reader)
+ {
+ if (collection == null) throw new ArgumentNullException("collection");
+ if (context == null) throw new ArgumentNullException("context");
+ if (reader == null) throw new ArgumentNullException("reader");
+
+
+ ImportElements((ICollection<Element>) collection, context, reader);
+ }
+
+
+ private static void ImportElements(ICollection<Element> collection, ImportContext context, JsonReader reader)
+ {
+ Debug.Assert(collection != null);
+ Debug.Assert(context != null);
+ Debug.Assert(reader != null);
+
+
+ while (reader.TokenClass != JsonTokenClass.EndArray)
+ collection.Add((Element) context.Import(typeof(Element), reader));
+ }
+ }
+
+ /// <summary>
+ /// Imports <see cref="List{T}"/> from a JSON array.
+ /// </summary>
+
+ public class ListImporter<T> : CollectionImporter<List<T>, T>
+ where T : new() { }
+
+}
\ No newline at end of file
=== added file 'Transmission/src/TorrentAbstractLimitSpeedAction.cs'
--- Transmission/src/TorrentAbstractLimitSpeedAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentAbstractLimitSpeedAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,80 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ public abstract class TorrentAbstractLimitSpeedAction: Act {
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get { yield return typeof (TorrentItem); }
+ }
+
+ public override IEnumerable<Type> SupportedModifierItemTypes {
+ get {
+ yield return typeof (ITextItem);
+ yield return typeof (PredefinedSpeed);
+ }
+ }
+
+ public override bool ModifierItemsOptional {
+ get { return false; }
+ }
+
+ protected abstract PredefinedSpeed GetCurrentSpeedItem(TorrentItem torrent);
+
+ public override IEnumerable<Item> DynamicModifierItemsForItem(Item item) {
+ TorrentItem torrent = (TorrentItem)item;
+
+ yield return new PredefinedSpeed(0, "Unlimited", "Turn download speed limit off");
+ yield return GetCurrentSpeedItem(torrent);
+ foreach (PredefinedSpeed speed in Utils.PredefinedSpeedItems)
+ yield return speed;
+ }
+
+ protected abstract void SetSpeedLimit(TransmissionAPI api, IEnumerable<TorrentItem> torrents, int speed);
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ int? speed = null;
+
+ // Get speed item, it can be either ITextItem or PredefinedSpeed.
+ Item modItem = modItems.First();
+ if (modItem is PredefinedSpeed) {
+ speed = ((PredefinedSpeed)modItem).Value;
+ } else {
+ string speed_str = ((ITextItem)modItem).Text;
+
+ try {
+ // Try to parse entered speed value.
+ speed = Utils.ParseSpeed(speed_str);
+
+ } catch (ArgumentException) {
+ Log<TransmissionPlugin>.Debug("Invalid speed string: {0}", speed_str);
+
+ // Show notification about invalid speed value with some hints on
+ // accepted formats.
+ string message = AddinManager.CurrentLocalizer.GetString(
+ "Can't recognize \"{0}\" as speed\nUse values like: 100k, 50 kb, 20m, 10 mib"
+ );
+ Services.Notifications.Notify("Transmission", string.Format(message, speed_str), "transmission");
+ }
+ }
+
+ // If speed is recognized successfully, set speed limit and update item.
+ if (speed.HasValue) {
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+ IEnumerable<TorrentItem> torrents = items.Cast<TorrentItem>();
+ SetSpeedLimit(api, torrents, speed.Value);
+ }
+
+ yield break;
+ }
+ }
+}
=== added file 'Transmission/src/TorrentDirectoryItem.cs'
--- Transmission/src/TorrentDirectoryItem.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentDirectoryItem.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,64 @@
+
+using System;
+using System.Collections.Generic;
+
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentDirectoryItem: Item, ITorrentEntry {
+
+ private TorrentItem _torrent;
+ private TorrentDirectoryItem _parent;
+ private string _name;
+ private IList<Item> _files;
+
+ public TorrentDirectoryItem(TorrentItem torrent, TorrentDirectoryItem parent, string name) {
+ _torrent = torrent;
+ _parent = parent;
+ _name = name;
+ _files = new List<Item>();
+ }
+
+ public TorrentItem Torrent {
+ get { return _torrent; }
+ }
+
+ public IEnumerable<TorrentFileItem> GetFiles() {
+ foreach (ITorrentEntry entry in _files)
+ foreach (TorrentFileItem file in entry.GetFiles())
+ yield return file;
+ }
+
+ public override string Name {
+ get { return _name; }
+ }
+
+ public override string Description {
+ get { return string.Empty; }
+ }
+
+ public override string Icon {
+ get { return "folder"; }
+ }
+
+ public IList<Item> Files {
+ get { return _files; }
+ }
+
+ public string Path {
+ get {
+ if (_parent != null) {
+ return _parent.Path + '/' + _name;
+ } else {
+ return _name;
+ }
+ }
+ }
+
+ public string Uri {
+ get { return "file://" + Path; }
+ }
+ }
+
+}
=== added file 'Transmission/src/TorrentFileItem.cs'
--- Transmission/src/TorrentFileItem.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentFileItem.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,97 @@
+
+using System;
+using System.Collections.Generic;
+
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentFileItem: Item, ITorrentEntry {
+
+ // Owner torrent.
+ private TorrentItem _torrent;
+ // Position of this file within torrents file list.
+ private int _index;
+
+ private TorrentDirectoryItem _parent;
+ private string _name;
+ private long _size, _downloaded;
+ private bool _wanted;
+ private TransmissionAPI.FilePriority _priority;
+
+ public TorrentFileItem(
+ TorrentItem torrent, int index, TorrentDirectoryItem parent,
+ string name, TransmissionAPI.TorrentFileInfo info
+ ) {
+ _torrent = torrent;
+ _index = index;
+
+ _parent = parent;
+ _name = name;
+ _size = info.Length;
+ _downloaded = info.BytesCompleted;
+
+ _wanted = info.Wanted;
+ _priority = info.Priority;
+ }
+
+ public TorrentItem Torrent {
+ get { return _torrent; }
+ }
+
+ public int Index {
+ get { return _index; }
+ }
+
+ public TransmissionAPI.FilePriority Priority {
+ get { return _priority; }
+ }
+
+ public IEnumerable<TorrentFileItem> GetFiles() {
+ yield return this;
+ }
+
+ public override string Name {
+ get { return _name; }
+ }
+
+ public override string Description {
+ get {
+ // I don't use special percentage format string, because it rounds
+ // value and I don't want to get "100%" until file is really downloaded.
+ // High precision isn't needed, because info is mostly out-of-date.
+
+ if (_downloaded == _size)
+ return string.Format("Complete, {0}", Utils.FormatSize(_size));
+
+ else if (_wanted)
+ return string.Format("{0} of {1} complete ({2:0}%)",
+ Utils.FormatSize(_downloaded), Utils.FormatSize(_size),
+ Math.Floor(100.0 * _downloaded / _size)
+ );
+
+ else if (_downloaded != 0)
+ return string.Format("Skipped, {0} of {1} complete ({2:0}%)",
+ Utils.FormatSize(_downloaded),Utils.FormatSize(_size),
+ Math.Floor(100.0 * _downloaded / _size)
+ );
+
+ else
+ return string.Format("Skipped, {0}", Utils.FormatSize(_size));
+ }
+ }
+
+ public override string Icon {
+ get { return "document"; }
+ }
+
+ public string Path {
+ get { return _parent.Path + '/' + _name; }
+ }
+
+ public string Uri {
+ get { return "file://" + Path; }
+ }
+ }
+
+}
=== added file 'Transmission/src/TorrentFileSetPriorityAction.cs'
--- Transmission/src/TorrentFileSetPriorityAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentFileSetPriorityAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,108 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ public class PriorityItem: Item {
+ private TransmissionAPI.FilePriority _value;
+ private string _name;
+ private string _icon;
+
+ public PriorityItem(TransmissionAPI.FilePriority value, string name, string icon) {
+ _value = value;
+ _name = name;
+ _icon = icon;
+ }
+
+ public override string Name {
+ get { return _name; }
+ }
+
+ public override string Description {
+ get { return ""; }
+ }
+
+ public override string Icon {
+ get { return _icon; }
+ }
+
+ public TransmissionAPI.FilePriority Value {
+ get { return _value; }
+ }
+ }
+
+ public class TorrentFileSetPriorityAction: Act {
+
+ public TorrentFileSetPriorityAction() {
+ }
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Set priority"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Set download priority"); }
+ }
+
+ public override string Icon {
+ get { return "object-flip-vertical"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get {
+ yield return typeof (ITorrentEntry);
+ }
+ }
+
+ public override IEnumerable<Type> SupportedModifierItemTypes {
+ get {
+ yield return typeof (PriorityItem);
+ }
+ }
+
+ public override bool ModifierItemsOptional {
+ get { return false; }
+ }
+
+ public override IEnumerable<Item> DynamicModifierItemsForItem(Item item) {
+ yield return new PriorityItem(TransmissionAPI.FilePriority.Low, "Low", "down");
+ yield return new PriorityItem(TransmissionAPI.FilePriority.Normal, "Normal", "forward");
+ yield return new PriorityItem(TransmissionAPI.FilePriority.High, "High", "up");
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ TransmissionAPI.FilePriority priority = (modItems.First() as PriorityItem).Value;
+
+ TransmissionAPI.FileOperation operation = new TransmissionAPI.FileOperation(null, priority);
+
+ // Group torrent entries by torrent.
+ var files_by_torrent = items
+ .Cast<ITorrentEntry>()
+ .GroupBy(
+ item => item.Torrent,
+ (torrent, entries) => new {
+ Torrent = torrent,
+ Files = entries.SelectMany(entry => entry.GetFiles())
+ }
+ );
+
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ // Expand entries for each torrent into set of torrent file entries.
+ // Perform action for each torrent separately.
+ foreach (var group in files_by_torrent) {
+ var operations = group.Files.ToDictionary(f => f.Index, f => operation);
+ api.SetTorrent(group.Torrent.HashString, null, null, null, null, null, operations);
+ }
+
+ yield break;
+ }
+ }
+}
=== added file 'Transmission/src/TorrentItem.cs'
--- Transmission/src/TorrentItem.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentItem.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,72 @@
+
+using System;
+using System.Collections.Generic;
+
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentItem: Item {
+
+ private string _name;
+ private string _comment;
+ private string _hash_string;
+ private long _size;
+ private TransmissionAPI.TorrentStatus _status;
+ private int _download_speed_limit, _upload_speed_limit;
+ private TorrentDirectoryItem _root;
+
+ public TorrentItem(TransmissionAPI.TorrentInfo info) {
+ _hash_string = info.HashString;
+ _name = info.Name;
+ _comment = info.Comment;
+ _status = info.Status;
+ _size = info.TotalSize;
+ _download_speed_limit = info.DownloadLimit;
+ _upload_speed_limit = info.UploadLimit;
+ _root = new TorrentDirectoryItem(this, null, info.DownloadDir);
+ }
+
+ public override string Name {
+ get { return _name; }
+ }
+
+ public override string Description {
+ get {
+ string status_text = "";
+ switch (_status) {
+ case TransmissionAPI.TorrentStatus.CheckWait: status_text = "Waiting for check"; break;
+ case TransmissionAPI.TorrentStatus.Check: status_text = "Checking"; break;
+ case TransmissionAPI.TorrentStatus.Download: status_text = "Downloading"; break;
+ case TransmissionAPI.TorrentStatus.Seed: status_text = "Seeding"; break;
+ case TransmissionAPI.TorrentStatus.Stopped: status_text = "Stopped"; break;
+ }
+
+ return string.Format("{0}, {1}", Utils.FormatSize(_size), status_text);
+ }
+ }
+
+ public override string Icon {
+ get { return "transmission"; }
+ }
+
+ public string HashString {
+ get { return _hash_string; }
+ }
+
+ public TorrentDirectoryItem Root {
+ get { return _root; }
+ }
+
+ public int DownloadSpeedLimit {
+ get { return _download_speed_limit; }
+ set { _download_speed_limit = value; }
+ }
+
+ public int UploadSpeedLimit {
+ get { return _upload_speed_limit; }
+ set { _upload_speed_limit = value; }
+ }
+ }
+
+}
=== added file 'Transmission/src/TorrentItemSource.cs'
--- Transmission/src/TorrentItemSource.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentItemSource.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,130 @@
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+using Do.Platform;
+using Do.Platform.Linux;
+
+namespace Transmission {
+
+ public class TorrentItemSource: ItemSource, IConfigurable {
+
+ private List<Item> _torrents = new List<Item>();
+
+ public TorrentItemSource() {
+ }
+
+ public override string Name {
+ get { return "Torrents"; }
+ }
+
+ public override string Description {
+ get { return "Transmission torrent client downloads"; }
+ }
+
+ public override string Icon {
+ get { return "transmission"; }
+ }
+
+ public override void UpdateItems () {
+ Log<TorrentItemSource>.Debug("Updating torrents list");
+
+ // Clear current torrents list.
+ _torrents.Clear();
+
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ foreach (TransmissionAPI.TorrentInfo t in api.GetAllTorrents()) {
+ Log<TorrentItemSource>.Debug("Torrent: {0}", t.Name);
+
+ TorrentItem torrent = new TorrentItem(t);
+
+ // Transmission returns files as flat list with full names, this map
+ // is used to organize files into hierarchy.
+ // It maps directory path to directory item.
+ Dictionary<string, TorrentDirectoryItem> dirs = new Dictionary<string, TorrentDirectoryItem>();
+ dirs.Add("", torrent.Root);
+
+ int index = 0; // File index within list.
+ foreach (TransmissionAPI.TorrentFileInfo f in t.files) {
+ // Split path and name.
+ int sep_pos = f.Name.LastIndexOf('/');
+
+ string name = f.Name.Substring(sep_pos+1);
+ string path = f.Name.Substring(0, sep_pos == -1 ? 0 : sep_pos);
+ Log<TorrentItemSource>.Debug("File {0} in dir {1}", name, path);
+ TorrentDirectoryItem dir = FindOrCreateDirectory(path, dirs);
+
+ dir.Files.Add(new TorrentFileItem(torrent, index, dir, name, f));
+
+ ++index;
+ }
+
+ _torrents.Add(torrent);
+ }
+ }
+
+ private TorrentDirectoryItem FindOrCreateDirectory(
+ string path,
+ Dictionary<string, TorrentDirectoryItem> dirs
+ ) {
+ TorrentDirectoryItem dir;
+ dirs.TryGetValue(path, out dir);
+
+ if (dir != null) {
+ // Found already added directory.
+ return dir;
+
+ } else {
+ // Directory doesn't exist, find or add parent one, then add this one.
+ int sep_pos = path.LastIndexOf('/');
+
+ string parent_path = path.Substring(0, sep_pos == -1 ? 0 : sep_pos);
+ TorrentDirectoryItem parent = FindOrCreateDirectory(parent_path, dirs);
+
+ string name = path.Substring(sep_pos+1);
+ dir = new TorrentDirectoryItem(parent.Torrent, parent, name);
+
+ parent.Files.Add(dir);
+ dirs.Add(path, dir);
+
+ return dir;
+ }
+ }
+
+ public override IEnumerable<Item> Items {
+ get { return _torrents; }
+ }
+
+ public override IEnumerable<Item> ChildrenOfItem(Item item) {
+ if (item is TorrentItem) {
+ foreach (Item entry in ((TorrentItem)item).Root.Files)
+ yield return entry;
+
+ } else if (item is TorrentDirectoryItem) {
+ foreach (Item entry in ((TorrentDirectoryItem)item).Files)
+ yield return entry;
+
+ } else {
+ yield break;
+
+ }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get {
+ yield return typeof(TorrentItem);
+ yield return typeof(TorrentDirectoryItem);
+ yield return typeof(TorrentFileItem);
+ }
+ }
+
+ public Gtk.Bin GetConfiguration() {
+ return new TransmissionConfig();
+ }
+
+ }
+}
=== added file 'Transmission/src/TorrentLimitDownloadSpeedAction.cs'
--- Transmission/src/TorrentLimitDownloadSpeedAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentLimitDownloadSpeedAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,47 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentLimitDownloadSpeedAction: TorrentAbstractLimitSpeedAction {
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString("Limit download speed"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString("Set download speed limit"); }
+ }
+
+ public override string Icon {
+ get { return "top"; }
+ }
+
+ protected override PredefinedSpeed GetCurrentSpeedItem(TorrentItem torrent) {
+ int currentSpeed = torrent.DownloadSpeedLimit;
+ return new PredefinedSpeed(
+ currentSpeed,
+ string.Format("Saved: {0}", Utils.FormatSpeed(currentSpeed)),
+ "Use limit from torrent settings"
+ );
+ }
+
+ protected override void SetSpeedLimit(TransmissionAPI api, IEnumerable<TorrentItem> torrents, int speed) {
+ bool limit_speed = (speed != 0);
+ int? limit = (speed == 0 ? (int?)null : speed);
+ api.SetTorrents(torrents.Select(t => t.HashString), null, limit_speed, limit, null, null);
+
+ foreach (TorrentItem torrent in torrents)
+ torrent.DownloadSpeedLimit = speed;
+ }
+
+ }
+}
=== added file 'Transmission/src/TorrentLimitUploadSpeedAction.cs'
--- Transmission/src/TorrentLimitUploadSpeedAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentLimitUploadSpeedAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,47 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentLimitUploadSpeedAction: TorrentAbstractLimitSpeedAction {
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Limit upload speed"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Set upload speed limit"); }
+ }
+
+ public override string Icon {
+ get { return "top"; }
+ }
+
+ protected override PredefinedSpeed GetCurrentSpeedItem(TorrentItem torrent) {
+ int currentSpeed = torrent.UploadSpeedLimit;
+ return new PredefinedSpeed(
+ currentSpeed,
+ string.Format("Saved: {0}", Utils.FormatSpeed(currentSpeed)),
+ "Use limit from torrent settings"
+ );
+ }
+
+ protected override void SetSpeedLimit(TransmissionAPI api, IEnumerable<TorrentItem> torrents, int speed) {
+ bool limit_speed = (speed != 0);
+ int? limit = (speed == 0 ? (int?)null : speed);
+ api.SetTorrents(torrents.Select(t => t.HashString), null, null, null, limit_speed, limit);
+
+ foreach (TorrentItem torrent in torrents)
+ torrent.UploadSpeedLimit = speed;
+ }
+
+ }
+}
=== added file 'Transmission/src/TorrentMarkForDownloadAction.cs'
--- Transmission/src/TorrentMarkForDownloadAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentMarkForDownloadAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,62 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentMarkForDownloadAction: Act {
+
+ public TorrentMarkForDownloadAction() {
+ }
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Mark for download"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Mark file as needed to be downloaded"); }
+ }
+
+ public override string Icon {
+ get { return "add"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get {
+ yield return typeof (ITorrentEntry);
+ }
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ // Operation is common for all files.
+ TransmissionAPI.FileOperation operation = new TransmissionAPI.FileOperation(true, null);
+
+ // Group selected items by owner torrent.
+ var files_by_torrent = items
+ .Cast<ITorrentEntry>()
+ .GroupBy(
+ item => item.Torrent,
+ (torrent, entries) => new {
+ Torrent = torrent,
+ Files = entries.SelectMany(entry => entry.GetFiles())
+ }
+ );
+
+ // Perform action for each torrent separately.
+ foreach (var group in files_by_torrent) {
+ var operations = group.Files.ToDictionary(f => f.Index, f => operation);
+ api.SetTorrent(group.Torrent.HashString, null, null, null, null, null, operations);
+ }
+
+ yield break;
+ }
+ }
+}
=== added file 'Transmission/src/TorrentOperateAction.cs'
--- Transmission/src/TorrentOperateAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentOperateAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+using Do.Platform;
+
+namespace Transmission {
+
+ public class TorrentOperateAction: Act {
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Operate on files"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Operate on downloaded file"); }
+ }
+
+ public override string Icon {
+ get { return "file"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get { yield return typeof (ITorrentEntry); }
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ foreach (Item item in items) {
+ ITorrentEntry entry = (ITorrentEntry)item;
+ yield return Services.UniverseFactory.NewFileItem(entry.Path) as Item;
+ }
+ }
+ }
+}
=== added file 'Transmission/src/TorrentStartAction.cs'
--- Transmission/src/TorrentStartAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentStartAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,42 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentStartAction: Act {
+
+ public TorrentStartAction() {
+ }
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Start"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Start downloading torrent"); }
+ }
+
+ public override string Icon {
+ get { return "gtk-media-play"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get { yield return typeof (TorrentItem); }
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ var hashes = items.Cast<TorrentItem>().Select(t => t.HashString);
+ api.StartTorrents(hashes);
+
+ return null;
+ }
+ }
+}
=== added file 'Transmission/src/TorrentStopAction.cs'
--- Transmission/src/TorrentStopAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentStopAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,42 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentStopAction: Act {
+
+ public TorrentStopAction() {
+ }
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Stop"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Stop downloading torrent"); }
+ }
+
+ public override string Icon {
+ get { return "gtk-media-pause"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get { yield return typeof (TorrentItem); }
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ var hashes = items.Cast<TorrentItem>().Select(t => t.HashString);
+ api.StopTorrents(hashes);
+
+ return null;
+ }
+ }
+}
=== added file 'Transmission/src/TorrentUnmarkForDownloadAction.cs'
--- Transmission/src/TorrentUnmarkForDownloadAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentUnmarkForDownloadAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,62 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentUnmarkForDownloadAction: Act {
+
+ public TorrentUnmarkForDownloadAction() {
+ }
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Unmark for download"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Unmark file as needed to be downloaded"); }
+ }
+
+ public override string Icon {
+ get { return "remove"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get {
+ yield return typeof (ITorrentEntry);
+ }
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ // Operation is common for all files.
+ TransmissionAPI.FileOperation operation = new TransmissionAPI.FileOperation(false, null);
+
+ // Group selected items by owner torrent.
+ var files_by_torrent = items
+ .Cast<ITorrentEntry>()
+ .GroupBy(
+ item => item.Torrent,
+ (torrent, entries) => new {
+ Torrent = torrent,
+ Files = entries.SelectMany(entry => entry.GetFiles())
+ }
+ );
+
+ // Perform action for each torrent separately.
+ foreach (var group in files_by_torrent) {
+ var operations = group.Files.ToDictionary(f => f.Index, f => operation);
+ api.SetTorrent(group.Torrent.HashString, null, null, null, null, null, operations);
+ }
+
+ yield break;
+ }
+ }
+}
=== added file 'Transmission/src/TorrentVerifyAction.cs'
--- Transmission/src/TorrentVerifyAction.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentVerifyAction.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,42 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+
+namespace Transmission {
+
+ public class TorrentVerifyAction: Act {
+
+ public TorrentVerifyAction() {
+ }
+
+ public override string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Verify"); }
+ }
+
+ public override string Description {
+ get { return AddinManager.CurrentLocalizer.GetString ("Verify torrent"); }
+ }
+
+ public override string Icon {
+ get { return "dialog-question"; }
+ }
+
+ public override IEnumerable<Type> SupportedItemTypes {
+ get { yield return typeof (TorrentItem); }
+ }
+
+ public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+ TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+ var hashes = items.Cast<TorrentItem>().Select(t => t.HashString);
+ api.VerifyTorrents(hashes);
+
+ return null;
+ }
+ }
+}
=== added file 'Transmission/src/TransmissionAPI.cs'
--- Transmission/src/TransmissionAPI.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TransmissionAPI.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,575 @@
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Net;
+using System.Text;
+using Jayrock.Json;
+
+namespace Transmission {
+
+ /// <summary>
+ /// Transmission API client.
+ /// Compatible with RPC version 5 to 10 (release version 1.60 to 2.10).
+ /// </summary>
+ public class TransmissionAPI {
+
+ /// <summary>
+ /// File loading priority.
+ /// </summary>
+ public enum FilePriority {
+ Low, Normal, High
+ };
+
+ /// <summary>
+ /// Operation on individual file from torrent.
+ /// </summary>
+ /// <remarks>
+ /// All fields are nullable, <c>null</c> means "don't change current value".
+ /// </remarks>
+ public struct FileOperation {
+ public FileOperation(bool? download, FilePriority? priority) {
+ this.download = download;
+ this.priority = priority;
+ }
+
+ /// <summary>Whether it is needed to download this file</summary>
+ public bool? download;
+
+ /// <summary>Priority relative to other files of the same torrent</summary>
+ public FilePriority? priority;
+ };
+
+ /// <summary>
+ /// Error communicating to Transmission.
+ /// </summary>
+ public class TransmissionAPIError: Exception {
+ public TransmissionAPIError(string message): base(message) {}
+ public TransmissionAPIError(string message, Exception reason): base(message, reason) {}
+ };
+
+ /// <summary>
+ /// Error returned by Transmission.
+ /// </summary>
+ public class TransmissionError: Exception {
+ public TransmissionError(string message): base(message) {
+ }
+ };
+
+ public const int DEFAULT_PORT = 9091;
+ public const string DEFAULT_PATH = "/transmission/rpc";
+ public const string SESSION_HEADER = "X-Transmission-Session-Id";
+
+ private string _url, _username, _password;
+ private string _session_id = "";
+
+ private delegate void ResultReader(JsonReader json);
+
+ /// <summary>Response handler which does nothing</summary>
+ /// <remarks>Prefer using <see cref="Call(string, IDictionary<string, object>)"/> instead</remarks>
+ private void NullHandler(JsonReader json) {}
+
+ /// <summary>
+ /// Create API client.
+ /// </summary>
+ /// <param name="url">Transmission API-RPC URL</param>
+ /// <param name="username">Username for authentication</param>
+ /// <param name="password">Password for authentication</param>
+ /// <remarks>Pass <c>null</c> for both <paramref="username"/> and <paramref="username"/> is
+ /// authentication isn't needed.</remarks>
+ public TransmissionAPI(string url, string username, string password) {
+ _url = url;
+ _username = username;
+ _password = password;
+ }
+
+ /// <summary>Compose request JSON string</summary>
+ protected string ComposeRequest(string method, IDictionary<string, object> arguments) {
+ StringWriter sw = new StringWriter();
+
+ using (JsonWriter json = new JsonTextWriter(sw)) {
+ json.WriteStartObject();
+
+ json.WriteMember("method");
+ json.WriteString(method);
+
+ json.WriteMember("arguments");
+ json.WriteStartObject();
+ foreach (KeyValuePair<string, object> pair in arguments) {
+ string name = pair.Key;
+ object value = pair.Value;
+
+ json.WriteMember(name);
+ Jayrock.Json.Conversion.JsonConvert.Export(value, json);
+ }
+ json.WriteEndObject();
+
+ json.WriteEndObject();
+ }
+
+ return sw.ToString();
+ }
+
+ /// <summary></summary>
+ /// <param name="payload">HTTP POST request content</param>
+ /// <returns>HTTP response content</returns>
+ /// <exception cref="System.Net.WebException"/>
+ protected string PerformRequest(byte[] payload) {
+ // Prepare request.
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);
+ request.Method = "POST";
+ request.ContentType = "application/x-www-form-urlencoded";
+ request.Headers[SESSION_HEADER] = _session_id;
+ request.ContentLength = payload.Length;
+
+ // Authenticate if credentials are given.
+ if (_username != null && _password != null) {
+ string auth = Convert.ToBase64String(Encoding.Default.GetBytes(_username + ":" + _password));
+ request.Headers["Authorization"] = "Basic " + auth;
+ }
+
+ // Perform request.
+ using (Stream stream = request.GetRequestStream()) {
+ stream.Write(payload, 0, payload.Length);
+ }
+
+ // Get response.
+ HttpWebResponse response = (HttpWebResponse)request.GetResponse();
+ return new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8).ReadToEnd();
+ }
+
+ /// <summary>
+ /// Call Transmission API method.
+ /// </summary>
+ /// <param name="method">API method name</param>
+ /// <param name="arguments">Arguments passed to API method</param>
+ /// <param name="handler">Function called with value returned by method</param>
+ /// <exception cref="TransmissionError" />
+ /// <exception cref="TransmissionAPIError" />
+ private void Call(string method, IDictionary<string, object> arguments, ResultReader handler) {
+ // Compose request and encode it to UTF-8.
+ string req = ComposeRequest(method, arguments);
+ byte[] reqb = System.Text.Encoding.UTF8.GetBytes(req);
+
+ try {
+
+ string resp = null;
+ try {
+ resp = PerformRequest(reqb);
+
+ } catch (System.Net.WebException err) {
+ HttpWebResponse response = (HttpWebResponse)err.Response;
+
+ // Transmission 1.53 and 1.6 introduced X-Transmission-Session-Id header
+ // in order to protect from CSRF attacks. If you make request without
+ // this header (or your session expire) you'll get 409 response with
+ // this header set. Just copy header to your request and try again.
+ if (
+ err.Status == WebExceptionStatus.ProtocolError &&
+ response.StatusCode == HttpStatusCode.Conflict
+ ) {
+ _session_id = response.Headers[SESSION_HEADER];
+ resp = PerformRequest(reqb);
+
+ } else {
+ throw;
+ }
+ }
+
+ JsonReader json_reader = new JsonTextReader(new StringReader(resp));
+
+ json_reader.ReadToken(JsonTokenClass.Object);
+ while (json_reader.TokenClass == JsonTokenClass.Member) {
+ switch (json_reader.ReadMember()) {
+ case "result":
+ string result = json_reader.ReadString();
+ if (result != "success")
+ throw new TransmissionError(result);
+ break;
+ case "tag":
+ json_reader.ReadNumber();
+ break;
+ case "arguments":
+ json_reader.ReadToken(JsonTokenClass.Object);
+ handler(json_reader);
+ json_reader.ReadToken(JsonTokenClass.EndObject);
+ break;
+ default:
+ json_reader.Skip();
+ break;
+ }
+ }
+ json_reader.ReadToken(JsonTokenClass.EndObject);
+ json_reader.ReadToken(JsonTokenClass.EOF);
+
+ } catch (System.Net.WebException err) {
+ throw new TransmissionAPIError("Cannot access Transmission RPC service", err);
+ }
+ }
+
+ /// <summary>Call API method and ignore return value</summary>
+ /// <remarks>This is equivalent to <c>Call(method, arguments, NullHandler)</c>.</remarks>
+ private void Call(string method, IDictionary<string, object> arguments) {
+ Call(method, arguments, NullHandler);
+ }
+
+ /// <summary>
+ /// Start torrents.
+ /// </summary>
+ /// <param name="torrent_hashes">Sequence of torrent's hashes</param>
+ /// <exception cref="TransmissionAPIError" />
+ /// <exception cref="TransmissionError" />
+ public void StartTorrents(IEnumerable<string> torrent_hashes) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+ arguments.Add("ids", torrent_hashes);
+ Call("torrent-start", arguments);
+ }
+
+ /// <summary>
+ /// Start all torrents.
+ /// </summary>
+ /// <exception cref="TransmissionAPIError" />
+ /// <exception cref="TransmissionError" />
+ public void StartAllTorrents() {
+ Call("torrent-start", new Dictionary<string, object>());
+ }
+
+ /// <summary>
+ /// Stop torrents.
+ /// </summary>
+ /// <param name="torrent_hashes">Sequence of torrent's hashes</param>
+ /// <exception cref="TransmissionAPIError" />
+ /// <exception cref="TransmissionError" />
+ public void StopTorrents(IEnumerable<string> torrent_hashes) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+ arguments.Add("ids", torrent_hashes);
+ Call("torrent-stop", arguments);
+ }
+
+ /// <summary>
+ /// Stop all torrents.
+ /// </summary>
+ /// <exception cref="TransmissionAPIError" />
+ /// <exception cref="TransmissionError" />
+ public void StopAllTorrents() {
+ Call("torrent-stop", new Dictionary<string, object>());
+ }
+
+ /// <summary>
+ /// Start torrents verification.
+ /// </summary>
+ /// <param name="torrent_hashes">Sequence of torrent's hashes to verify</param>
+ /// <exception cref="TransmissionAPIError" />
+ /// <exception cref="TransmissionError" />
+ public void VerifyTorrents(IEnumerable<string> torrent_hashes) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+ arguments.Add("ids", torrent_hashes);
+ Call("torrent-verify", arguments);
+ }
+
+ /// <summary>
+ /// Start torrents verification for all torrents.
+ /// </summary>
+ /// <exception cref="TransmissionAPIError" />
+ /// <exception cref="TransmissionError" />
+ public void VerifyAllTorrents() {
+ Call("torrent-verify", new Dictionary<string, object>());
+ }
+
+ /// <summary>
+ /// Set torrents' properties.
+ ///
+ /// Not all torrent properties can be changed using this method, because some of them
+ /// are meaningless for torrent group, e.g. file priorities. Use <c>SetTorrent</c> method
+ /// to set such properties.
+ /// </summary>
+ /// <param name="torrent_hashes">Sequence of torrents hashed to modify</param>
+ /// <param name="peer_limit">Maximum number of used peers, if it is <c>null</c> then value won't be changed.</param>
+ /// <param name="limit_download">
+ /// Downloading speed limit switch, if it is <c>true</c>, downloading speed will be limited,
+ /// if it is <c>false</c>, downloading speed isn't limited, if it is <c>null</c>, limit switch won't be changed.
+ /// A <see cref="System.Nullable"/>
+ /// </param>
+ /// <param name="download_speed_limit">
+ /// A <see cref="System.Nullable"/>
+ /// </param>
+ /// <param name="limit_upload">
+ /// A <see cref="System.Nullable"/>
+ /// </param>
+ /// <param name="upload_speed_limit">
+ /// A <see cref="System.Nullable"/>
+ /// </param>
+ public void SetTorrents(IEnumerable<string> torrent_hashes, int? peer_limit, bool? limit_download, int? download_speed_limit, bool? limit_upload, int? upload_speed_limit) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+
+ arguments.Add("ids", torrent_hashes);
+
+ if (peer_limit != null)
+ arguments.Add("peer-limit", peer_limit);
+
+ if (limit_download.HasValue) {
+ if (limit_download.Value) {
+ arguments.Add("downloadLimited", true);
+ if (download_speed_limit.HasValue)
+ arguments.Add("downloadLimit", download_speed_limit.Value);
+ } else {
+ arguments.Add("downloadLimited", false);
+ }
+ }
+
+ if (limit_upload.HasValue) {
+ if (limit_upload.Value) {
+ arguments.Add("uploadLimited", true);
+ if (upload_speed_limit.HasValue)
+ arguments.Add("uploadLimit", upload_speed_limit.Value);
+ } else {
+ arguments.Add("uploadLimited", false);
+ }
+ }
+
+ Call("torrent-set", arguments);
+ }
+
+ public void SetTorrent(string torrent_hash, int? peer_limit, bool? limit_download, int? download_speed_limit, bool? limit_upload, int? upload_speed_limit, IDictionary<int, FileOperation> files) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+
+ arguments.Add("ids", new string[] { torrent_hash });
+
+ if (peer_limit != null)
+ arguments.Add("peer-limit", peer_limit);
+
+ if (limit_download.HasValue) {
+ if (limit_download.Value) {
+ arguments.Add("downloadLimited", true);
+ if (download_speed_limit.HasValue)
+ arguments.Add("downloadLimit", download_speed_limit.Value);
+ } else {
+ arguments.Add("downloadLimited", false);
+ }
+ }
+
+ if (limit_upload.HasValue) {
+ if (limit_upload.Value) {
+ arguments.Add("uploadLimited", true);
+ if (upload_speed_limit.HasValue)
+ arguments.Add("uploadLimit", upload_speed_limit.Value);
+ } else {
+ arguments.Add("uploadLimited", false);
+ }
+ }
+
+ List<int> wanted_files = new List<int>();
+ List<int> unwanted_files = new List<int>();
+ List<int> low_priority_files = new List<int>();
+ List<int> normal_priority_files = new List<int>();
+ List<int> high_priority_files = new List<int>();
+ foreach (KeyValuePair<int, FileOperation> op in files) {
+ int index = op.Key;
+ FileOperation file = op.Value;
+
+ if (file.download.HasValue) {
+ if (file.download.Value) wanted_files.Add(index);
+ else unwanted_files.Add(index);
+ }
+
+ if (file.priority.HasValue) {
+ switch (file.priority.Value) {
+ case FilePriority.Low: low_priority_files.Add(index); break;
+ case FilePriority.Normal: normal_priority_files.Add(index); break;
+ case FilePriority.High: high_priority_files.Add(index); break;
+ }
+ }
+ }
+ if (wanted_files.Count > 0) arguments.Add("files-wanted", wanted_files);
+ if (unwanted_files.Count > 0) arguments.Add("files-unwanted", unwanted_files);
+ if (low_priority_files.Count > 0) arguments.Add("priority-low", low_priority_files);
+ if (normal_priority_files.Count > 0) arguments.Add("priority-normal", normal_priority_files);
+ if (high_priority_files.Count > 0) arguments.Add("priority-high", high_priority_files);
+
+ Call("torrent-set", arguments);
+ }
+
+ /// <summary>
+ /// Torrent status.
+ /// </summary>
+ public enum TorrentStatus {
+ CheckWait = 1, Check = 2, Download = 4, Seed = 8, Stopped = 16
+ };
+
+ public class TorrentInfo {
+ public int Id; // Torrent's unique ID within Transmission.
+ public string Comment;
+ public string HashString;
+ public string Name;
+ public string DownloadDir;
+ public IList<TorrentFileInfo> files;
+ public TorrentStatus Status;
+ public long TotalSize;
+
+ public int DownloadLimit;
+ public bool DownloadLimited;
+ public int UploadLimit;
+ public bool UploadLimited;
+ };
+
+ public class TorrentFileInfo {
+ public string Name;
+ public long Length;
+ public long BytesCompleted;
+ public bool Wanted;
+ public FilePriority Priority;
+ };
+
+ /// <summary>Get information about all torrents.</summary>
+ /// <remarks>This is equivalent to <c>GetTorrents(null)</c>.</remarks>
+ public IEnumerable<TorrentInfo> GetAllTorrents() {
+ return GetTorrents(null);
+ }
+
+ public IEnumerable<TorrentInfo> GetTorrents(IEnumerable<string> torrent_hashes) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+ if (torrent_hashes != null) arguments.Add("ids", torrent_hashes);
+ arguments.Add("fields", new string[] {"comment", "downloadDir", "files", "hashString", "id", "name", "priorities", "status", "totalSize", "wanted", "downloadLimited", "downloadLimit", "uploadLimited", "uploadLimit"});
+
+ List<TorrentInfo> torrents = new List<TorrentInfo>();
+
+ Jayrock.Json.Conversion.ImportContext jsonctx = new Jayrock.Json.Conversion.ImportContext();
+ jsonctx.Register(new ListImporter<int>());
+ jsonctx.Register(new ListImporter<TorrentFileInfo>());
+
+ Call("torrent-get", arguments, delegate(JsonReader json) {
+ while (json.TokenClass == JsonTokenClass.Member) {
+ switch (json.ReadMember()) {
+ case "torrents":
+ json.ReadToken(JsonTokenClass.Array);
+ while (json.TokenClass != JsonTokenClass.EndArray) {
+
+ json.ReadToken(JsonTokenClass.Object);
+
+ TorrentInfo torrent = new TorrentInfo();
+ IList<TorrentFileInfo> files = null;
+ IList<int> wanted = null;
+ IList<int> priorities = null;
+
+ while (json.TokenClass == JsonTokenClass.Member) {
+ switch (json.ReadMember()) {
+ case "comment": torrent.Comment = json.ReadString(); break;
+ case "hashString": torrent.HashString = json.ReadString(); break;
+ case "name": torrent.Name = json.ReadString(); break;
+ case "id": torrent.Id = json.ReadNumber().ToInt32(); break;
+ case "downloadDir": torrent.DownloadDir = json.ReadString(); break;
+ case "status": torrent.Status = (TorrentStatus)json.ReadNumber().ToInt32(); break;
+ case "totalSize": torrent.TotalSize = json.ReadNumber().ToInt64(); break;
+ case "wanted": wanted = (IList<int>)jsonctx.Import(typeof(List<int>), json); break;
+ case "priorities": priorities = (IList<int>)jsonctx.Import(typeof(List<int>), json); break;
+ case "files": files = jsonctx.Import(typeof(List<TorrentFileInfo>), json) as IList<TorrentFileInfo>; break;
+ case "downloadLimited": torrent.DownloadLimited = json.ReadBoolean(); break;
+ case "downloadLimit": torrent.DownloadLimit = json.ReadNumber().ToInt32(); break;
+ case "uploadLimited": torrent.UploadLimited = json.ReadBoolean(); break;
+ case "uploadLimit": torrent.UploadLimit = json.ReadNumber().ToInt32(); break;
+ }
+ }
+
+ for (int i = 0; i < files.Count; ++i) {
+ files[i].Wanted = wanted[i] == 1;
+
+ int prio = priorities[i];
+ if (prio < -1 || prio > 1)
+ throw new TransmissionAPIError(string.Format("Invalid priority value: {0}", prio));
+ files[i].Priority = (FilePriority)(prio+1);
+ }
+ torrent.files = files;
+ torrents.Add(torrent);
+
+ json.ReadToken(JsonTokenClass.EndObject);
+ }
+ json.ReadToken(JsonTokenClass.EndArray);
+ break;
+ default:
+ json.Skip();
+ break;
+ }
+ }
+ });
+
+ return torrents;
+ }
+
+ // Transmission RPC allows to use .torrent file content instead of it's filename, but
+ // this isn't supported.
+ public void AddTorrent(string filename, string download_to, bool paused, int? peer_limit) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+ arguments.Add("filename", filename);
+ arguments.Add("download-dir", download_to);
+ arguments.Add("paused", paused);
+ if (peer_limit.HasValue) arguments.Add("peer-limit", peer_limit.Value);
+ Call("torrent-add", arguments);
+ }
+
+ /// <summary>
+ /// Remove torrents.
+ /// </summary>
+ /// <param name="hashes">Sequence of torrent hashes to remove.</param>
+ /// <param name="delete_files">Whether downloaded files should be deleted or not.</param>
+ public void RemoveTorrent(IEnumerable<string> hashes, bool delete_files) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+ arguments.Add("ids", hashes);
+ arguments.Add("delete-load-data", delete_files);
+ Call("torrent-remove", arguments);
+ }
+
+ /// <summary>
+ /// Set session parameters.
+ /// </summary>
+ /// <param name="download_dir">
+ /// Path to directory to download torrent contents to.
+ /// Pass <c>null</c> to keep old value.
+ /// </param>
+ /// <param name="peer_limit">
+ /// Global limit on number of connected peers. Pass <c>null</c> to keep old value.
+ /// </param>
+ /// <param name="limit_download">
+ /// Global limit on number of connected peers. Pass <c>null</c> to keep old value.
+ /// </param>
+ public void SetSession(
+ string download_dir, int? peer_limit,
+ bool? limit_download, int? download_speed_limit, bool? limit_upload, int? upload_speed_limit
+ ) {
+ Dictionary<string, object> arguments = new Dictionary<string, object>();
+
+ if (download_dir != null)
+ arguments.Add("download-dir", download_dir);
+
+ if (peer_limit.HasValue)
+ arguments.Add("peer-limit", peer_limit.Value);
+
+ if (limit_download.HasValue) {
+ if (limit_download.Value) {
+ arguments.Add("downloadLimited", true);
+ if (download_speed_limit.HasValue)
+ arguments.Add("downloadLimit", download_speed_limit.Value);
+ } else {
+ arguments.Add("downloadLimited", false);
+ }
+ }
+
+ if (limit_upload.HasValue) {
+ if (limit_upload.Value) {
+ arguments.Add("uploadLimited", true);
+ if (upload_speed_limit.HasValue)
+ arguments.Add("uploadLimit", upload_speed_limit.Value);
+ } else {
+ arguments.Add("uploadLimited", false);
+ }
+ }
+
+ Call("session-set", arguments);
+ }
+
+ public void GetSession() {
+ Call("session-get", new Dictionary<string, object>());
+ }
+
+ }
+}
=== added file 'Transmission/src/TransmissionPlugin.cs'
--- Transmission/src/TransmissionPlugin.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/TransmissionPlugin.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+
+using Do.Platform;
+
+namespace Transmission {
+
+ public class ConnectionParameters {
+ public ConnectionParameters(string url, string username, string password) {
+ this.url = url;
+ this.username = username;
+ this.password = password;
+ }
+
+ public string url;
+ public string username;
+ public string password;
+ };
+
+ public class TransmissionPlugin {
+ private static TransmissionAPI transmission;
+
+ public static TransmissionAPI getTransmission() {
+ if (transmission == null) {
+ ConnectionParameters p = getTransmissionConnectionParameters();
+ Log<TransmissionPlugin>.Info("Using Transmission on {0}", p.url);
+ Log<TransmissionPlugin>.Debug("Using name, password: {0}:{1}", p.username, p.password);
+ transmission = new TransmissionAPI(p.url, p.username, p.password);
+ }
+ return transmission;
+ }
+
+ public static void ResetConnection() {
+ transmission = null;
+ }
+
+ public static ConnectionParameters getTransmissionConnectionParameters() {
+ string host = TransmissionConfig.Address;
+ int port = TransmissionConfig.Port;
+ string username = TransmissionConfig.UserName;
+ string password = TransmissionConfig.Password;
+
+ string url = string.Format("http://{0}:{1}/transmission/rpc", host, port);
+ return new ConnectionParameters(url, username, password);
+ }
+
+ };
+
+}
+
=== added file 'Transmission/src/Utils.cs'
--- Transmission/src/Utils.cs 1970-01-01 00:00:00 +0000
+++ Transmission/src/Utils.cs 2010-07-26 16:47:47 +0000
@@ -0,0 +1,111 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+ class Utils {
+
+ public static int ParseSpeed(string speed) {
+ Regex regex = new Regex(
+ @"^(\d+)\s*(b|[km]i?b?)$",
+ RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace
+ );
+ Match match = regex.Match(speed);
+
+ if (match.Success) {
+ int number = int.Parse(match.Groups[1].Value);
+ string unit = match.Groups[2].Value.ToLower();
+ int scale = 1;
+
+ if (unit == "" || unit[0] == 'k') scale = 1;
+ else if (unit[0] == 'm') scale = 1024;
+
+ return number * scale;
+
+ } else {
+ throw new ArgumentException("Invalid speed string");
+ }
+ }
+
+ public static string FormatAmount(float amount, string baseFormat, float[] scales, string[] formats) {
+ if (scales.Length != formats.Length)
+ throw new ArgumentException("'scales' and 'formats' arguments must have equal length");
+
+ // The typical scales count is three to five, so don't use binary search,
+ // but just plain reverse loop.
+ for (int i = scales.Length-1; i >= 0; --i) {
+ if (amount >= scales[i])
+ return string.Format(formats[i], amount / scales[i]);
+ }
+
+ return string.Format(baseFormat, amount);
+ }
+
+ // Format speed in KiB/sec into human-readable representation.
+ public static string FormatSpeed(int speed_kbytes_sec) {
+ return FormatAmount(speed_kbytes_sec, "{0} KiB/sec",
+ new float[] { 1024, 1024*1024},
+ new string[] {"{0:#.#} MiB/sec", "{0:#.#} GiB/sec"}
+ );
+ }
+
+ // Format size in bytes into human-readable representation.
+ public static string FormatSize(long size_bytes) {
+ return FormatAmount(size_bytes, "{0} B",
+ new float[] { 1024, 1024*1024, 1024*1024*1024},
+ new string[] {"{0:#.#} KiB", "{0:#.#} MiB", "{0:#.##} GiB"}
+ );
+ }
+
+ public readonly static IEnumerable<PredefinedSpeed> PredefinedSpeedItems = new List<PredefinedSpeed>() {
+ new PredefinedSpeed( 10, "10 KiB/sec", ""),
+ new PredefinedSpeed( 20, "20 KiB/sec", ""),
+ new PredefinedSpeed( 50, "50 KiB/sec", ""),
+ new PredefinedSpeed( 100, "100 KiB/sec", ""),
+ new PredefinedSpeed( 200, "200 KiB/sec", ""),
+ new PredefinedSpeed( 500, "500 KiB/sec", ""),
+ new PredefinedSpeed( 1 * 1024, "1 MiB/sec", ""),
+ new PredefinedSpeed( 2 * 1024, "2 MiB/sec", ""),
+ new PredefinedSpeed( 5 * 1024, "5 MiB/sec", ""),
+ new PredefinedSpeed( 10 * 1024, "10 MiB/sec", ""),
+ new PredefinedSpeed( 20 * 1024, "20 MiB/sec", ""),
+ new PredefinedSpeed( 50 * 1024, "50 MiB/sec", ""),
+ new PredefinedSpeed(100 * 1024, "100 MiB/sec", ""),
+ };
+
+ }
+
+ public class PredefinedSpeed: Item {
+ private string _name, _desc;
+ private int _value;
+
+ public PredefinedSpeed(int value, string name, string desc) {
+ _value = value;
+ _name = name;
+ _desc = desc;
+ }
+
+ public override string Name {
+ get { return _name; }
+ }
+
+ public override string Description {
+ get { return _desc; }
+ }
+
+ public override string Icon {
+ get { return "top"; }
+ }
+
+ public int Value {
+ get { return _value; }
+ }
+ }
+
+}
=== modified file 'configure.ac'
--- configure.ac 2010-03-08 02:43:56 +0000
+++ configure.ac 2010-07-26 16:47:47 +0000
@@ -383,6 +383,8 @@
Tracker/Resources/TrackerSearch.addin.xml
Translate/Makefile
Translate/Resources/Translate.addin.xml
+Transmission/Makefile
+Transmission/Resources/Transmission.addin.xml
Tomboy/Makefile
Tomboy/Resources/Tomboy.addin.xml
Vinagre/Makefile