prepping for fix release

This commit is contained in:
Marcos Pinto 2007-10-28 07:47:54 +00:00
parent dbbace5f23
commit ba7143acb9
57 changed files with 9535 additions and 450 deletions

View file

@ -1,4 +1,8 @@
Deluge 0.5.6 (xx October 2007)
Deluge 0.5.7 (xx November 2007)
* Set default piece size to 256-KiB in TorrentCreator plugin and add 2048KiB
as a size option.
Deluge 0.5.6 (24 October 2007)
* Web Interface Plugin
* Hopefully fix "losing data" and having to re-download parts (for real this time :p)
* Use new full allocation method which does not create files until one of its

7
TODO
View file

@ -1,2 +1,5 @@
for 0.5.6
1. fix ratio saving
for 0.5.7
1. manual recheck
2. preference for .torrent location
3. have blocklist detect 7zip files and popup a warning instead of crashing
4. add auto-pickup folder

View file

@ -375,12 +375,6 @@ namespace libtorrent
// buffers from.
boost::pool<> m_send_buffers;
// this is where all active sockets are stored.
// the selector can sleep while there's no activity on
// them
io_service m_io_service;
asio::strand m_strand;
// the file pool that all storages in this session's
// torrents uses. It sets a limit on the number of
// open files by this session.
@ -395,6 +389,12 @@ namespace libtorrent
// object.
disk_io_thread m_disk_thread;
// this is where all active sockets are stored.
// the selector can sleep while there's no activity on
// them
io_service m_io_service;
asio::strand m_strand;
// this is a list of half-open tcp connections
// (only outgoing connections)
// this has to be one of the last
@ -646,7 +646,7 @@ namespace libtorrent
void debug_log(const std::string& line)
{
(*m_ses.m_logger) << line << "\n";
(*m_ses.m_logger) << time_now_string() << " " << line << "\n";
}
session_impl& m_ses;
};

View file

@ -56,6 +56,7 @@ public:
void done(int ticket);
void limit(int limit);
int limit() const;
void close();
#ifndef NDEBUG

View file

@ -130,6 +130,8 @@ namespace libtorrent
, proxy_settings const& ps
, std::string const& password = "");
void close();
private:
boost::intrusive_ptr<http_tracker_connection> self()

View file

@ -357,6 +357,7 @@ namespace libtorrent
// the different priority levels
switch (piece_priority)
{
case 1: return prio;
case 2: return prio - 1;
case 3: return (std::max)(prio / 2, 1);
case 4: return (std::max)(prio / 2 - 1, 1);

View file

@ -111,6 +111,7 @@ namespace libtorrent
, initial_picker_threshold(4)
, allowed_fast_set_size(10)
, max_outstanding_disk_bytes_per_connection(64 * 1024)
, handshake_timeout(10)
#ifndef TORRENT_DISABLE_DHT
, use_dht_as_fallback(true)
#endif
@ -270,6 +271,11 @@ namespace libtorrent
// to not completely disrupt normal downloads.
int max_outstanding_disk_bytes_per_connection;
// the number of seconds to wait for a handshake
// response from a peer. If no response is received
// within this time, the peer is disconnected.
int handshake_timeout;
#ifndef TORRENT_DISABLE_DHT
// while this is true, the dht will note be used unless the
// tracker is online

View file

@ -240,8 +240,8 @@ namespace libtorrent
void parse_info_section(entry const& e);
entry extra(char const* key) const
{ return m_extra_info[key]; }
entry const* extra(char const* key) const
{ return m_extra_info.find_key(key); }
// frees parts of the metadata that isn't
// used by seeds

View file

@ -202,7 +202,7 @@ namespace libtorrent
void fail(int code, char const* msg);
void fail_timeout();
void close();
virtual void close();
address const& bind_interface() const { return m_bind_interface; }
protected:

View file

@ -74,6 +74,8 @@ namespace libtorrent
, boost::weak_ptr<request_callback> c
, session_settings const& stn);
void close();
private:
enum action_t

View file

@ -148,7 +148,18 @@ private:
{
mapping[0].protocol = 0;
mapping[1].protocol = 1;
#ifndef NDEBUG
magic = 1337;
#endif
}
#ifndef NDEBUG
~rootdevice()
{
TORRENT_ASSERT(magic == 1337);
magic = 0;
}
#endif
// the interface url, through which the list of
// supported interfaces are fetched
@ -174,8 +185,12 @@ private:
mutable boost::shared_ptr<http_connection> upnp_connection;
#ifndef NDEBUG
int magic;
#endif
void close() const
{
TORRENT_ASSERT(magic == 1337);
if (!upnp_connection) return;
upnp_connection->close();
upnp_connection.reset();

View file

@ -48,8 +48,8 @@ namespace aux
template<class IO_Control_Command>
struct io_control_visitor_ec: boost::static_visitor<>
{
io_control_visitor_ec(IO_Control_Command& ioc, asio::error_code& ec)
: ioc(ioc), ec(ec) {}
io_control_visitor_ec(IO_Control_Command& ioc, asio::error_code& ec_)
: ioc(ioc), ec(ec_) {}
template <class T>
void operator()(T* p) const
@ -188,7 +188,7 @@ namespace aux
: boost::static_visitor<>
{
close_visitor_ec(asio::error_code& ec_)
: ec(ec)
: ec(ec_)
{}
template <class T>
@ -329,9 +329,9 @@ namespace aux
struct read_some_visitor_ec
: boost::static_visitor<std::size_t>
{
read_some_visitor_ec(Mutable_Buffers const& buffers, asio::error_code& ec)
read_some_visitor_ec(Mutable_Buffers const& buffers, asio::error_code& ec_)
: buffers(buffers)
, ec(ec)
, ec(ec_)
{}
template <class T>

View file

@ -86,6 +86,11 @@ namespace libtorrent
try_connect();
}
void connection_queue::close()
{
m_timer.cancel();
}
void connection_queue::limit(int limit)
{ m_half_open_limit = limit; }

View file

@ -341,6 +341,10 @@ namespace libtorrent
// else std::cerr << "DISK THREAD: invoking callback" << std::endl;
try { if (handler) handler(ret, j); }
catch (std::exception&) {}
#ifndef NDEBUG
m_current.storage = 0;
#endif
if (j.buffer && free_buffer)
{

View file

@ -489,7 +489,9 @@ namespace libtorrent
, boost::lexical_cast<std::string>(m_port));
m_name_lookup.async_resolve(q, m_strand.wrap(
boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2)));
set_timeout(m_settings.tracker_completion_timeout
set_timeout(req.event == tracker_request::stopped
? m_settings.stop_tracker_timeout
: m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
@ -503,6 +505,17 @@ namespace libtorrent
fail_timeout();
}
void http_tracker_connection::close()
{
asio::error_code ec;
m_socket.close(ec);
m_name_lookup.cancel();
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
m_connection_ticket = -1;
m_timed_out = true;
tracker_connection::close();
}
void http_tracker_connection::name_lookup(asio::error_code const& error
, tcp::resolver::iterator i) try
{
@ -759,7 +772,6 @@ namespace libtorrent
if (m_parser.status_code() != 200)
{
fail(m_parser.status_code(), m_parser.message().c_str());
close();
return;
}
@ -821,6 +833,7 @@ namespace libtorrent
TORRENT_ASSERT(false);
}
#endif
close();
}
peer_entry http_tracker_connection::extract_peer_info(const entry& info)

View file

@ -184,5 +184,6 @@ void lsd::on_announce(udp::endpoint const& from, char* buffer
void lsd::close()
{
m_socket.close();
m_broadcast_timer.cancel();
}

View file

@ -382,6 +382,8 @@ void natpmp::try_next_mapping(int i)
void natpmp::close()
{
asio::error_code ec;
m_socket.close(ec);
if (m_disabled) return;
for (int i = 0; i < num_mappings; ++i)
{
@ -390,5 +392,7 @@ void natpmp::close()
m_mappings[i].external_port = 0;
refresh_mapping(i);
}
m_refresh_timer.cancel();
m_send_timer.cancel();
}

View file

@ -3000,13 +3000,12 @@ namespace libtorrent
return true;
}
// if it takes more than 5 seconds to receive
// handshake, disconnect
if (in_handshake() && d > seconds(5))
// do not stall waiting for a handshake
if (in_handshake() && d > seconds(m_ses.settings().handshake_timeout))
{
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << time_now_string() << " *** NO HANDSHAKE [ "
<< total_seconds(d) << " seconds ago ] ***\n";
(*m_logger) << time_now_string() << " *** NO HANDSHAKE [ waited "
<< total_seconds(d) << " seconds ] ***\n";
#endif
return true;
}

View file

@ -547,8 +547,8 @@ namespace detail
, fingerprint const& cl_fprint
, char const* listen_interface)
: m_send_buffers(send_buffer_size)
, m_strand(m_io_service)
, m_files(40)
, m_strand(m_io_service)
, m_half_open(m_io_service)
, m_download_channel(m_io_service, peer_connection::download_channel)
, m_upload_channel(m_io_service, peer_connection::upload_channel)
@ -675,6 +675,14 @@ namespace detail
if (m_dht) m_dht->stop();
#endif
m_timer.cancel();
// close the listen sockets
for (std::list<listen_socket_t>::iterator i = m_listen_sockets.begin()
, end(m_listen_sockets.end()); i != end; ++i)
{
i->sock->close();
}
// abort all torrents
for (torrent_map::iterator i = m_torrents.begin()
, end(m_torrents.end()); i != end; ++i)
@ -682,7 +690,15 @@ namespace detail
i->second->abort();
}
m_io_service.stop();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " aborting all tracker requests\n";
#endif
m_tracker_manager.abort_all_requests();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " shutting down connection queue\n";
#endif
m_half_open.close();
mutex::scoped_lock l2(m_checker_impl.m_mutex);
// abort the checker thread
@ -977,6 +993,17 @@ namespace detail
return;
}
// don't allow more connections than the max setting
if (m_connections.size() > max_connections())
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << "number of connections limit exceeded (conns: "
<< num_connections() << ", limit: " << max_connections()
<< "), connection rejected\n";
#endif
return;
}
// check if we have any active torrents
// if we don't reject the connection
if (m_torrents.empty()) return;
@ -1474,20 +1501,12 @@ namespace detail
while (!m_abort);
deadline_timer tracker_timer(m_io_service);
// this will remove the port mappings
if (m_natpmp.get())
m_natpmp->close();
if (m_upnp.get())
m_upnp->close();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " locking mutex\n";
#endif
session_impl::mutex_t::scoped_lock l(m_mutex);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " aborting all tracker requests\n";
#endif
m_tracker_manager.abort_all_requests();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " sending stopped to all torrent's trackers\n";
@ -2127,16 +2146,10 @@ namespace detail
session_impl::~session_impl()
{
abort();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n";
#endif
// lock the main thread and abort it
mutex_t::scoped_lock l(m_mutex);
m_abort = true;
m_io_service.stop();
l.unlock();
abort();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " waiting for main thread\n";

View file

@ -1526,7 +1526,7 @@ namespace libtorrent
INVARIANT_CHECK;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_ses.m_logger) << time_now_string() << " resolving: " << url << "\n";
(*m_ses.m_logger) << time_now_string() << " resolving web seed: " << url << "\n";
#endif
m_resolving_web_seeds.insert(url);
@ -3055,7 +3055,7 @@ namespace libtorrent
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
void torrent::debug_log(const std::string& line)
{
(*m_ses.m_logger) << line << "\n";
(*m_ses.m_logger) << time_now_string() << " " << line << "\n";
}
#endif

