summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Laroche2015-07-02 16:50:05 +0200
committerThomas Laroche2015-07-02 16:50:05 +0200
commit98075a60147dd8d48f63e485405ad464e0e5aed1 (patch)
treed7241a41fe94c91f12cb870387ec7a3ff48ee1fc
downloadaur-98075a60147dd8d48f63e485405ad464e0e5aed1.tar.gz
Initial import
-rw-r--r--.SRCINFO58
-rw-r--r--PKGBUILD100
-rw-r--r--sequential.patch643
-rw-r--r--transmission-cli.install10
-rw-r--r--transmission-gtk.install12
-rw-r--r--transmission-qt.install12
6 files changed, 835 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..b339fedd40ad
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,58 @@
+pkgbase = transmission-sequential
+ pkgver = 2.84
+ pkgrel = 2
+ url = http://www.transmissionbt.com/
+ install = transmission-cli.install
+ arch = i686
+ arch = x86_64
+ license = MIT
+ makedepends = gtk3
+ makedepends = intltool
+ makedepends = curl
+ makedepends = qt5-base
+ makedepends = libevent
+ depends = curl
+ depends = libevent
+ depends = systemd
+ provides = transmission-cli
+ conflicts = transmission-cli
+ source = https://transmission.cachefly.net/transmission-2.84.tar.xz
+ source = sequential.patch
+ md5sums = 411aec1c418c14f6765710d89743ae42
+ md5sums = cbbee2c84c25183d7322babfc8ab11e3
+
+pkgname = transmission-sequential-cli
+ pkgdesc = Fast, easy, and free BitTorrent client (CLI tools, daemon and web client) (+sequential patch)
+ install = transmission-cli.install
+ depends = curl
+ depends = libevent
+ depends = systemd
+ provides = transmission-cli
+ conflicts = transmission-cli
+
+pkgname = transmission-sequential-gtk
+ pkgdesc = Fast, easy, and free BitTorrent client (GTK+ GUI) (+sequential patch)
+ install = transmission-gtk.install
+ depends = curl
+ depends = libevent
+ depends = gtk3
+ depends = desktop-file-utils
+ depends = hicolor-icon-theme
+ depends = desktop-file-utils
+ optdepends = notification-daemon: Desktop notification support
+ optdepends = transmission-sequential-cli: daemon and web support
+ provides = transmission-gtk
+ conflicts = transmission-gtk
+
+pkgname = transmission-sequential-qt
+ pkgdesc = Fast, easy, and free BitTorrent client (Qt GUI) (+sequential patch)
+ install = transmission-qt.install
+ depends = curl
+ depends = qt5-base
+ depends = libevent
+ depends = libxkbcommon-x11
+ depends = desktop-file-utils
+ optdepends = transmission-sequential-cli: daemon and web support
+ provides = transmission-qt
+ conflicts = transmission-qt
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..6e9123323a5c
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,100 @@
+# Maintainer : Thomas Laroche <tho.laroche@gmail.com>
+# Contributor : Tom Gundersen <teg@jklm.no>
+# Contributor : Ionut Biru <ibiru@archlinux.org>
+
+pkgbase=transmission-sequential
+pkgname=('transmission-sequential-cli' 'transmission-sequential-gtk' 'transmission-sequential-qt')
+pkgver=2.84
+pkgrel=2
+arch=('i686' 'x86_64')
+url="http://www.transmissionbt.com/"
+license=('MIT')
+depends=('curl' 'libevent' 'systemd')
+makedepends=('gtk3' 'intltool' 'curl' 'qt5-base' 'libevent')
+provides=('transmission-cli')
+conflicts=('transmission-cli')
+install=transmission-cli.install
+source=("https://transmission.cachefly.net/transmission-$pkgver.tar.xz" sequential.patch)
+md5sums=('411aec1c418c14f6765710d89743ae42'
+ 'cbbee2c84c25183d7322babfc8ab11e3')
+
+_inarray() {
+ local e
+ for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
+ return 1
+}
+
+prepare() {
+ cd transmission-$pkgver
+ patch -Np1 -i $srcdir/sequential.patch
+}
+
+build() {
+ cd transmission-$pkgver
+
+#Don't build if not needed since long to build
+ if _inarray 'transmission-sequential-gtk' "${pkgname[@]}"; then
+ ./configure --prefix=/usr
+ else
+ ./configure --prefix=/usr --without-gtk
+ fi
+ make
+
+#Don't build if not needed since long to build
+ if _inarray 'transmission-sequential-qt' "${pkgname[@]}"; then
+ pushd qt
+ qmake qtr.pro
+ make
+ fi
+}
+
+package_transmission-sequential-cli() {
+ pkgdesc="Fast, easy, and free BitTorrent client (CLI tools, daemon and web client) (+sequential patch)"
+ depends=('curl' 'libevent' 'systemd')
+ provides=('transmission-cli')
+ conflicts=('transmission-cli')
+ install=transmission-cli.install
+
+ cd transmission-$pkgver
+
+ for dir in daemon cli web utils
+ do
+ make -C "$dir" DESTDIR="$pkgdir" install
+ done
+
+ install -D -m644 "$srcdir/transmission-$pkgver/daemon/transmission-daemon.service" "$pkgdir/usr/lib/systemd/system/transmission.service"
+ install -D -m644 COPYING "$pkgdir/usr/share/licenses/transmission-sequential-cli/COPYING"
+}
+
+package_transmission-sequential-gtk() {
+ pkgdesc="Fast, easy, and free BitTorrent client (GTK+ GUI) (+sequential patch)"
+ depends=('curl' 'libevent' 'gtk3' 'desktop-file-utils' 'hicolor-icon-theme' 'desktop-file-utils')
+ optdepends=('notification-daemon: Desktop notification support'
+ 'transmission-sequential-cli: daemon and web support')
+ provides=('transmission-gtk')
+ conflicts=('transmission-gtk')
+ install=transmission-gtk.install
+
+ cd transmission-$pkgver
+
+ make -C gtk DESTDIR="$pkgdir" install
+ make -C po DESTDIR="$pkgdir" install
+ install -D -m644 COPYING "$pkgdir/usr/share/licenses/transmission-sequential-gtk/COPYING"
+}
+
+package_transmission-sequential-qt() {
+ pkgdesc="Fast, easy, and free BitTorrent client (Qt GUI) (+sequential patch)"
+ depends=('curl' 'qt5-base' 'libevent' 'libxkbcommon-x11' 'desktop-file-utils')
+ optdepends=('transmission-sequential-cli: daemon and web support')
+ provides=('transmission-qt')
+ conflicts=('transmission-qt')
+ install=transmission-qt.install
+
+ cd transmission-$pkgver
+
+ make -C qt INSTALL_ROOT="$pkgdir"/usr install
+
+ install -D -m644 COPYING "$pkgdir/usr/share/licenses/transmission-sequential-qt/COPYING"
+ install -D -m644 qt/icons/transmission.png "$pkgdir/usr/share/pixmaps/transmission-qt.png"
+ install -D -m644 qt/transmission-qt.desktop "$pkgdir/usr/share/applications/transmission-qt.desktop"
+}
diff --git a/sequential.patch b/sequential.patch
new file mode 100644
index 000000000000..1dff28ea3f8c
--- /dev/null
+++ b/sequential.patch
@@ -0,0 +1,643 @@
+Patch works against Transmission 2.83 and 2.84
+Some parts inspired by https://github.com/midenok/transmission-patches
+diff --git a/cli/cli.c b/cli/cli.c
+index ac2cf52..e550860 100644
+--- a/cli/cli.c
++++ b/cli/cli.c
+@@ -93,6 +93,8 @@ static const struct tr_option options[] =
+ { 'v', "verify", "Verify the specified torrent", "v", 0, NULL },
+ { 'V', "version", "Show version number and exit", "V", 0, NULL },
+ { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" },
++ { 500, "sequential-download", "Download pieces sequentialy", "seq", 0, NULL },
++ { 501, "random-download", "Download pieces randomly (default)", "rnd", 0, NULL },
+ { 0, NULL, NULL, NULL, 0, NULL }
+ };
+
+@@ -471,7 +473,12 @@ parseCommandLine (tr_variant * d, int argc, const char ** argv)
+ if (torrentPath == NULL)
+ torrentPath = optarg;
+ break;
+-
++ case 500:
++ tr_variantDictAddBool (d, TR_KEY_sequentialDownload, false);
++ break;
++ case 501:
++ tr_variantDictAddBool (d, TR_KEY_sequentialDownload, false);
++ break;
+ default:
+ return 1;
+ }
+diff --git a/daemon/remote.c b/daemon/remote.c
+index 5206ea9..b1bc1b2 100644
+--- a/daemon/remote.c
++++ b/daemon/remote.c
+@@ -309,6 +309,8 @@ static tr_option opts[] =
+ { 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL },
+ { 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL },
+ { 941, "peer-info", "List the current torrent(s)' peers", "pi", 0, NULL },
++ { 500, "sequential-download", "Download pieces sequentialy", "seq", 0, NULL },
++ { 501, "random-download", "Download pieces randomly (default)", "rnd", 0, NULL },
+ { 0, NULL, NULL, NULL, 0, NULL }
+ };
+
+@@ -413,6 +415,8 @@ getOptMode (int val)
+ case 952: /* no-seedratio */
+ case 984: /* honor-session */
+ case 985: /* no-honor-session */
++ case 500: /* sequential-download */
++ case 501: /* random-download */
+ return MODE_TORRENT_SET;
+
+ case 920: /* session-info */
+@@ -717,6 +721,7 @@ static const tr_quark details_keys[] = {
+ TR_KEY_secondsSeeding,
+ TR_KEY_seedRatioMode,
+ TR_KEY_seedRatioLimit,
++ TR_KEY_sequentialDownload,
+ TR_KEY_sizeWhenDone,
+ TR_KEY_startDate,
+ TR_KEY_status,
+@@ -902,6 +907,8 @@ printDetails (tr_variant * top)
+ printf (" Percent Done: %s%%\n", buf);
+ }
+
++ if (tr_variantDictFindBool (t, TR_KEY_sequentialDownload, &boolVal))
++ printf (" Sequential download: %s\n", (boolVal ? "Yes" : "No"));
+ if (tr_variantDictFindInt (t, TR_KEY_eta, &i))
+ printf (" ETA: %s\n", tr_strltime (buf, i, sizeof (buf)));
+ if (tr_variantDictFindInt (t, TR_KEY_rateDownload, &i))
+@@ -2164,6 +2171,10 @@ processArgs (const char * rpcurl, int argc, const char ** argv)
+ break;
+ case 985: tr_variantDictAddBool (args, TR_KEY_honorsSessionLimits, false);
+ break;
++ case 500: tr_variantDictAddBool (args, TR_KEY_sequentialDownload, true);
++ break;
++ case 501: tr_variantDictAddBool (args, TR_KEY_sequentialDownload, false);
++ break;
+ default: assert ("unhandled value" && 0);
+ break;
+ }
+diff --git a/gtk/details.c b/gtk/details.c
+index a8b9c2e..a22099c 100644
+--- a/gtk/details.c
++++ b/gtk/details.c
+@@ -36,6 +36,7 @@ struct DetailsImpl
+ {
+ GtkWidget * dialog;
+
++ GtkWidget * sequential_check;
+ GtkWidget * honor_limits_check;
+ GtkWidget * up_limited_check;
+ GtkWidget * up_limit_sping;
+@@ -49,6 +50,7 @@ struct DetailsImpl
+ GtkWidget * idle_spin;
+ GtkWidget * max_peers_spin;
+
++ gulong sequential_check_tag;
+ gulong honor_limits_check_tag;
+ gulong up_limited_check_tag;
+ gulong down_limited_check_tag;
+@@ -186,6 +188,20 @@ refreshOptions (struct DetailsImpl * di, tr_torrent ** torrents, int n)
+ **** Options Page
+ ***/
+
++ /* sequential_check */
++ if (n)
++ {
++ int i;
++ const bool baseline = tr_torrentGetSequentialDownload (torrents[0]);
++
++ for (i=1; i<n; ++i)
++ if (baseline != tr_torrentGetSequentialDownload (torrents[i]))
++ break;
++
++ if (i == n)
++ set_togglebutton_if_different (di->sequential_check, di->sequential_check_tag, baseline);
++ }
++
+ /* honor_limits_check */
+ if (n)
+ {
+@@ -444,6 +460,12 @@ max_peers_spun_cb (GtkSpinButton * s, struct DetailsImpl * di)
+ }
+
+ static void
++sequential_toggled_cb (GtkToggleButton * tb, gpointer d)
++{
++ torrent_set_bool (d, TR_KEY_sequentialDownload, gtk_toggle_button_get_active (tb));
++}
++
++static void
+ onPriorityChanged (GtkComboBox * combo_box, struct DetailsImpl * di)
+ {
+ const tr_priority_t priority = gtr_priority_combo_get_value (combo_box);
+@@ -504,6 +526,11 @@ options_page_new (struct DetailsImpl * d)
+ t = hig_workarea_create ();
+ hig_workarea_add_section_title (t, &row, _("Speed"));
+
++ tb = hig_workarea_add_wide_checkbutton (t, &row, _("Sequential download"), 0);
++ d->sequential_check = tb;
++ tag = g_signal_connect (tb, "toggled", G_CALLBACK (sequential_toggled_cb), d);
++ d->sequential_check_tag = tag;
++
+ tb = hig_workarea_add_wide_checkbutton (t, &row, _("Honor global _limits"), 0);
+ d->honor_limits_check = tb;
+ tag = g_signal_connect (tb, "toggled", G_CALLBACK (global_speed_toggled_cb), d);
+diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c
+index 376fd19..d62b380 100644
+--- a/libtransmission/peer-mgr.c
++++ b/libtransmission/peer-mgr.c
+@@ -1021,7 +1021,7 @@ pieceListSort (tr_swarm * s, enum piece_sort_state state)
+ static void
+ assertWeightedPiecesAreSorted (Torrent * t)
+ {
+- if (!t->endgame)
++ if (!t->endgame && !t->tor->sequentialOrder)
+ {
+ int i;
+ setComparePieceByWeightTorrent (t);
+@@ -1127,7 +1127,10 @@ pieceListRebuild (tr_swarm * s)
+ s->pieces = pieces;
+ s->pieceCount = pieceCount;
+
+- pieceListSort (s, PIECES_SORTED_BY_WEIGHT);
++ if(s->tor->sequentialDownload)
++ pieceListSort (s, PIECES_SORTED_BY_INDEX);
++ else
++ pieceListSort (s, PIECES_SORTED_BY_WEIGHT);
+
+ /* cleanup */
+ tr_free (pool);
+@@ -1165,6 +1168,9 @@ pieceListResortPiece (tr_swarm * s, struct weighted_piece * p)
+ if (p == NULL)
+ return;
+
++ if(s->tor->sequentialDownload)
++ return;
++
+ /* is the torrent already sorted? */
+ pos = p - s->pieces;
+ setComparePieceByWeightTorrent (s);
+@@ -1344,9 +1350,17 @@ tr_peerMgrGetNextRequests (tr_torrent * tor,
+ if (s->pieces == NULL)
+ pieceListRebuild (s);
+
+- if (s->pieceSortState != PIECES_SORTED_BY_WEIGHT)
+- pieceListSort (s, PIECES_SORTED_BY_WEIGHT);
+-
++ if (tor->sequentialDownload)
++ {
++ if (s->pieceSortState != PIECES_SORTED_BY_INDEX)
++ pieceListSort (s, PIECES_SORTED_BY_INDEX);
++ }
++ else
++ {
++ if (s->pieceSortState != PIECES_SORTED_BY_WEIGHT)
++ pieceListSort (s, PIECES_SORTED_BY_WEIGHT);
++ }
++
+ assertReplicationCountIsExact (s);
+ assertWeightedPiecesAreSorted (s);
+
+@@ -1431,7 +1445,7 @@ tr_peerMgrGetNextRequests (tr_torrent * tor,
+ /* In most cases we've just changed the weights of a small number of pieces.
+ * So rather than qsort ()ing the entire array, it's faster to apply an
+ * adaptive insertion sort algorithm. */
+- if (got > 0)
++ if (got > 0 && !tor->sequentialDownload)
+ {
+ /* not enough requests || last piece modified */
+ if (i == s->pieceCount)
+diff --git a/libtransmission/quark.c b/libtransmission/quark.c
+index e24d50d..5b8eb43 100644
+--- a/libtransmission/quark.c
++++ b/libtransmission/quark.c
+@@ -315,6 +315,7 @@ static const struct tr_key_struct my_static[] =
+ { "seedRatioMode", 13 },
+ { "seederCount", 11 },
+ { "seeding-time-seconds", 20 },
++ { "sequential", 10 },
+ { "session-count", 13 },
+ { "sessionCount", 12 },
+ { "show-backup-trackers", 20 },
+diff --git a/libtransmission/quark.h b/libtransmission/quark.h
+index 2e3a9fb..a764786 100644
+--- a/libtransmission/quark.h
++++ b/libtransmission/quark.h
+@@ -313,6 +313,7 @@ enum
+ TR_KEY_seedRatioMode,
+ TR_KEY_seederCount,
+ TR_KEY_seeding_time_seconds,
++ TR_KEY_sequentialDownload,
+ TR_KEY_session_count,
+ TR_KEY_sessionCount,
+ TR_KEY_show_backup_trackers,
+diff --git a/libtransmission/resume.c b/libtransmission/resume.c
+index f8df285..9c0f7fc 100644
+--- a/libtransmission/resume.c
++++ b/libtransmission/resume.c
+@@ -673,6 +673,7 @@ tr_torrentSaveResume (tr_torrent * tor)
+ tr_variantDictAddInt (&top, TR_KEY_max_peers, tor->maxConnectedPeers);
+ tr_variantDictAddInt (&top, TR_KEY_bandwidth_priority, tr_torrentGetPriority (tor));
+ tr_variantDictAddBool (&top, TR_KEY_paused, !tor->isRunning && !tor->isQueued);
++ tr_variantDictAddBool (&top, TR_KEY_sequentialDownload, tor->sequentialDownload);
+ savePeers (&top, tor);
+ if (tr_torrentHasMetadata (tor))
+ {
+@@ -822,6 +823,13 @@ loadFromFile (tr_torrent * tor, uint64_t fieldsToLoad)
+ fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY;
+ }
+
++ if ((fieldsToLoad & TR_FR_SEQUENTIAL)
++ && tr_variantDictFindBool (&top, TR_KEY_sequentialDownload, &boolVal))
++ {
++ tor->sequentialDownload = boolVal;
++ fieldsLoaded |= TR_FR_SEQUENTIAL;
++ }
++
+ if (fieldsToLoad & TR_FR_PEERS)
+ fieldsLoaded |= loadPeers (&top, tor);
+
+diff --git a/libtransmission/resume.h b/libtransmission/resume.h
+index 49bc44b..7f43536 100644
+--- a/libtransmission/resume.h
++++ b/libtransmission/resume.h
+@@ -38,6 +38,7 @@ enum
+ TR_FR_TIME_DOWNLOADING = (1 << 19),
+ TR_FR_FILENAMES = (1 << 20),
+ TR_FR_NAME = (1 << 21),
++ TR_FR_SEQUENTIAL = (1 << 22),
+ };
+
+ /**
+diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c
+index 3fa61f5..6f3b3de 100644
+--- a/libtransmission/rpcimpl.c
++++ b/libtransmission/rpcimpl.c
+@@ -819,6 +819,10 @@ addField (tr_torrent * const tor,
+ tr_variantDictAddInt (d, key, tr_torrentGetRatioMode (tor));
+ break;
+
++ case TR_KEY_sequentialDownload:
++ tr_variantDictAddBool (d, key, tr_torrentGetSequentialDownload (tor));
++ break;
++
+ case TR_KEY_sizeWhenDone:
+ tr_variantDictAddInt (d, key, st->sizeWhenDone);
+ break;
+@@ -1316,6 +1320,9 @@ torrentSet (tr_session * session,
+ if (tr_variantDictFindInt (args_in, TR_KEY_queuePosition, &tmp))
+ tr_torrentSetQueuePosition (tor, tmp);
+
++ if (tr_variantDictFindBool (args_in, TR_KEY_sequentialDownload, &boolVal))
++ tr_torrentSetSequentialDownload (tor, boolVal);
++
+ if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerAdd, &trackers))
+ errmsg = addTrackerUrls (tor, trackers);
+
+diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c
+index 4042baa..c66725b 100644
+--- a/libtransmission/torrent.c
++++ b/libtransmission/torrent.c
+@@ -888,6 +888,8 @@ torrentInit (tr_torrent * tor, const tr_ctor * ctor)
+
+ tor->finishedSeedingByIdle = false;
+
++ tor->sequentialDownload = false;
++
+ tr_peerMgrAddTorrent (session->peerMgr, tor);
+
+ assert (!tor->downloadedCur);
+@@ -2406,6 +2408,28 @@ tr_torrentSetPriority (tr_torrent * tor, tr_priority_t priority)
+ }
+ }
+
++bool
++tr_torrentGetSequentialDownload (const tr_torrent * tor)
++{
++ assert (tr_isTorrent (tor));
++
++ return tor->sequentialDownload;
++}
++
++void
++tr_torrentSetSequentialDownload (tr_torrent * tor, bool sequential)
++{
++ assert (tr_isTorrent (tor));
++
++
++ if (tor->sequentialDownload != sequential)
++ {
++ tor->sequentialDownload = sequential;
++
++ tr_torrentSetDirty (tor);
++ }
++}
++
+ /***
+ ****
+ ***/
+diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h
+index a023417..d86175f 100644
+--- a/libtransmission/torrent.h
++++ b/libtransmission/torrent.h
+@@ -272,6 +272,8 @@ struct tr_torrent
+ uint16_t idleLimitMinutes;
+ tr_idlelimit idleLimitMode;
+ bool finishedSeedingByIdle;
++
++ bool sequentialDownload;
+ };
+
+ static inline tr_torrent*
+diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h
+index 39b00c4..67e3d38 100644
+--- a/libtransmission/transmission.h
++++ b/libtransmission/transmission.h
+@@ -690,6 +690,9 @@ bool tr_sessionGetDeleteSource (const tr_session *);
+ tr_priority_t tr_torrentGetPriority (const tr_torrent *);
+ void tr_torrentSetPriority (tr_torrent *, tr_priority_t);
+
++bool tr_torrentGetSequentialDownload (const tr_torrent *);
++void tr_torrentSetSequentialDownload (tr_torrent *, bool);
++
+ /***
+ ****
+ **** Torrent Queueing
+diff --git a/po/en_AU.po b/po/en_AU.po
+index 69e30e8..5c35df9 100644
+--- a/po/en_AU.po
++++ b/po/en_AU.po
+@@ -279,6 +279,10 @@ msgstr "Stop seeding if idle for N minutes:"
+ msgid "Speed"
+ msgstr "Speed"
+
++#: ../gtk/details.c
++msgid "Sequential download"
++msgstr "Sequential download"
++
+ #: ../gtk/details.c:480
+ msgid "Honor global _limits"
+ msgstr "Honour global _limits"
+diff --git a/po/en_CA.po b/po/en_CA.po
+index 24a66d5..b8a59f9 100644
+--- a/po/en_CA.po
++++ b/po/en_CA.po
+@@ -279,6 +279,10 @@ msgstr "Stop seeding if idle for N minutes:"
+ msgid "Speed"
+ msgstr "Speed"
+
++#: ../gtk/details.c
++msgid "Sequential download"
++msgstr "Sequential download"
++
+ #: ../gtk/details.c:480
+ msgid "Honor global _limits"
+ msgstr "Honour global _limits"
+diff --git a/po/en_GB.po b/po/en_GB.po
+index 4faefe9..5fb72e0 100644
+--- a/po/en_GB.po
++++ b/po/en_GB.po
+@@ -279,6 +279,10 @@ msgstr "Stop seeding if idle for N minutes:"
+ msgid "Speed"
+ msgstr "Speed"
+
++#: ../gtk/details.c
++msgid "Sequential download"
++msgstr "Sequential download"
++
+ #: ../gtk/details.c:480
+ msgid "Honor global _limits"
+ msgstr "Honour global _limits"
+diff --git a/po/es.po b/po/es.po
+index 86c4cf0..612cf72 100644
+--- a/po/es.po
++++ b/po/es.po
+@@ -285,6 +285,10 @@ msgstr "Dejar de compartir si se está inactivo por N minutos:"
+ msgid "Speed"
+ msgstr "Velocidad"
+
++#: ../gtk/details.c
++msgid "Sequential download"
++msgstr "Descarga secuencial"
++
+ #: ../gtk/details.c:480
+ msgid "Honor global _limits"
+ msgstr "Satisfacer _límites globales"
+diff --git a/po/fr.po b/po/fr.po
+index 75da4e6..ad9ce5f 100644
+--- a/po/fr.po
++++ b/po/fr.po
+@@ -285,6 +285,10 @@ msgstr "Arrêter de partager si inactif depuis N minutes :"
+ msgid "Speed"
+ msgstr "Vitesse"
+
++#: ../gtk/details.c
++msgid "Sequential download"
++msgstr "Téléchargement séquentiel"
++
+ #: ../gtk/details.c:480
+ msgid "Honor global _limits"
+ msgstr "Respecter les _limites globales"
+diff --git a/qt/details.cc b/qt/details.cc
+index 66eb7ca..e714a78 100644
+--- a/qt/details.cc
++++ b/qt/details.cc
+@@ -763,6 +763,12 @@ Details :: refresh ()
+ const Torrent * tor;
+ const Torrent * baseline = *torrents.begin ();
+
++ // mySequentialCheck
++ uniform = true;
++ baselineFlag = baseline->sequentialDownload ();
++ foreach (tor, torrents) if (baselineFlag != tor->sequentialDownload ()) { uniform = false; break; }
++ mySequentialCheck->setChecked (uniform && baselineFlag);
++
+ // mySessionLimitCheck
+ uniform = true;
+ baselineFlag = baseline->honorsSessionLimits ();
+@@ -985,6 +991,12 @@ Details :: onShowBackupTrackersToggled (bool val)
+ }
+
+ void
++Details :: onSequentialToggled (bool val)
++{
++ mySession.torrentSet (myIds, TR_KEY_sequentialDownload, val);
++ getNewData ();
++}
++void
+ Details :: onHonorsSessionLimitsToggled (bool val)
+ {
+ mySession.torrentSet (myIds, TR_KEY_honorsSessionLimits, val);
+@@ -1162,6 +1174,11 @@ Details :: createOptionsTab ()
+ HIG * hig = new HIG (this);
+ hig->addSectionTitle (tr ("Speed"));
+
++ c = new QCheckBox (tr ("Sequential download"));
++ mySequentialCheck = c;
++ hig->addWideControl (c);
++ connect (c, SIGNAL (clicked (bool)), this, SLOT (onSequentialToggled (bool)));
++
+ c = new QCheckBox (tr ("Honor global &limits"));
+ mySessionLimitCheck = c;
+ hig->addWideControl (c);
+diff --git a/qt/details.h b/qt/details.h
+index 617801c..ac1d46b 100644
+--- a/qt/details.h
++++ b/qt/details.h
+@@ -86,6 +86,7 @@ class Details: public QDialog
+ QLabel * myETALabel;
+ QLabel * myLastActivityLabel;
+
++ QCheckBox * mySequentialCheck;
+ QCheckBox * mySessionLimitCheck;
+ QCheckBox * mySingleDownCheck;
+ QCheckBox * mySingleUpCheck;
+@@ -140,6 +141,7 @@ class Details: public QDialog
+ void onFileWantedChanged (const QSet<int>& fileIndices, bool);
+ void onPathEdited (const QString& oldpath, const QString& newname);
+ void onOpenRequested (const QString& path);
++ void onSequentialToggled (bool);
+ void onHonorsSessionLimitsToggled (bool);
+ void onDownloadLimitedToggled (bool);
+ void onSpinBoxEditingFinished ();
+diff --git a/qt/torrent.cc b/qt/torrent.cc
+index 7fd8a3d..79c1b66 100644
+--- a/qt/torrent.cc
++++ b/qt/torrent.cc
+@@ -97,6 +97,7 @@ Torrent :: myProperties[] =
+ { DOWN_LIMITED, TR_KEY_downloadLimited, QVariant::Bool, STAT_EXTRA },
+ { UP_LIMIT, TR_KEY_uploadLimit, QVariant::Int, STAT_EXTRA }, /* KB/s */
+ { UP_LIMITED, TR_KEY_uploadLimited, QVariant::Bool, STAT_EXTRA },
++ { SEQUENTIAL_DOWNLOAD, TR_KEY_sequentialDownload, QVariant::Bool, STAT_EXTRA },
+ { HONORS_SESSION_LIMITS, TR_KEY_honorsSessionLimits, QVariant::Bool, STAT_EXTRA },
+ { PEER_LIMIT, TR_KEY_peer_limit, QVariant::Int, STAT_EXTRA },
+ { HASH_STRING, TR_KEY_hashString, QVariant::String, INFO },
+diff --git a/qt/torrent.h b/qt/torrent.h
+index 85f1b28..d5687b2 100644
+--- a/qt/torrent.h
++++ b/qt/torrent.h
+@@ -165,6 +165,7 @@ class Torrent: public QObject
+ DOWN_LIMITED,
+ UP_LIMIT,
+ UP_LIMITED,
++ SEQUENTIAL_DOWNLOAD,
+ HONORS_SESSION_LIMITS,
+ PEER_LIMIT,
+ HASH_STRING,
+@@ -300,6 +301,7 @@ class Torrent: public QObject
+ Speed downloadLimit () const { return Speed::fromKBps (getInt (DOWN_LIMIT)); }
+ bool uploadIsLimited () const { return getBool (UP_LIMITED); }
+ bool downloadIsLimited () const { return getBool (DOWN_LIMITED); }
++ bool sequentialDownload () const { return getBool (SEQUENTIAL_DOWNLOAD); }
+ bool honorsSessionLimits () const { return getBool (HONORS_SESSION_LIMITS); }
+ int peerLimit () const { return getInt (PEER_LIMIT); }
+ double seedRatioLimit () const { return getDouble (SEED_RATIO_LIMIT); }
+diff --git a/web/index.html b/web/index.html
+index 326baaf..d77a0ab 100755
+--- a/web/index.html
++++ b/web/index.html
+@@ -205,7 +205,8 @@
+ <div class="row"><div class="key">Uploaded:</div><div class="value" id="inspector-info-uploaded">&nbsp;</div></div>
+ <div class="row"><div class="key">Downloaded:</div><div class="value" id="inspector-info-downloaded">&nbsp;</div></div>
+ <div class="row"><div class="key">State:</div><div class="value" id="inspector-info-state">&nbsp;</div></div>
+- <div class="row"><div class="key">Running Time:</div><div class="value" id="inspector-info-running-time">&nbsp;</div></div>
++ <div class="row"><div class="key">Sequential download:</div><div class="value" id="inspector-info-sequential-download">&nbsp;</div></div>
++ <div class="row"><div class="key">Running Time:</div><div class="value" id="inspector-info-running-time">&nbsp;</div></div>
+ <div class="row"><div class="key">Remaining Time:</div><div class="value" id="inspector-info-remaining-time">&nbsp;</div></div>
+ <div class="row"><div class="key">Last Activity:</div><div class="value" id="inspector-info-last-activity">&nbsp;</div></div>
+ <div class="row"><div class="key">Error:</div><div class="value" id="inspector-info-error">&nbsp;</div></div>
+@@ -413,6 +414,9 @@
+ <li id="context_resume_selected" class="disabled context_resume_selected">Resume</li>
+ <li id="context_resume_now_selected" class="disabled context_resume_selected">Resume Now</li>
+ <li class="separator"></li>
++ <li id="context_sequential_download">Download sequentially</li>
++ <li id="context_random_download">Download randomly</li>
++ <li class="separator"></li>
+ <li id="context_move_top">Move to Top</li>
+ <li id="context_move_up">Move Up</li>
+ <li id="context_move_down">Move Down</li>
+diff --git a/web/javascript/inspector.js b/web/javascript/inspector.js
+index ab226f8..dc79241 100644
+--- a/web/javascript/inspector.js
++++ b/web/javascript/inspector.js
+@@ -188,6 +188,26 @@ function Inspector(controller) {
+ setTextContent(e.availability_lb, str);
+
+ //
++ // sequential_lb
++ //
++
++ if(torrents.length < 1)
++ str = none;
++ else {
++ str = torrents[0].getSequential();
++ for(i=0; t=torrents[i]; ++i) {
++ if(str != t.getSequential()) {
++ str = mixed;
++ break;
++ }
++ }
++ }
++ if(typeof str == "boolean")
++ setTextContent(e.sequential_lb, str?"Yes":"No");
++ else
++ setTextContent(e.sequential_lb, str || none);
++
++ //
+ // downloaded_lb
+ //
+
+@@ -778,6 +798,7 @@ function Inspector(controller) {
+ data.elements.downloaded_lb = $('#inspector-info-downloaded')[0];
+ data.elements.uploaded_lb = $('#inspector-info-uploaded')[0];
+ data.elements.state_lb = $('#inspector-info-state')[0];
++ data.elements.sequential_lb = $('#inspector-info-sequential-download')[0];
+ data.elements.running_time_lb = $('#inspector-info-running-time')[0];
+ data.elements.remaining_time_lb = $('#inspector-info-remaining-time')[0];
+ data.elements.last_activity_lb = $('#inspector-info-last-activity')[0];
+diff --git a/web/javascript/torrent.js b/web/javascript/torrent.js
+index 17b093b..f904f27 100644
+--- a/web/javascript/torrent.js
++++ b/web/javascript/torrent.js
+@@ -104,6 +104,7 @@ Torrent.Fields.StatsExtra = [
+ 'haveUnchecked',
+ 'haveValid',
+ 'peers',
++ 'sequential',
+ 'startDate',
+ 'trackerStats'
+ ];
+@@ -242,6 +243,7 @@ Torrent.prototype =
+ getRecheckProgress: function() { return this.fields.recheckProgress; },
+ getSeedRatioLimit: function() { return this.fields.seedRatioLimit; },
+ getSeedRatioMode: function() { return this.fields.seedRatioMode; },
++ getSequential: function() { return this.fields.sequential; },
+ getSizeWhenDone: function() { return this.fields.sizeWhenDone; },
+ getStartDate: function() { return this.fields.startDate; },
+ getStatus: function() { return this.fields.status; },
+diff --git a/web/javascript/transmission.js b/web/javascript/transmission.js
+index bd317db..2aea612 100644
+--- a/web/javascript/transmission.js
++++ b/web/javascript/transmission.js
+@@ -183,6 +183,8 @@ Transmission.prototype =
+ context_pause_selected: function() { tr.stopSelectedTorrents(); },
+ context_resume_selected: function() { tr.startSelectedTorrents(false); },
+ context_resume_now_selected: function() { tr.startSelectedTorrents(true); },
++ context_sequential_download: function() { tr.sequentialSelectedTorrents(true); },
++ context_random_download: function() { tr.sequentialSelectedTorrents(false); },
+ context_move: function() { tr.moveSelectedTorrents(false); },
+ context_remove: function() { tr.removeSelectedTorrents(); },
+ context_removedata: function() { tr.removeSelectedTorrentsAndData(); },
+@@ -1185,6 +1187,11 @@ Transmission.prototype =
+ this.refreshTorrents, this);
+ },
+
++ sequentialSelectedTorrents: function(seq) {
++ this.remote.sendTorrentSetRequests('torrent-set', this.getSelectedTorrentIds(),
++ { sequential: seq }, this.refreshTorrents, this);
++ },
++
+ /***
+ ****
+ ***/ \ No newline at end of file
diff --git a/transmission-cli.install b/transmission-cli.install
new file mode 100644
index 000000000000..f113d47c8a7c
--- /dev/null
+++ b/transmission-cli.install
@@ -0,0 +1,10 @@
+post_install() {
+ post_upgrade
+ passwd -l transmission &>/dev/null
+}
+
+post_upgrade() {
+ # create user/group that the daemon will run as by default, do not delete this on uninstall, as it will own files
+ getent group transmission >/dev/null || groupadd -g 169 transmission
+ getent passwd transmission >/dev/null || useradd -c 'Transmission BitTorrent Client' -u 169 -g transmission -b '/var/lib' -m -s /bin/false transmission
+}
diff --git a/transmission-gtk.install b/transmission-gtk.install
new file mode 100644
index 000000000000..c317fbaca442
--- /dev/null
+++ b/transmission-gtk.install
@@ -0,0 +1,12 @@
+post_install() {
+ update-desktop-database -q
+ gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
+}
+
+post_upgrade() {
+ post_install
+}
+
+post_remove() {
+ post_install
+}
diff --git a/transmission-qt.install b/transmission-qt.install
new file mode 100644
index 000000000000..d3289ab64233
--- /dev/null
+++ b/transmission-qt.install
@@ -0,0 +1,12 @@
+post_install() {
+ update-desktop-database -q
+}
+
+post_upgrade() {
+ post_install
+}
+
+post_remove() {
+ post_install
+}
+