View file

@ -302,12 +302,12 @@ namespace libtorrent
{
m_completion_timeout = completion_timeout;
m_read_timeout = read_timeout;
m_start_time = time_now();
m_read_time = time_now();
m_start_time = m_read_time = time_now();
m_timeout.expires_at((std::min)(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
, m_start_time + seconds((std::min)(m_completion_timeout
, m_read_timeout))));
m_timeout.async_wait(m_strand.wrap(bind(
&timeout_handler::timeout_callback, self(), _1)));
}
@ -343,7 +343,8 @@ namespace libtorrent
m_timeout.expires_at((std::min)(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
, m_start_time + seconds((std::min)(m_completion_timeout
, m_read_timeout))));
m_timeout.async_wait(m_strand.wrap(
bind(&timeout_handler::timeout_callback, self(), _1)));
}
@ -567,12 +568,24 @@ namespace libtorrent
m_abort = true;
tracker_connections_t keep_connections;
for (tracker_connections_t::const_iterator i =
m_connections.begin(); i != m_connections.end(); ++i)
while (!m_connections.empty())
{
tracker_request const& req = (*i)->tracker_req();
boost::intrusive_ptr<tracker_connection>& c = m_connections.back();
if (!c)
{
m_connections.pop_back();
continue;
}
tracker_request const& req = c->tracker_req();
if (req.event == tracker_request::stopped)
keep_connections.push_back(*i);
{
keep_connections.push_back(c);
m_connections.pop_back();
continue;
}
// close will remove the entry from m_connections
// so no need to pop
c->close();
}
std::swap(m_connections, keep_connections);

View file

@ -96,7 +96,9 @@ namespace libtorrent
m_name_lookup.async_resolve(q
, m_strand.wrap(boost::bind(
&udp_tracker_connection::name_lookup, self(), _1, _2)));
set_timeout(m_settings.tracker_completion_timeout
set_timeout(req.event == tracker_request::stopped
? m_settings.stop_tracker_timeout
: m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
@ -156,11 +158,20 @@ namespace libtorrent
void udp_tracker_connection::on_timeout()
{
m_socket.close();
asio::error_code ec;
m_socket.close(ec);
m_name_lookup.cancel();
fail_timeout();
}
void udp_tracker_connection::close()
{
asio::error_code ec;
m_socket.close(ec);
m_name_lookup.cancel();
tracker_connection::close();
}
void udp_tracker_connection::send_udp_connect()
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
@ -468,6 +479,7 @@ namespace libtorrent
, complete, incomplete);
m_man.remove_request(this);
close();
return;
}
catch (std::exception& e)
@ -543,6 +555,7 @@ namespace libtorrent
if (!cb)
{
m_man.remove_request(this);
close();
return;
}
@ -551,6 +564,7 @@ namespace libtorrent
, complete, incomplete);
m_man.remove_request(this);
close();
}
catch (std::exception& e)
{

View file

@ -148,6 +148,7 @@ void upnp::set_mappings(int tcp, int udp)
, end(m_devices.end()); i != end; ++i)
{
rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
if (d.mapping[0].local_port != m_tcp_local_port)
{
if (d.mapping[0].external_port == 0)
@ -200,8 +201,13 @@ try
// we don't have a WANIP or WANPPP url for this device,
// ask for it
rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.url << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2
, boost::ref(d)))));
@ -270,7 +276,7 @@ try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice responded with incorrect HTTP packet. Ignoring device (" << e.what() << ")" << std::endl;
<< " <== (" << from << ") Rootdevice responded with incorrect HTTP packet. Ignoring device (" << e.what() << ")" << std::endl;
#endif
return;
}
@ -280,11 +286,11 @@ try
#ifdef TORRENT_UPNP_LOGGING
if (p.method().empty())
m_log << time_now_string()
<< " <== Device responded with HTTP status: " << p.status_code()
<< " <== (" << from << ") Device responded with HTTP status: " << p.status_code()
<< ". Ignoring device" << std::endl;
else
m_log << time_now_string()
<< " <== Device with HTTP method: " << p.method()
<< " <== (" << from << ") Device with HTTP method: " << p.method()
<< ". Ignoring device" << std::endl;
#endif
return;
@ -294,7 +300,7 @@ try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice responded with incomplete HTTP "
<< " <== (" << from << ") Rootdevice responded with incomplete HTTP "
"packet. Ignoring device" << std::endl;
#endif
return;
@ -305,7 +311,7 @@ try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice response is missing a location header. "
<< " <== (" << from << ") Rootdevice response is missing a location header. "
"Ignoring device" << std::endl;
#endif
return;
@ -332,7 +338,7 @@ try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice uses unsupported protocol: '" << protocol
<< " <== (" << from << ") Rootdevice uses unsupported protocol: '" << protocol
<< "'. Ignoring device" << std::endl;
#endif
return;
@ -342,16 +348,27 @@ try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice responded with a url with port 0. "
<< " <== (" << from << ") Rootdevice responded with a url with port 0. "
"Ignoring device" << std::endl;
#endif
return;
}
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Found rootdevice: " << d.url << std::endl;
<< " <== (" << from << ") Found rootdevice: " << d.url
<< " total: " << m_devices.size() << std::endl;
#endif
if (m_devices.size() >= 50)
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== (" << from << ") Too many devices (" << m_devices.size() << "), "
"ignoring: " << d.url << std::endl;
#endif
return;
}
if (m_tcp_local_port != 0)
{
d.mapping[0].need_update = true;
@ -390,8 +407,13 @@ try
// we don't have a WANIP or WANPPP url for this device,
// ask for it
rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
try
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.url << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2
, boost::ref(d)))));
@ -420,6 +442,7 @@ catch (std::exception&)
void upnp::post(upnp::rootdevice const& d, std::string const& soap
, std::string const& soap_action)
{
TORRENT_ASSERT(d.magic == 1337);
std::stringstream header;
header << "POST " << d.control_url << " HTTP/1.1\r\n"
@ -439,6 +462,7 @@ void upnp::post(upnp::rootdevice const& d, std::string const& soap
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
{
TORRENT_ASSERT(d.magic == 1337);
std::string soap_action = "AddPortMapping";
std::stringstream soap;
@ -463,6 +487,7 @@ void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
void upnp::map_port(rootdevice& d, int i)
{
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection) return;
if (!d.mapping[i].need_update)
@ -479,6 +504,10 @@ void upnp::map_port(rootdevice& d, int i)
TORRENT_ASSERT(!d.upnp_connection);
TORRENT_ASSERT(d.service_namespace);
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.hostname << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, self(), _1, _2
, boost::ref(d), i)), true
@ -490,6 +519,7 @@ void upnp::map_port(rootdevice& d, int i)
void upnp::delete_port_mapping(rootdevice& d, int i)
{
TORRENT_ASSERT(d.magic == 1337);
std::stringstream soap;
std::string soap_action = "DeletePortMapping";
@ -510,23 +540,24 @@ void upnp::delete_port_mapping(rootdevice& d, int i)
// requires the mutex to be locked
void upnp::unmap_port(rootdevice& d, int i)
{
if (d.mapping[i].external_port == 0)
TORRENT_ASSERT(d.magic == 1337);
if (d.mapping[i].external_port == 0
|| d.disabled)
{
if (i < num_mappings - 1)
{
unmap_port(d, i + 1);
}
else
{
m_devices.erase(d);
}
return;
}
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.hostname << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, self(), _1, _2
, boost::ref(d), i)), true
, bind(&upnp::delete_port_mapping, self(), boost::ref(d), i)));
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
, seconds(10));
}
@ -591,6 +622,7 @@ namespace
void upnp::on_upnp_xml(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d) try
{
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection)
{
d.upnp_connection->close();
@ -601,8 +633,10 @@ void upnp::on_upnp_xml(asio::error_code const& e
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== error while fetching control url: " << e.message() << std::endl;
<< " <== (" << d.url << ") error while fetching control url: "
<< e.message() << std::endl;
#endif
d.disabled = true;
return;
}
@ -610,8 +644,9 @@ void upnp::on_upnp_xml(asio::error_code const& e
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== error while fetching control url: incomplete http message" << std::endl;
<< " <== (" << d.url << ") error while fetching control url: incomplete http message" << std::endl;
#endif
d.disabled = true;
return;
}
@ -619,8 +654,9 @@ void upnp::on_upnp_xml(asio::error_code const& e
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== error while fetching control url: " << p.message() << std::endl;
<< " <== (" << d.url << ") error while fetching control url: " << p.message() << std::endl;
#endif
d.disabled = true;
return;
}
@ -647,15 +683,17 @@ void upnp::on_upnp_xml(asio::error_code const& e
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice response, did not find a port mapping interface" << std::endl;
<< " <== (" << d.url << ") Rootdevice response, did not find "
"a port mapping interface" << std::endl;
#endif
d.disabled = true;
return;
}
}
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== Rootdevice response, found control URL: " << s.control_url
<< " <== (" << d.url << ") Rootdevice response, found control URL: " << s.control_url
<< " namespace: " << d.service_namespace << std::endl;
#endif
@ -732,6 +770,7 @@ namespace
void upnp::on_upnp_map_response(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d, int mapping) try
{
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection)
{
d.upnp_connection->close();
@ -744,7 +783,7 @@ void upnp::on_upnp_map_response(asio::error_code const& e
m_log << time_now_string()
<< " <== error while adding portmap: " << e.message() << std::endl;
#endif
m_devices.erase(d);
d.disabled = true;
return;
}
@ -773,7 +812,7 @@ void upnp::on_upnp_map_response(asio::error_code const& e
m_log << time_now_string()
<< " <== error while adding portmap: incomplete http message" << std::endl;
#endif
m_devices.erase(d);
d.disabled = true;
return;
}
@ -877,6 +916,7 @@ catch (std::exception&)
void upnp::on_upnp_unmap_response(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d, int mapping) try
{
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection)
{
d.upnp_connection->close();
@ -906,7 +946,7 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
m_log << time_now_string()
<< " <== error while deleting portmap: " << p.message() << std::endl;
#endif
m_devices.erase(d);
d.disabled = true;
return;
}
@ -922,10 +962,6 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
unmap_port(d, mapping + 1);
return;
}
// the main thread is likely to be waiting for
// all the unmap operations to complete
m_devices.erase(d);
}
catch (std::exception&)
{
@ -943,6 +979,7 @@ void upnp::on_expire(asio::error_code const& e) try
, end(m_devices.end()); i != end; ++i)
{
rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
for (int m = 0; m < num_mappings; ++m)
{
if (d.mapping[m].expires != max_time())
@ -984,15 +1021,11 @@ void upnp::close()
}
for (std::set<rootdevice>::iterator i = m_devices.begin()
, end(m_devices.end()); i != end;)
, end(m_devices.end()); i != end; ++i)
{
rootdevice& d = const_cast<rootdevice&>(*i);
if (d.control_url.empty())
{
m_devices.erase(i++);
continue;
}
++i;
TORRENT_ASSERT(d.magic == 1337);
if (d.control_url.empty()) continue;
unmap_port(d, 0);
}
}

View file

@ -82,7 +82,7 @@ class TorrentCreator:
self.glade = gtk.glade.XML(self.path + "/torrentcreator.glade")
self.dialog = self.glade.get_widget("torrentcreator")
self.glade.get_widget("piece_size_combobox").set_active(0)
self.glade.get_widget("piece_size_combobox").set_active(3)
self.glade.get_widget("torrent_chooserbutton").connect("clicked", self.torrent_chooserbutton_clicked)
self.glade.get_widget("ok_button").connect("clicked", self.create_torrent)
self.glade.get_widget("close_button").connect("clicked", self.destroy)

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.2.2 on Wed Sep 5 12:27:39 2007 by markybob@peg-->
<!--Generated with glade3 3.2.2 on Thu Oct 25 22:18:52 2007 by markybob@peg-->
<glade-interface>
<widget class="GtkDialog" id="torrentcreator">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@ -54,7 +54,6 @@
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment2">
@ -151,7 +150,6 @@
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment3">
@ -271,7 +269,6 @@
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment4">
@ -318,7 +315,6 @@
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment5">
@ -365,7 +361,6 @@
<widget class="GtkFrame" id="frame5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment6">
@ -417,6 +412,9 @@
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="column_spacing">10</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkCheckButton" id="chk_set_priv">
<property name="visible">True</property>
@ -431,9 +429,6 @@
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkComboBox" id="piece_size_combobox">
<property name="visible">True</property>
@ -445,6 +440,7 @@
256 KiB
512 KiB
1024 KiB
2048 KiB
</property>
</widget>
<packing>

View file

@ -28,10 +28,10 @@
# but you are not obligated to do so. If you do not wish to do so, delete
# this exception statement from your version. If you delete this exception
plugin_name = "Web User Interface"
plugin_name = _("Web User Interface")
plugin_author = "Martijn Voncken"
plugin_version = "rev."
plugin_description = """A Web based User Interface
plugin_description = _("""A Web based User Interface
Firefox greasemonkey script: http://userscripts.org/scripts/show/12639
@ -41,8 +41,8 @@ There is support for multiple templates, but just one is included.
Other contributors:
*somedude : template enhancements.
"""
*markybob : stability : synced with his changes in deluge-svn.
""")
import deluge.common
import deluge.pref
@ -80,9 +80,10 @@ class plugin_WebUi(object):
self.web_server = None
if not deluge.common.windows_check():
import commands
status = commands.getstatusoutput('ps x |grep -v grep |grep run_webserver')
status = commands.getstatusoutput(
'ps x |grep -v grep |grep run_webserver')
if status[0] == 0:
os.kill(status[1].split()[0], 9)
os.kill(int(status[1].split()[0]), 9)
time.sleep(1) #safe time to wait for kill to finish.
self.config_file = deluge.common.CONFIG_DIR + "/webui.conf"
self.config = deluge.pref.Preferences(self.config_file, False)
@ -95,9 +96,6 @@ class plugin_WebUi(object):
if not self.config.get('port'): #ugly way to detect new config file.
#set default values:
self.config.set("port", 8112)
#future->use deluge-core setting for download_dir (if it is set)
self.config.set("download_dir", os.path.expanduser("~"))
self.config.set("torrent_dir", os.path.expanduser("~"))
self.config.set("button_style", 2)
self.config.set("auto_refresh", False)
self.config.set("auto_refresh_secs", 4)
@ -112,11 +110,9 @@ class plugin_WebUi(object):
self.config.set("cache_templates", True)
if deluge.common.windows_check():
if self.config.get("run_in_thread") == None:
self.config.set("run_in_thread", True)
self.config.set("run_in_thread", True)
else:
if self.config.get("run_in_thread") == None:
self.config.set("run_in_thread", False)
self.config.set("run_in_thread", False)
self.dbus_manager = get_dbus_manager(deluge_core, deluge_interface,
self.config, self.config_file)
@ -141,8 +137,8 @@ class plugin_WebUi(object):
self.kill_server()
if self.config.get("run_in_thread"):
print 'start Webui(inside gtk)..'
webserver_common.init() #reload changed config.
print 'Start Webui(inside gtk)..'
webserver_common.init_gtk_05() #reload changed config.
from deluge_webserver import WebServer #only import in threaded mode
@ -150,11 +146,9 @@ class plugin_WebUi(object):
self.web_server.start_gtk()
else:
print 'start Webui(in process)..'
path = os.path.dirname(__file__)
server_bin = path + '/run_webserver'
port = str(self.config.get('port'))
self.proc = Popen((server_bin, port),cwd=path)
print 'Start Webui(in process)..'
server_bin = os.path.dirname(__file__) + '/run_webserver'
self.proc = Popen((server_bin,'env=0.5'))
def kill_server(self):
if self.web_server:
@ -187,7 +181,8 @@ class ConfigDialog(gtk.Dialog):
template_path = os.path.join(os.path.dirname(__file__), 'templates')
self.templates = [dirname for dirname
in os.listdir(template_path)
if os.path.isdir(os.path.join(template_path, dirname))]
if os.path.isdir(os.path.join(template_path, dirname))
and not dirname.startswith('.')]
self.port = self.add_widget(_('Port Number'), gtk.SpinButton())
self.pwd1 = self.add_widget(_('New Password'), gtk.Entry())
@ -195,16 +190,11 @@ class ConfigDialog(gtk.Dialog):
self.template = self.add_widget(_('Template'), gtk.combo_box_new_text())
self.button_style = self.add_widget(_('Button Style'),
gtk.combo_box_new_text())
self.download_dir = self.add_widget(_('Download Directory'),
gtk.FileChooserButton(_('Download Directory')))
self.torrent_dir = self.add_widget(_('Torrent Directory'),
gtk.FileChooserButton(_('Torrent Directory')))
self.cache_templates = self.add_widget(_('Cache Templates'),
gtk.CheckButton())
self.run_in_thread = self.add_widget(_('Run inside GTK'), gtk.CheckButton())
#self.share_downloads = self.add_widget(_('Share Download Directory'),
# gtk.CheckButton())
self.download_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.torrent_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.port.set_range(80, 65536)
self.port.set_increments(1, 10)
self.pwd1.set_visibility(False)
@ -218,23 +208,16 @@ class ConfigDialog(gtk.Dialog):
for item in [_('Text and image'), _('Image Only'), _('Text Only')]:
self.button_style.append_text(item)
if not self.config.get("button_style"):
if self.config.get("button_style") == None:
self.config.set("button_style", 2)
self.port.set_value(int(self.config.get("port")))
self.template.set_active(
self.templates.index(self.config.get("template")))
self.button_style.set_active(self.config.get("button_style"))
#self.share_downloads.set_active(
# bool(self.config.get("share_downloads")))
self.torrent_dir.set_filename(self.config.get("torrent_dir"))
self.download_dir.set_filename(self.config.get("download_dir"))
if deluge.common.windows_check():
self.run_in_thread.set_active(True)
self.run_in_thread.set_sensitive(False)
else:
self.run_in_thread.set_active(False)
self.run_in_thread.set_sensitive(False)
self.cache_templates.set_active(self.config.get("cache_templates"))
self.vbox.pack_start(self.vb, True, True, 0)
@ -270,9 +253,7 @@ class ConfigDialog(gtk.Dialog):
self.config.set("port", int(self.port.get_value()))
self.config.set("template", self.template.get_active_text())
self.config.set("button_style", self.button_style.get_active())
self.config.set("torrent_dir", self.torrent_dir.get_filename())
self.config.set("download_dir",self.download_dir.get_filename())
self.config.set("cache_templates", self.cache_templates.get_active())
self.config.set("run_in_thread", self.run_in_thread.get_active())
#self.config.set("share_downloads", self.share_downloads.get_active())
self.config.save(self.plugin.config_file)
self.plugin.start_server() #restarts server

View file

@ -68,7 +68,7 @@ class DbusManager(dbus.service.Object):
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="",out_signature="as")
def get_torrent_state(self):
def get_session_state(self):
"""Returns a list of torrent_ids in the session.
same as 0.6, but returns type "as" instead of a pickle
"""
@ -129,18 +129,20 @@ class DbusManager(dbus.service.Object):
return status_subset
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s",out_signature="")
def pause_torrent(self, torrent_id):
in_signature="as",out_signature="")
def pause_torrent(self, torrents):
"""same as 0.6 interface"""
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,True)
for torrent_id in torrents:
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,True)
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="")
def resume_torrent(self, torrent_id):
in_signature="as", out_signature="")
def resume_torrent(self, torrents):
"""same as 0.6 interface"""
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,False)
for torrent_id in torrents:
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,False)
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="sbb", out_signature="")
@ -157,7 +159,6 @@ class DbusManager(dbus.service.Object):
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="b")
def add_torrent_url(self, url):
"""not available in deluge 0.6 interface"""
filename = fetch_url(url)
self._add_torrent(filename)
return True
@ -182,8 +183,8 @@ class DbusManager(dbus.service.Object):
#name = fillename without directory
name = name.replace('\\','/')
name = 'deluge_' + str(random.random()) + '_' + name.split('/')[-1]
filename = os.path.join(self.core.config.get("default_download_path"), name)
filename = os.path.join(self.config.get("torrent_dir"),name)
filecontent = base64.b64decode(filecontent_b64)
f = open(filename,"wb") #no with statement, that's py 2.5+
f.write(filecontent)
@ -192,11 +193,42 @@ class DbusManager(dbus.service.Object):
self._add_torrent(filename)
return True
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="a{sv}")
def get_config(self):
return self.core.config.mapping
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="v")
def get_config_value(self,key):
return self.core.config.mapping[pythonize(key)] #ugly!
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="a{sv}", out_signature="")
def set_config(self, config):
"""Set the config with values from dictionary"""
config = deluge.common.pythonize(config)
# Load all the values into the configuration
for key in self.core.config.keys():
self.core.config[key] = config[key]
self.core.apply_prefs()
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="v")
def get_download_rate(self):
return self.core.get_state()['download_rate']
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="v")
def get_upload_rate(self):
return self.core.get_state()['upload_rate']
#internal
def _add_torrent(self, filename):
#dbus types break pickle, again.....
filename = unicode(filename)
target = self.config.get("download_dir")
target = self.core.config.get("default_download_path")
torrent_id = self.core.add_torrent(filename, target,
self.interface.config.get("use_compact_storage"))

View file

@ -31,67 +31,15 @@
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
from webserver_common import TORRENT_KEYS, STATE_MESSAGES
import webserver_common as ws
from webserver_framework import *
import webpy022 as web
from webpy022.http import seeother, url
from webpy022.utils import Storage
from md5 import md5
import base64
from deluge.common import fsize
from operator import attrgetter
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = ws.proxy.get_torrent_status(torrent_id,TORRENT_KEYS)
status["id"] = torrent_id
#for naming the status-images
status["calc_state_str"] = "downloading"
if status["paused"]:
status["calc_state_str"] = "inactive"
elif status["is_seed"]:
status["calc_state_str"] = "seeding"
#action for torrent_pause
if status["calc_state_str"] == "inactive":
status["action"] = "start"
else:
status["action"] = "stop"
if status["paused"]:
status["message"] = _("Paused %s%%") % status['progress']
else:
status["message"] = "%s %i%%" % (STATE_MESSAGES[status["state"]]
, status['progress'])
#add some pre-calculated values
status.update({
"calc_total_downloaded" : (fsize(status["total_done"])
+ " (" + fsize(status["total_download"]) + ")"),
"calc_total_uploaded": (fsize(status['uploaded_memory']
+ status["total_payload_upload"]) + " ("
+ fsize(status["total_upload"]) + ")"),
})
return Storage(status) #Storage for easy templating.
#/utils
import os
#routing:
urls = (
@ -107,15 +55,21 @@ urls = (
"/resume_all(.*)", "resume_all",
"/refresh/set(.*)", "refresh_set",
"/refresh/(.*)", "refresh",
"/config(.*)","config",
"/home(.*)", "home",
"/about(.*)", "about",
"/logout(.*)", "logout",
#default-pages
"/", "login",
"", "login",
"/", "home",
"", "home",
#remote-api:
"/remote/torrent/add(.*)", "remote_torrent_add"
)
"/remote/torrent/add(.*)", "remote_torrent_add",
#static:
"/static/(.*)","static",
"/template/static/(.*)","template_static",
#"/downloads/(.*)","downloads" disabled until it can handle large downloads.
)
#/routing
#pages:
@ -133,15 +87,10 @@ class login:
start_session()
do_redirect()
elif vars.redir:
seeother(url('/login',error=1,redir=vars.redir))
seeother(url('/login',error=1, redir=vars.redir))
else:
seeother('/login?error=1')
class home:
@check_session
def GET(self, name):
do_redirect()
class index:
"page containing the torrent list."
@auto_refreshed
@ -150,7 +99,7 @@ class index:
vars = web.input(sort=None, order=None)
status_rows = [get_torrent_status(torrent_id)
for torrent_id in ws.proxy.get_torrent_state()]
for torrent_id in ws.proxy.get_session_state()]
#sorting:
if vars.sort:
@ -164,21 +113,19 @@ class index:
return ws.render.index(status_rows)
class torrent_info:
"torrent details"
@auto_refreshed
@deluge_page
def GET(self, torrent_id):
return ws.render.torrent_info(get_torrent_status(torrent_id))
class torrent_pause:
"start/stop a torrent"
@check_session
def POST(self, name):
vars = web.input(stop = None, start = None, redir = None)
if vars.stop:
ws.proxy.pause_torrent(vars.stop)
ws.proxy.pause_torrent([vars.stop])
elif vars.start:
ws.proxy.resume_torrent(vars.start)
ws.proxy.resume_torrent([vars.start])
do_redirect()
@ -194,7 +141,7 @@ class torrent_add:
if vars.url and vars.torrent.filename:
error_page(_("Choose an url or a torrent, not both."))
if vars.url:
ws.proxy.add_torrent_url(vars.url)
ws.proxy.add_torrent_url(vars.url )
do_redirect()
elif vars.torrent.filename:
data = vars.torrent.file.read()
@ -227,8 +174,7 @@ class torrent_delete:
return ws.render.torrent_delete(get_torrent_status(torrent_id))
@check_session
def POST(self, name):
torrent_id = name
def POST(self, torrent_id):
vars = web.input(data_also = None, torrent_also = None)
data_also = bool(vars.data_also)
torrent_also = bool(vars.torrent_also)
@ -238,30 +184,26 @@ class torrent_delete:
class torrent_queue_up:
@check_session
def POST(self, name):
torrent_id = name
def POST(self, torrent_id):
ws.proxy.queue_up(torrent_id)
do_redirect()
class torrent_queue_down:
@check_session
def POST(self, name):
torrent_id = name
def POST(self, torrent_id):
ws.proxy.queue_down(torrent_id)
do_redirect()
class pause_all:
@check_session
def POST(self, name):
for torrent_id in ws.proxy.get_torrent_state():
ws.proxy.pause_torrent(torrent_id)
ws.proxy.pause_torrent(ws.proxy.get_session_state())
do_redirect()
class resume_all:
@check_session
def POST(self, name):
for torrent_id in ws.proxy.get_torrent_state():
ws.proxy.resume_torrent(torrent_id)
ws.proxy.resume_torrent(ws.proxy.get_session_state())
do_redirect()
class refresh:
@ -287,13 +229,61 @@ class refresh_set:
else:
error_page(_('refresh must be > 0'))
class config:
"""core config
TODO:good validation.
"""
cfg_form = web.form.Form(
web.form.Dropdown('max_download', ws.SPEED_VALUES,
description=_('Download Speed Limit'),
post='%s Kib/sec' % ws.proxy.get_config_value('max_download_speed')
)
,web.form.Dropdown('max_upload', ws.SPEED_VALUES,
description=_('Upload Speed Limit'),
post='%s Kib/sec' % ws.proxy.get_config_value('max_upload_speed')
)
)
@deluge_page
def GET(self, name):
return ws.render.config(self.cfg_form())
def POST(self, name):
vars = web.input(max_download=None, max_upload=None)
#self.config.set("max_download_speed", float(str_bwdown))
raise NotImplementedError('todo')
class home:
@check_session
def GET(self, name):
do_redirect()
class about:
@deluge_page_noauth
def GET(self, name):
return ws.render.about()
#/pages
class logout:
def POST(self, name):
end_session()
seeother('/login')
class static(static_handler):
base_dir = os.path.join(os.path.dirname(__file__),'static')
class template_static(static_handler):
def get_base_dir(self):
return os.path.join(os.path.dirname(__file__),
'templates/%s/static' % ws.config.get('template'))
class downloads(static_handler):
def GET(self, name):
self.base_dir = ws.proxy.get_config_value('default_download_path')
if not ws.config.get('share_downloads'):
raise Exception('Access to downloads is forbidden.')
return static_handler.GET(self, name)
#/pages
def WebServer():
return create_webserver(urls, globals())
@ -307,4 +297,3 @@ def run():
if __name__ == "__main__":
run()

View file

@ -1 +1 @@
87
112

View file

@ -1,3 +1,3 @@
#!/usr/bin/env python
from deluge_webserver import *
web.run(urls, globals())
import deluge_webserver
deluge_webserver.run()

View file

@ -0,0 +1,10 @@
#!/bin/bash
pwd=deluge
url=http://localhost:8112
for arg in "$@"
do
curl -F torrent=@"$arg" -F pwd=$pwd $url/remote/torrent/add
done

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

View file

@ -0,0 +1,136 @@
#!/usr/bin/env python
#(c) Martijn Voncken, mvoncken@gmail.com
#Same Licence as web.py 0.22 ->Public Domain
#
"""
static fileserving for web.py
without the need for wsgi wrapper magic.
"""
import webpy022 as web
from webpy022.http import seeother, url
import posixpath
import urlparse
import urllib
import mimetypes
import os
import datetime
import cgi
from StringIO import StringIO
mimetypes.init() # try to read system mime.types
class static_handler:
"""
mostly c&p from SimpleHttpServer
serves relative from start location
"""
base_dir = './'
extensions_map = mimetypes.types_map
def get_base_dir(self):
#override this if you have a config that changes the base dir at runtime
#deluge on windows :(
return self.base_dir
def GET(self, path):
path = self.translate_path(path)
if os.path.isdir(path):
if not path.endswith('/'):
path += "/"
return self.list_directory(path)
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except IOError:
raise Exception('file not found:%s' % path)
#web.header("404", "File not found")
#return
web.header("Content-type", ctype)
fs = os.fstat(f.fileno())
web.header("Content-Length", str(fs[6]))
web.lastmodified(datetime.datetime.fromtimestamp(fs.st_mtime))
print f.read()
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = self.get_base_dir()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def guess_type(self, path):
base, ext = posixpath.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return 'application/octet-stream'
def list_directory(self, path):
"""Helper to produce a directory listing (absent index.html).
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
#TODO ->use web.py +template!
"""
try:
list = os.listdir(path)
except os.error:
web.header('404', "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
f = StringIO()
displaypath = cgi.escape(urllib.unquote(path))
f.write("<title>Directory listing for %s</title>\n" % displaypath)
f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
f.write("<hr>\n<ul>\n")
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
f.write('<li><a href="%s">%s</a>\n'
% (urllib.quote(linkname), cgi.escape(displayname)))
f.write("</ul>\n<hr>\n")
length = f.tell()
f.seek(0)
web.header("Content-type", "text/html")
web.header("Content-Length", str(length))
print f.read()
if __name__ == '__main__':
#example:
class usr_static(static_handler):
base_dir = os.path.expanduser('~')
urls = ('/relative/(.*)','static_handler',
'/(.*)','usr_static')
web.run(urls,globals())

View file

@ -7,6 +7,7 @@ $:render.header(_('About'))
<li><a href="http://deluge-torrent.org">Deluge</a></li>
<li><a href="http://forum.deluge-torrent.org/viewtopic.php?f=9&t=425">
WebUi forum Thread</a>
</li>
<li><a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL v2
</a></li>
@ -18,6 +19,7 @@ $:render.header(_('About'))
<ul>
<li>Martijn Voncken</li>
</ul>
<h3>Template</h3>
<ul>
<li>Martijn Voncken</li>
@ -27,6 +29,7 @@ $:render.header(_('About'))
<ul>
<li>Zach Tibbitts</li>
<li>Alon Zakai</li>
<li>Alon Zakai</li>
<li>Marcos Pinto</li>
<li>Andrew Resch</li>
@ -36,4 +39,4 @@ $:render.header(_('About'))
*and all other authors/helpers/contributors I forgot to mention.
</div>
$:render.footer()
$:render.footer()

View file

@ -0,0 +1,10 @@
$def with (form)
$:render.header(_('Config'))
<div class="error">Not Implemented!</div>
<form method="POST">
$:form.render()
<input type="submit" value="$_('Apply')"/>
</form>
$:render.footer()

View file

@ -51,8 +51,10 @@ $for torrent in torrent_list:
$:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png')
$:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png')
$:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png')
$:render.part_button('POST', '/logout', _('Logout'), 'tango/system-log-out.png')
</div>
$:render.part_refresh()
$:part_stats()
$:render.footer()

View file

@ -2,21 +2,25 @@ $def with (method, url, title, image='')
<div class="deluge_button">
<form method="$method" action="$url" class="deluge_button">
<input type="hidden" name="redir" value="$self_url()">
<button type="submit" class="deluge_button">
$if (get_config('button_style') == 0):
<button type="submit" class="deluge_button" alt="$title">
$title
$if image:
<image src="/static/images/$image" class="button" alt="$title"/>
</button>
$if (get_config('button_style') == 1):
$if image:
<image src="/static/images/$image" class="button" alt="$title"/>
<input type="image" image src="/static/images/$image" class="img_button" alt="$title"/>
$else:
<button type="submit" class="deluge_button" alt="$title">
$title
</button>
$if (get_config('button_style') == 2):
<button type="submit" class="deluge_button" alt="$title">
$title
</button>
</button>
</form>
</div>

View file

@ -0,0 +1,30 @@
$def with (stats)
<div class="panel" id='refresh_panel'>
$_('Auto refresh:')
$if getcookie('auto_refresh') == '1':
($getcookie('auto_refresh_secs')) $_('seconds') &nbsp;
$:render.part_button('GET', '/refresh/set', _('Set'), 'tango/preferences-system.png')
$:render.part_button('POST', '/refresh/off', _('Disable'), 'tango/process-stop.png')
$else:
$_('Off') &nbsp;
$:render.part_button('POST', '/refresh/on', _('Enable'), 'tango/view-refresh.png')
$#end
</div>
<div class="panel" id='refresh_panel'>
<a href='/config'>
$_('Down Speed') : $stats.download_rate ($stats.max_download)
$_('Up Speed') : $stats.upload_rate ($stats.max_upload)
</a>
(<a href='/about'>$_('About')</a>)
</div>

View file

@ -84,6 +84,7 @@ $:render.part_button('GET', '/torrent/delete/' + str(torrent.id), _('Remove'), '
<br>
<!--
[<a onclick="javascript:toggle_dump()">$_('Debug:Data Dump')</a>]
<pre style="background-color:white;color:black;display:none" id="data_dump">
@ -102,6 +103,8 @@ function toggle_dump(){
}
}
</script>
-->
</div>
@ -114,6 +117,6 @@ $:render.part_button('POST', '/torrent/queue/up/' + str(torrent.id), _('Queue Up
$:render.part_button('POST', '/torrent/queue/down/' + str(torrent.id), _('Queue Down'), 'tango/down.png')
</div>
$:render.part_refresh()
$:part_stats()
$:render.footer()

View file

@ -0,0 +1,3 @@
</body>
</html>

View file

@ -0,0 +1,12 @@
$def with (title)
<html>
<head>
<title>Deluge(example) : $title</title>
<link rel="icon" href="/static/images/deluge_icon.gif" type="image/gif" />
<link rel="shortcut icon" href="/static/images/deluge_icon.gif" type="image/gif" />
</head>
<body>
<img src="/template/static/example.png">
<a href=/home>[HOME]</a>
<h1>$title</h1>

View file

@ -0,0 +1,42 @@
$def with (torrent_list)
$:render.header(_('Torrent list'))
<form action="/torrent/pause" method="POST">
<table class="torrent_list" border=1>
<tr>
$:(sort_head('calc_state_str', 'S'))
$:(sort_head('queue_pos', '#'))
$:(sort_head('name', _('Name')))
$:(sort_head('progress', _('Progress')))
</tr>
$#4-space indentation is mandatory for for-loops in templetor!
$for torrent in torrent_list:
<tr>
<td><input type="image"
src="/static/images/$(torrent.calc_state_str)16.png"
name="$torrent.action" value="$torrent.id">
</td>
<td>$torrent.queue_pos</td>
<td style="width:100px; overflow:hidden;white-space: nowrap">
<a href="/torrent/info/$torrent.id">$(crop(torrent.name, 40))</a></td>
<td class="progress_bar">
<div class="progress_bar_outer">
<div class="progress_bar" style="width:$(torrent.progress)%">
$torrent.message
</div>
</div>
</td>
</tr>
</table>
</form>
<div class="panel" bgcolor="5555AA">
$:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png')
$:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png')
$:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png')
$:render.part_button('POST', '/logout', _('Logout'), 'tango/system-log-out.png')
</div>
$:render.footer()

View file

@ -1,5 +1,5 @@
revision-id: mvoncken@gmail.com-20070930083408-sv8mo0mi1rbjnfvk
date: 2007-10-23 15:10:08 +0200
build-date: 2007-10-23 15:34:50 +0200
revno: 87
revno: 112
branch-nick: WebUi

View file

@ -29,40 +29,96 @@
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
"""
initializes config,render and proxy.
contains all hacks to support running in process0.5 ,run inside-gtk0.5 and
run in process0.6
"""
import os
import deluge
from deluge.common import INSTALL_PREFIX
import random
import pickle
import sys
from webpy022 import template
random.seed()
path = os.path.dirname(__file__)
config_file = deluge.common.CONFIG_DIR + "/webui.conf"
try:
_('translate something')
except:
import gettext
gettext.install('~/') #no translations :(
#a bit hacky way of detecting i'm in the deluge gui or in a process :(
if not hasattr(deluge,'pref'):
try:
config_dir = deluge.common.CONFIG_DIR
except:
config_dir = os.path.expanduser("~/.config/deluge")
config_file = os.path.join(config_dir,'webui.conf')
session_file = os.path.join(config_dir,'webui.sessions')
class subclassed_render(object):
"""
try to use the html template in configured dir.
not available : use template in /deluge/
"""
def __init__(self, template_dirname, cache=False):
self.base_template = template.render(
os.path.join(path, 'templates/deluge/'),
cache=cache)
self.sub_template = template.render(
os.path.join(path, 'templates/%s/' % template_dirname),
cache=cache)
def __getattr__(self, attr):
if hasattr(self.sub_template, attr):
return getattr(self.sub_template, attr)
else:
return getattr(self.base_template, attr)
def init_process():
globals()['config'] = pickle.load(open(config_file))
globals()['render'] = subclassed_render(config.get('template'),
config.get('cache_templates'))
def init_06():
import deluge.ui.client as proxy
proxy.set_core_uri('http://localhost:58846') #How to configure this?
init_process()
globals()['proxy'] = proxy
def init_05():
import dbus
init_process()
bus = dbus.SessionBus()
proxy = bus.get_object("org.deluge_torrent.dbusplugin"
, "/org/deluge_torrent/DelugeDbusPlugin")
config = pickle.load(open(config_file))
render = template.render('templates/%s/' % config.get('template'),
cache=config.get('cache_templates'))
globals()['proxy'] = proxy
def init():
def init_gtk_05():
#appy possibly changed config-vars, only called in when runing inside gtk.
path = os.path.dirname(__file__)
from dbus_interface import get_dbus_manager
globals()['proxy'] = get_dbus_manager()
globals()['config'] = deluge.pref.Preferences(config_file, False)
globals()['render'] = template.render(os.path.join(path, 'templates/%s/' %
config.get('template')), cache=config.get('cache_templates'))
globals()['render'] = subclassed_render(config.get('template'),
config.get('cache_templates'))
#hacks to determine environment, TODO: clean up.
if 'env=0.5' in sys.argv:
init_05()
elif not hasattr(deluge, 'common'):
init_06()
elif not hasattr(deluge,'pref'):
init_05()
REVNO = '0.56.stable.' + open(os.path.join(os.path.dirname(__file__),'revno')).read()
#constants
REVNO = open(os.path.join(os.path.dirname(__file__),'revno')).read()
VERSION = open(os.path.join(os.path.dirname(__file__),'version')).read()
TORRENT_KEYS = ['distributed_copies', 'download_payload_rate',
@ -82,3 +138,30 @@ STATE_MESSAGES = (_("Queued"),
_("Finished"),
_("Seeding"),
_("Allocating"))
SPEED_VALUES = [
(-1, 'Unlimited'),
(5, '5.0 Kib/sec'),
(10, '10.0 Kib/sec'),
(15, '15.0 Kib/sec'),
(25, '25.0 Kib/sec'),
(30, '30.0 Kib/sec'),
(50, '50.0 Kib/sec'),
(80, '80.0 Kib/sec'),
(300, '300.0 Kib/sec'),
(500, '500.0 Kib/sec')
]
COOKIE_DEFAULTS = {
'auto_refresh_secs':'10'
}
try:
SESSIONS = pickle.load(open(session_file))
except:
SESSIONS = []

View file

@ -45,15 +45,21 @@ import webpy022 as web
from webpy022.webapi import cookies, setcookie as w_setcookie
from webpy022.http import seeother, url
from webpy022 import template,changequery as self_url
from webpy022.utils import Storage
from static_handler import static_handler
from deluge.common import fsize,fspeed
import traceback
import random
from operator import attrgetter
import datetime
import pickle
from md5 import md5
from deluge import common
from webserver_common import REVNO, VERSION
from webserver_common import REVNO, VERSION, COOKIE_DEFAULTS
import webserver_common as ws
from debugerror import deluge_debugerror
#init:
@ -65,14 +71,22 @@ def setcookie(key, val):
"""add 30 days expires header for persistent cookies"""
return w_setcookie(key, val , expires=2592000)
SESSIONS = [] #dumb sessions.
#really simple sessions, to bad i had to implement them myself.
def start_session():
session_id = str(random.random())
SESSIONS.append(session_id)
ws.SESSIONS.append(session_id)
if len(ws.SESSIONS) > 20: #save max 20 sessions?
ws.SESSIONS = ws.SESSIONS[-20:]
#not thread safe! , but a verry rare bug.
pickle.dump(ws.SESSIONS, open(ws.session_file,'wb'))
setcookie("session_id", session_id)
if getcookie('auto_refresh_secs') == None:
setcookie('auto_refresh_secs','10')
def end_session():
session_id = getcookie("session_id")
if session_id in ws.SESSIONS:
ws.SESSIONS.remove(session_id)
#not thread safe! , but a verry rare bug.
pickle.dump(ws.SESSIONS, open(ws.session_file,'wb'))
def do_redirect():
"""for redirects after a POST"""
@ -92,7 +106,6 @@ def error_page(error):
print ws.render.error(error)
def getcookie(key, default=None):
COOKIE_DEFAULTS = {'auto_refresh_secs':'10'}
key = str(key).strip()
ck = cookies()
val = ck.get(key, default)
@ -120,9 +133,8 @@ def check_session(func):
"""
def deco(self, name):
vars = web.input(redir_after_login=None)
ck = cookies()
if ck.has_key("session_id") and ck["session_id"] in SESSIONS:
if ck.has_key("session_id") and ck["session_id"] in ws.SESSIONS:
return func(self, name) #ok, continue..
elif vars.redir_after_login:
seeother(url("/login",redir=self_url()))
@ -154,6 +166,77 @@ def remote(func):
print traceback.format_exc()
return deco
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_stats():
stats = Storage({
'download_rate':fspeed(ws.proxy.get_download_rate()),
'upload_rate':fspeed(ws.proxy.get_upload_rate()),
'max_download':ws.proxy.get_config_value('max_download_speed_bps'),
'max_upload':ws.proxy.get_config_value('max_upload_speed_bps'),
})
if stats.max_upload < 0:
stats.max_upload = _("Unlimited")
else:
stats.max_upload = fspeed(stats.max_upload)
if stats.max_download < 0:
stats.max_download = _("Unlimited")
else:
stats.max_download = fspeed(stats.max_download)
return stats
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = Storage(ws.proxy.get_torrent_status(torrent_id,ws.TORRENT_KEYS))
#add missing values for deluge 0.6:
for key in ws.TORRENT_KEYS:
if not key in status:
status[key] = 0
status["id"] = torrent_id
#for naming the status-images
status["calc_state_str"] = "downloading"
if status["paused"]:
status["calc_state_str"] = "inactive"
elif status["is_seed"]:
status["calc_state_str"] = "seeding"
#action for torrent_pause
if status["calc_state_str"] == "inactive":
status["action"] = "start"
else:
status["action"] = "stop"
if status["paused"]:
status["message"] = _("Paused %s%%") % status['progress']
else:
status["message"] = "%s %i%%" % (ws.STATE_MESSAGES[status["state"]]
, status['progress'])
#add some pre-calculated values
status.update({
"calc_total_downloaded" : (fsize(status["total_done"])
+ " (" + fsize(status["total_download"]) + ")"),
"calc_total_uploaded": (fsize(status['uploaded_memory']
+ status["total_payload_upload"]) + " ("
+ fsize(status["total_upload"]) + ")"),
})
return status
#/utils
#template-defs:
def template_crop(text, end):
if len(text) > end:
@ -176,12 +259,15 @@ def template_sort_head(id,name):
return ws.render.sort_column_head(id, name, order, active_up, active_down)
def template_part_stats():
return ws.render.part_stats(get_stats())
def get_config(var):
return ws.config.get(var)
template.Template.globals.update({
'sort_head': template_sort_head,
'part_stats':template_part_stats,
'crop': template_crop,
'_': _ , #gettext/translations
'str': str, #because % in templetor is broken.
@ -198,145 +284,20 @@ template.Template.globals.update({
})
#/template-defs
def create_webserver(urls, methods):
from webpy022.request import webpyfunc
from webpy022 import webapi
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
#------------------------------------------------------------------------------
#Some copy and paste from web.py
#mostly caused by /static
#TODO : FIX THIS.
#static-files serving should be moved to the normal webserver!
from SimpleHTTPServer import SimpleHTTPRequestHandler
from BaseHTTPServer import BaseHTTPRequestHandler
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
from BaseHTTPServer import BaseHTTPRequestHandler
from webpy022.request import webpyfunc
from webpy022 import webapi
import os
import posixpath
import urllib
import urlparse
class RelativeHandler(SimpleHTTPRequestHandler):
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.path.dirname(__file__)
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
class StaticApp(RelativeHandler):
"""WSGI application for serving static files."""
def __init__(self, environ, start_response):
self.headers = []
self.environ = environ
self.start_response = start_response
def send_response(self, status, msg=""):
self.status = str(status) + " " + msg
def send_header(self, name, value):
self.headers.append((name, value))
def end_headers(self):
pass
def log_message(*a): pass
def __iter__(self):
environ = self.environ
self.path = environ.get('PATH_INFO', '')
self.client_address = environ.get('REMOTE_ADDR','-'), \
environ.get('REMOTE_PORT','-')
self.command = environ.get('REQUEST_METHOD', '-')
from cStringIO import StringIO
self.wfile = StringIO() # for capturing error
f = self.send_head()
self.start_response(self.status, self.headers)
if f:
block_size = 16 * 1024
while True:
buf = f.read(block_size)
if not buf:
break
yield buf
f.close()
else:
value = self.wfile.getvalue()
yield value
class WSGIWrapper(BaseHTTPRequestHandler):
"""WSGI wrapper for logging the status and serving static files."""
def __init__(self, app):
self.app = app
self.format = '%s - - [%s] "%s %s %s" - %s'
def __call__(self, environ, start_response):
def xstart_response(status, response_headers, *args):
write = start_response(status, response_headers, *args)
self.log(status, environ)
return write
path = environ.get('PATH_INFO', '')
if path.startswith('/static/'):
return StaticApp(environ, xstart_response)
else:
return self.app(environ, xstart_response)
def log(self, status, environ):
#mvoncken,no logging..
return
outfile = environ.get('wsgi.errors', web.debug)
req = environ.get('PATH_INFO', '_')
protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
method = environ.get('REQUEST_METHOD', '-')
host = "%s:%s" % (environ.get('REMOTE_ADDR','-'),
environ.get('REMOTE_PORT','-'))
#@@ It is really bad to extend from
#@@ BaseHTTPRequestHandler just for this method
time = self.log_date_time_string()
print >> outfile, self.format % (host, time, protocol,
method, req, status)
def create_webserver(urls,methods):
func = webapi.wsgifunc(webpyfunc(urls,methods, False))
server_address=("0.0.0.0",ws.config.get('port'))
func = WSGIWrapper(func)
func = webapi.wsgifunc(webpyfunc(urls, methods, False))
server_address=("0.0.0.0", int(ws.config.get('port')))
server = CherryPyWSGIServer(server_address, func, server_name="localhost")
print "(created) http://%s:%d/" % server_address
print "http://%s:%d/" % server_address
return server
#------
__all__ = ['deluge_page_noauth', 'deluge_page', 'remote',
'auto_refreshed', 'check_session',
'do_redirect', 'error_page','start_session','getcookie'
,'create_webserver','setcookie']
,'setcookie','create_webserver','end_session',
'get_torrent_status', 'check_pwd','static_handler']

View file

@ -54,3 +54,7 @@ plugins/WebSeed/__init__.py
plugins/WebSeed/webseed.glade
plugins/Scheduler/__init__.py
plugins/Scheduler/plugin.py
plugins/WebUi/__init__.py
plugins/WebUi/webserver_common.py
plugins/WebUi/deluge_webserver.py
plugins/WebUi/scripts/template_strings.py

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-22 22:35-0500\n"
"POT-Creation-Date: 2007-10-27 20:34-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -88,7 +88,7 @@ msgstr ""
msgid "<b>Torrent Info</b>"
msgstr ""
#: glade/delugegtk.glade:750
#: glade/delugegtk.glade:750 plugins/WebUi/scripts/template_strings.py:8
msgid "Details"
msgstr ""
@ -140,7 +140,7 @@ msgstr ""
msgid "_Columns"
msgstr ""
#: glade/delugegtk.glade:947 src/interface.py:596 src/files.py:79
#: glade/delugegtk.glade:947 src/interface.py:596 src/files.py:80
msgid "Size"
msgstr ""
@ -149,11 +149,13 @@ msgid "Status"
msgstr ""
#: glade/delugegtk.glade:965 src/interface.py:602
#: plugins/WebUi/scripts/template_strings.py:19
msgid "Seeders"
msgstr ""
#: glade/delugegtk.glade:974 src/interface.py:605
#: plugins/TorrentPeers/__init__.py:72
#: plugins/WebUi/scripts/template_strings.py:15
msgid "Peers"
msgstr ""
@ -171,11 +173,11 @@ msgstr ""
msgid "Time Remaining"
msgstr ""
#: glade/delugegtk.glade:1010
#: glade/delugegtk.glade:1010 plugins/WebUi/scripts/template_strings.py:4
msgid "Availability"
msgstr ""
#: glade/delugegtk.glade:1019
#: glade/delugegtk.glade:1019 plugins/WebUi/scripts/template_strings.py:21
msgid "Share Ratio"
msgstr ""
@ -211,7 +213,7 @@ msgstr ""
msgid "Remove Torrent"
msgstr ""
#: glade/delugegtk.glade:1157
#: glade/delugegtk.glade:1157 plugins/WebUi/scripts/template_strings.py:18
msgid "Remove"
msgstr ""
@ -278,7 +280,7 @@ msgstr ""
msgid "Delete downloaded files"
msgstr ""
#: glade/dgtkpopups.glade:88
#: glade/dgtkpopups.glade:88 plugins/WebUi/scripts/template_strings.py:6
msgid "Delete .torrent file"
msgstr ""
@ -294,7 +296,7 @@ msgstr ""
msgid "Clear Finished"
msgstr ""
#: glade/dgtkpopups.glade:241
#: glade/dgtkpopups.glade:241 plugins/WebUi/scripts/template_strings.py:22
msgid "Speed"
msgstr ""
@ -542,6 +544,7 @@ msgid "<b>Seeding</b>"
msgstr ""
#: glade/preferences_dialog.glade:964 src/core.py:91
#: plugins/WebUi/webserver_common.py:139
msgid "Seeding"
msgstr ""
@ -646,6 +649,7 @@ msgstr ""
#: glade/preferences_dialog.glade:1478 glade/preferences_dialog.glade:1672
#: glade/preferences_dialog.glade:1866 glade/preferences_dialog.glade:2060
#: plugins/WebUi/scripts/template_strings.py:13
msgid "Password"
msgstr ""
@ -1004,7 +1008,7 @@ msgstr ""
msgid "Name"
msgstr ""
#: src/interface.py:614
#: src/interface.py:614 plugins/WebUi/scripts/template_strings.py:10
msgid "ETA"
msgstr ""
@ -1088,31 +1092,32 @@ msgstr ""
msgid "Are you sure that you want to remove all seeding torrents?"
msgstr ""
#: src/core.py:85
#: src/core.py:85 plugins/WebUi/webserver_common.py:133
msgid "Queued"
msgstr ""
#: src/core.py:86
#: src/core.py:86 plugins/WebUi/webserver_common.py:134
msgid "Checking"
msgstr ""
#: src/core.py:87
#: src/core.py:87 plugins/WebUi/webserver_common.py:135
msgid "Connecting"
msgstr ""
#: src/core.py:88
#: src/core.py:88 plugins/WebUi/webserver_common.py:136
msgid "Downloading Metadata"
msgstr ""
#: src/core.py:89 plugins/BlocklistImport/ui.py:117
#: plugins/WebUi/webserver_common.py:137
msgid "Downloading"
msgstr ""
#: src/core.py:90
#: src/core.py:90 plugins/WebUi/webserver_common.py:138
msgid "Finished"
msgstr ""
#: src/core.py:92
#: src/core.py:92 plugins/WebUi/webserver_common.py:140
msgid "Allocating"
msgstr ""
@ -1156,11 +1161,11 @@ msgstr ""
msgid "Filename"
msgstr ""
#: src/files.py:81
#: src/files.py:82
msgid "Priority"
msgstr ""
#: src/files.py:103
#: src/files.py:104
msgid ""
"File priority can only be set when using full allocation.\n"
"Please change your preference to disable compact allocation, then remove and "
@ -1175,11 +1180,11 @@ msgstr ""
msgid "Enabled"
msgstr ""
#: src/dialogs.py:433
#: src/dialogs.py:435
msgid "translator-credits"
msgstr ""
#: src/dialogs.py:434
#: src/dialogs.py:436
msgid ""
"Deluge is free software, you can redistribute it and/or\n"
"modify it under the terms of the GNU General Public\n"
@ -1196,15 +1201,15 @@ msgid ""
"1301 USA"
msgstr ""
#: src/dialogs.py:474
#: src/dialogs.py:476
msgid "Choose a .torrent file"
msgstr ""
#: src/dialogs.py:479
#: src/dialogs.py:481
msgid "Torrent files"
msgstr ""
#: src/dialogs.py:483
#: src/dialogs.py:485
msgid "All files"
msgstr ""
@ -1390,66 +1395,66 @@ msgstr ""
msgid "Torrent Creator"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:103
#: plugins/TorrentCreator/torrentcreator.glade:102
msgid "This torrent will be made from a single file"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:104
#: plugins/TorrentCreator/torrentcreator.glade:103
msgid "File:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:120
#: plugins/TorrentCreator/torrentcreator.glade:119
msgid "This torrent will be made from a directory"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:121
#: plugins/TorrentCreator/torrentcreator.glade:120
msgid "Folder:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:138
#: plugins/TorrentCreator/torrentcreator.glade:137
msgid "<b>Source</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:184
#: plugins/TorrentCreator/torrentcreator.glade:182
msgid "Save Torrent File As:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:237
#: plugins/TorrentCreator/torrentcreator.glade:235
msgid "Load this torrent into Deluge for seeding"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:238
#: plugins/TorrentCreator/torrentcreator.glade:236
msgid "Add new torrent to queue"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:257
#: plugins/TorrentCreator/torrentcreator.glade:255
msgid "<b>Torrent File</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:305
#: plugins/TorrentCreator/torrentcreator.glade:302
msgid "<b>Trackers</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:352
#: plugins/TorrentCreator/torrentcreator.glade:348
msgid "<b>Comments</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:389
#: plugins/TorrentCreator/torrentcreator.glade:384
msgid "<b>Author</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:425
#: plugins/TorrentCreator/torrentcreator.glade:423
msgid "Set Private Flag"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:441
#: plugins/TorrentCreator/torrentcreator.glade:461
#: plugins/TorrentCreator/torrentcreator.glade:436
#: plugins/TorrentCreator/torrentcreator.glade:457
msgid ""
"The smaller the piece sizes, the more efficient the transfers will be, but "
"the actual \".torrent\" file will be larger"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:442
#: plugins/TorrentCreator/torrentcreator.glade:437
msgid ""
"32 KiB\n"
"64 KiB\n"
@ -1457,13 +1462,14 @@ msgid ""
"256 KiB\n"
"512 KiB\n"
"1024 KiB\n"
"2048 KiB\n"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:463
#: plugins/TorrentCreator/torrentcreator.glade:459
msgid "Piece Size:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:477
#: plugins/TorrentCreator/torrentcreator.glade:473
msgid "<b>Advanced</b>"
msgstr ""
@ -2102,3 +2108,171 @@ msgid ""
"When set to -1 (unlimited), the global limits in Deluge's preferences will "
"be obeyed."
msgstr ""
#: plugins/WebUi/__init__.py:31
msgid "Web User Interface"
msgstr ""
#: plugins/WebUi/__init__.py:34
msgid ""
"A Web based User Interface\n"
"\n"
"Firefox greasemonkey script: http://userscripts.org/scripts/show/12639\n"
"\n"
"Remotely add a file: \"curl -F torrent=@./test1.torrent -F pwd=deluge http://"
"localhost:8112/remote/torrent/add\"\n"
"\n"
"There is support for multiple templates, but just one is included.\n"
"\n"
"Other contributors:\n"
"*somedude : template enhancements.\n"
"*markybob : stability : synced with his changes in deluge-svn.\n"
msgstr ""
#: plugins/WebUi/__init__.py:179
msgid "WebUi Config"
msgstr ""
#: plugins/WebUi/__init__.py:187
msgid "Port Number"
msgstr ""
#: plugins/WebUi/__init__.py:188
msgid "New Password"
msgstr ""
#: plugins/WebUi/__init__.py:189
msgid "New Password(confirm)"
msgstr ""
#: plugins/WebUi/__init__.py:190
msgid "Template"
msgstr ""
#: plugins/WebUi/__init__.py:191
msgid "Button Style"
msgstr ""
#: plugins/WebUi/__init__.py:193
msgid "Cache Templates"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Text and image"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Image Only"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Text Only"
msgstr ""
#: plugins/WebUi/__init__.py:243
msgid "Confirmed Password <> New Password\n"
msgstr ""
#: plugins/WebUi/webserver_common.py:48
msgid "translate something"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:142
msgid "Choose an url or a torrent, not both."
msgstr ""
#: plugins/WebUi/deluge_webserver.py:153
msgid "no data."
msgstr ""
#: plugins/WebUi/deluge_webserver.py:230
msgid "refresh must be > 0"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:238
msgid "Download Speed Limit"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:242
msgid "Upload Speed Limit"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:1
msgid "# Of Files"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:2
msgid "About"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:3
msgid "Auto refresh:"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:5
msgid "Debug:Data Dump"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:7
msgid "Delete downloaded files."
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:9
msgid "Downloaded"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:11
msgid "Next Announce"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:12
msgid "Off"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:14
msgid "Password is invalid,try again"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:16
msgid "Pieces"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:17
msgid "Refresh page every:"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:20
msgid "Set"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:23
msgid "Submit"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:24
msgid "Total Size"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:25
msgid "Tracker"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:26
msgid "Tracker Status"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:27
msgid "Upload torrent"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:28
msgid "Uploaded"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:29
msgid "Url"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:30
msgid "seconds"
msgstr ""

2107
po/eo.po Normal file

File diff suppressed because it is too large Load diff

2146
po/ka.po Normal file

File diff suppressed because it is too large Load diff

2105
po/si.po Normal file

File diff suppressed because it is too large Load diff

2111
po/vi.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@
NAME = "deluge"
FULLNAME = "Deluge BitTorrent Client"
VERSION = "0.5.5.95"
VERSION = "0.5.6.1"
AUTHOR = "Zach Tibbitts, Alon Zakai, Marcos Pinto, Andrew Resch, Alex Dedul"
EMAIL = "zach@collegegeek.org, kripkensteiner@gmail.com, marcospinto@dipconsultants.com, alonzakai@gmail.com, rotmer@gmail.com"
DESCRIPTION = "A bittorrent client written in PyGTK"

View file

@ -32,7 +32,7 @@ import os
import xdg.BaseDirectory
PROGRAM_NAME = "Deluge"
PROGRAM_VERSION = "0.5.5.95"
PROGRAM_VERSION = "0.5.6.1"
CLIENT_CODE = "DE"
CLIENT_VERSION = "".join(PROGRAM_VERSION.split('.'))+"0"*(4 - len(PROGRAM_VERSION.split('.')))

View file

@ -646,7 +646,6 @@ static PyObject *torrent_set_max_connections_global(PyObject *self, PyObject *ar
// printf("Setting max connections: %d\r\n", max_conn);
M_ses->set_max_connections(max_conn);
Py_INCREF(Py_None); return Py_None;
}
@ -1217,11 +1216,12 @@ static PyObject *torrent_get_session_info(PyObject *self, PyObject *args)
{
session_status s = M_ses->status();
return Py_BuildValue("{s:l,s:f,s:f,s:l,s:f,s:f}",
return Py_BuildValue("{s:l,s:f,s:f,s:ls:l,s:f,s:f}",
"has_incoming_connections", long(s.has_incoming_connections),
"upload_rate", float(s.payload_upload_rate),
"download_rate", float(s.payload_download_rate),
"num_peers", long(s.num_peers),
"num_connections", long(M_ses->num_connections()),
"total_downloaded", float(s.total_payload_download),
"total_uploaded", float(s.total_payload_upload));
}
@ -1778,8 +1778,7 @@ static PyObject *torrent_replace_trackers(PyObject *self, PyObject *args)
if (PyErr_Occurred())
return NULL;
torrent_handle& h = M_torrents->at(index).handle;
if (h.is_valid()){
if (M_torrents->at(index).handle.is_valid()){
std::vector<libtorrent::announce_entry> trackerlist;
std::istringstream trackers(tracker);
std::string line;
@ -1787,8 +1786,8 @@ static PyObject *torrent_replace_trackers(PyObject *self, PyObject *args)
libtorrent::announce_entry a_entry(line);
trackerlist.push_back(a_entry);
}
h.replace_trackers(trackerlist);
h.force_reannounce();
M_torrents->at(index).handle.replace_trackers(trackerlist);
M_torrents->at(index).handle.force_reannounce();
}
Py_INCREF(Py_None); return Py_None;
}

View file

@ -43,7 +43,8 @@ import gtk
def cell_data_speed(column, cell, model, iter, data):
speed = int(model.get_value(iter, data))
speed_str = common.fspeed(speed)
if speed<100: speed_str = ""
else: speed_str = common.fspeed(speed)
cell.set_property('text', speed_str)
def cell_data_size(column, cell, model, iter, data):

View file

@ -428,6 +428,8 @@ def show_about_dialog(window):
abt.set_transient_for(window)
abt.set_name(common.PROGRAM_NAME)
abt.set_version(common.PROGRAM_VERSION)
abt.set_comments("Deluge is a full-featured BitTorrent\nclient for Linux, Mac OS X and Windows")
abt.set_documenters(["Marcos Pinto"])
abt.set_authors(["Zach Tibbitts", "Alon Zakai", "Marcos Pinto", "Andrew Resch", "Alex Dedul"])
abt.set_artists(["Andrew Wedderburn"])
abt.set_translator_credits(_("translator-credits"))

View file

@ -947,11 +947,17 @@ window, please enter your password"))
unique_id = self.manager.get_torrent_unique_id(torrent)
try:
if self.manager.unique_IDs[unique_id].trackers:
self.manager.replace_trackers(unique_id, \
try:
self.manager.replace_trackers(unique_id, \
self.manager.unique_IDs[unique_id].trackers)
except:
pass
if self.manager.unique_IDs[unique_id].uploaded_memory:
self.manager.unique_IDs[unique_id].initial_uploaded_memory \
try:
self.manager.unique_IDs[unique_id].initial_uploaded_memory \
= self.manager.unique_IDs[unique_id].uploaded_memory
except:
pass
except AttributeError:
pass
@ -1091,7 +1097,7 @@ window, please enter your password"))
def update_statusbar_and_tray(self):
plugin_messages = self.plugins.get_plugin_tray_messages()
core_state = self.manager.get_state()
connections = core_state['num_peers']
connections = core_state['num_connections']
if self.config.get("max_connections_global") < 0 :
max_connections = _("Unlimited")
else: