Compare commits

...

130 Commits

Author SHA1 Message Date
897955f0a1 Remove all Python 2 support
* Removed all __future__ imports from code
* Removed all six dependencies
* Removed all future_builtins imports
* Removed all Python 2 related code

Closes: deluge-torrent/deluge#325
2021-12-28 19:26:38 +00:00
ff309ea4c5 [GtkUI] Fix ETA sorting to match WebUI
The sort for Ascending was putting longest eta first but seems more
intuitive that the smallest time to wait should be first. The WebUI
in previous commit swapped this behaviour so updating GtkUI.
2021-12-22 23:17:14 +00:00
3b11613cc7 [WebUI] Fixed ETA sorting in WebUI
When sorting the according to ETA values, all torrents with infinite value were being
considered a lower value (INF -> 12 -> 32) instead of largest (12 -> 32 -> INF).
This is due to the fact that the INF symbol is placed to lower value (<= 0).
Now the lower values are being treated as the largest JS number when sorting.

Closes: https://dev.deluge-torrent.org/ticket/3413
Closes: https://github.com/deluge-torrent/deluge/pull/321
2021-12-22 22:04:24 +00:00
a2d0cb7141 [Console] Removed Core dependency from Console UI
UIs should not depend on core directly, so removing the dependency in ConsoleUI.
This is done by adding a hard-coded variable.

Closes https://dev.deluge-torrent.org/ticket/3491
Closes: https://github.com/deluge-torrent/deluge/pull/320
2021-12-22 21:53:17 +00:00
88ffd1b843 [Servers] Moved check_ssl_keys and generate_ssl_keys to crypto_utils.py
With this change, we drop a core dependency from the UI. This will help group together
all related functionality in one place, i.e. all security related functions.

Also updated testssl.sh version to 3.0.6 (SECURITY_TEST)

Closes: deluge-torrent/deluge#288
2021-12-20 22:09:08 +00:00
6a10e57f7e back to development 2021-12-15 19:43:39 +00:00
612e0061ed Release 2.0.5 2021-12-15 18:48:16 +00:00
2eee7453cb Update Changelog 2021-12-15 18:45:25 +00:00
58cc278145 [i18n] Fix set_language error with empty lang string
The Web server config for language was set to an empty string which
resulted in an warning logged by i18n set_language.

* Changed set_language to ignore empty language string and do nothing.
We don't want to override the user's system language unless actually
specified by the user.
* Improved the translation warning message.
2021-12-15 18:35:50 +00:00
a03e649da6 [Build] Fix WebUI js minifying error
Some users encoutered a bug where WebUI in browser show a white screen,
which indicates a problem with loading javascript files. The problem was
due to closure minifying failure leaving a zero-length deluge-all.js
file which broke the usual fallback mechanism to debug files.

* Fixed usage of ES6 const declaration breaking closure minifying.
* Cleanup minified files upon errors so no zero length files left
* Replaced broken and unmaintained slimit with rjsmin.
* Fixed unable to set dev or debug query args due to request args
requiring bytes.
2021-12-15 09:37:55 +00:00
073bbbc09d [Packaging] Start replacing deprecated distutils
Working towards removing distutils

> direct usage of distutils is now actively discouraged,
with setuptools being the preferred replacement.

Ref: https://setuptools.pypa.io/en/latest/deprecated/distutils-legacy.html
2021-12-13 23:57:09 +00:00
bca0aa3532 [CI] Replace pypi deluge-libtorrent with libtorrent
* Remove certifi since included in requirements.txt
* Remove old travis config
2021-12-12 21:49:31 +00:00
cb588d0205 [Docs] Update release checklist page 2021-12-12 21:43:54 +00:00
2b20e9689b back to development 2021-12-12 19:35:54 +00:00
65f7cf0d83 Release 2.0.4 2021-12-12 18:57:38 +00:00
5ac8f4c81b Update changelog 2021-12-12 18:55:41 +00:00
c33c9082d9 [Docs] Add Flatpak install links 2021-12-12 18:39:41 +00:00
62ae0f5ef6 [Docs] Update install guide
Rewrite install instructions to include more details on Deluge 2.0
install.

* Added other Linux distros
* Added stable PPA details
* Added link to forum for Windows and macOS community packages

Co-authored-by: Sergio M <sergio@example.com>
Co-authored-by: Ofry Linkovsky <15746116+OfryL@users.noreply.github.com>
Closes: deluge-torrent/deluge#296
Closes: deluge-torrent/deluge#310
2021-12-12 18:27:05 +00:00
24aa48187e [Docs] Replace recommonmark with MyST parser
We used recommonmark so that we can use markdown in sphinx but it is
buggy and now so switch to better supported MyST-parser.

* Fixed incorrect heading warnings in markdown.
* Added sphinx toctree to markdown using directive as required by MyST.
* Upgraded Sphinx to 4.3

Ref: https://myst-parser.readthedocs.io
2021-12-12 18:16:21 +00:00
342cca4367 [GTKUI] Open tracker edit with double click
Closes #2434
Closes: deluge-torrent/deluge#253
2021-11-26 07:29:34 +00:00
9194092d7b [GTKUI] Support using the Ayatana fork of indicators.
As this fork is maintained in Debian, and as of Impish/21.10 is the supported
variant in Ubuntu as well.

Closes: deluge-torrent/deluge#317
2021-11-24 19:49:49 +00:00
5f6f65a065 [GTKUI] Add "Last Transfer" column
Closes: deluge-torrent/deluge#255
2021-11-23 20:45:09 +00:00
967537a409 [GTK3UI] Allow escape key to close Create Torrent dialog 2021-11-23 20:07:44 +00:00
f74163489c [Packaging] Fix gtk3 glade path in MANIFEST
Missed path update when moving from gtk2 to gtk3
2021-11-23 20:07:33 +00:00
7a110bd60f [Plugins] Fix allow enabling any plugin Python version
Properly fix allow enabling any plugin Python version, first attempted
in previous commit 3433a91

Closes: deluge-torrent/deluge#316
2021-11-08 19:27:24 +00:00
d56636426e [GTKUI] Added detection of torrent URL on GTK UI focus
In case deluge GTK gets focus with a new torrent URL on the clipboard,
the "Add Torrent from URL" dialog will pop up automatically

Closes: deluge-torrent/deluge#306
2021-10-03 19:34:09 +01:00
de4fbd2e82 [Core] Workaround torrent file_progress lt 2.0 error
Workaround lt 2.0 python bindings error when calling a torrent handle
file_progress:

```
Boost.Python.ArgumentError: Python argument types in
    torrent_handle.file_progress(torrent_handle)
did not match C++ signature:
    file_progress(libtorrent::torrent_handle {lvalue}, libtorrent:🎏:bitfield_flag<unsigned char, libtorrent::file_progress_flags_tag, void> flags=0)
```

Should be fixed in 2.0.5 release: https://github.com/arvidn/libtorrent/commit/3feba04e6d
2021-10-03 18:53:31 +01:00
9c3982d4ff [GTKUI] Fix piecesbar crashing when enabled
When enabled in preferences the piecesbar was crashing the application.

This was narrowed down to an issue with text_font property and there was a
suggestion that the PangoFontDescription should be freed after getting
the result from get_style_context.

Fixed by creating a copy of the PangoFontDescription

Refs:

 * https://trac.wxwidgets.org/ticket/15697#comment:1
2021-10-03 17:20:22 +01:00
88fc21e993 [Stats] Fix cairo error and failing tests
Fixed the Stats GTKUI test not updated to the new GTK3 dir layout.

Fixed pygobject cairo ImageSurface error:

    AttributeError: 'gi.repository.cairo' object has no attribute 'ImageSurface'

The documentation seems to suggest that using `import cairo` is the correct
usage and this fixed the issue, along with adding suggested gi.require_foreign
call.

References:

https://pygobject.readthedocs.io/en/latest/guide/cairo_integration.html
2021-10-03 14:22:11 +01:00
54674576db [UI] Remove num_blocks_cache_hits usage for stats
Removed in libtorrent v2: https://github.com/arvidn/libtorrent/commit/569d4
2021-10-03 14:19:40 +01:00
89189adb24 [Core] Stop using removed disk.num_blocks_cache_hits stat
Removed in libtorrent v2.0.0:
569d47c391
2021-10-03 14:19:17 +01:00
a5a7da4a1a [Core] Use external_address in external_ip alert handler
Unit tests are segfaulting and this is a result of a subsequent call to
pop_alert creating a dangling pointer to alert.message method.

Replace alert.message call with correct external_address property that
doesn't require any parsing and wonder trigger segfault to expired
pointer.

https://github.com/arvidn/libtorrent/issues/6437
2021-10-03 14:16:57 +01:00
1e6cc03946 [Lint] Fix spelling mistakes
A quick fix of some of the mistakes caught by codespell.
Updated readme with new IRC server

Useful to add it as part of linting checks.
2021-09-21 21:43:53 +01:00
d8526ba653 [UI] Add magnet icons for copy and add actions
Added new magnet icons with a consistent naming scheme
Run script to update all icons

Co-authored-by: Matias Wilkman <matias.wilkman@gmail.com>
2021-09-21 20:38:08 +01:00
c38f913948 [WebUI] Add menu option to copy magnet URI 2021-09-21 20:35:24 +01:00
0659fe4641 [Core] Export torrent get_magnet_uri method
Returns a generated magnet uri from torrent info
2021-09-21 20:27:38 +01:00
10501db63d [i18n] Update translation PO files from Launchpad 2021-09-14 22:02:56 +01:00
2a312159b9 [GTKUI] Fix unhandled error with empty clipboard
If the primary clipboard was empty the fallback resulted in an unhandled
error due to missing arguments.

Fixed by using SELECTION_PRIMARY as fallback clipboard
2021-09-14 21:56:03 +01:00
cb75192df4 [CI] Add core dump capture to GH job
Add further debugging to trace segfaults with lt 1.2
2021-09-10 19:06:21 +01:00
588f600ba2 [#3310|Core] Change logging invalid session status key to debug
Users were complaining about logs being flooded with `Session status key not valid`
which was a result of the Stats plugin using the wrong status keys.

Fixed by changing to debug log level since not useful in warning level
if spamming.
2021-08-29 16:07:53 +01:00
ea609cd3e0 [#3310|Stats] Fix constant session status key warnings
Fixed logs flooded with:

    [WARNING ][deluge.core.core              :655 ] Session status key not valid: num_connections
    [WARNING ][deluge.core.core              :655 ] Session status key not valid: dht_cache_nodes
2021-08-29 16:05:36 +01:00
4b6c7d01b2 [#3478|Core] Fix loading magnet with resume_data and no metadata
Since libtorrent 1.2.10 magnets save resume_data even with metadata not
yet downloaded. Unfortunately when using the deprecated
add_torrent_params key resume_data results in an error  "missing
info-hash from URI"

The problem is due to lt session requiring an info_hash in
add_torrent_params but resume_data does not set or override this key and
resume_data overrides the add_torrent_params.url with an empty string.

The workaround is to specify the info_hash in add_torrent_params. We
require sha1_hash object or bytes and use of bytearray to maintain
python2 compatability.

https://dev.deluge-torrent.org/ticket/3478
2021-08-29 15:58:48 +01:00
b89b2c45b1 [Console] Fix using windows-curses on Windows
The console tests are still failing on Windows due to an issue where the
sys args are not being correctly replaced in the tests so the pytest
args are being passed to console.
2021-08-01 08:48:27 +01:00
e38f1173cf [Notifications] Fix email KeyError with status name
Fix user reported error:

    Notification failure using email:
    [Failure instance: Traceback: <class 'KeyError'>: 'name'

This was due to using empty dict used with get_status where a list of
keys is now required or the all_keys parameter is used.

Fixes: #3303
2021-08-01 08:43:37 +01:00
e1e0999de6 [Tests] Fix skipping torrent test for libtorrent >= 1.2
Test is still failing with libtorrent 2 so also skip.
2021-08-01 08:35:17 +01:00
5c9378ac5e [Tests] Fix incorrent twisted defer import
With the release Twisted 21.7.0 there was an import error running the
tests due to defer incorrectly imported via twisted.internet.tasks module.
2021-07-31 22:08:23 +01:00
f075f391cb [CI] Add catchsegv to get a backtrace for segfaults
Encountering random libtorrent segfault with GitHub action so add
catchsegv when running tests to get more information.
2021-07-31 22:08:23 +01:00
8fb25f71f3 [Install] Update and fix python optional requirements
* Added required dependency setuptools to install_requires
* Remove optional dependency ipaddress from install_requires
* Created extras_require in setup.py. The optional dependencies should
not be included in install_requires so that users can either install
forked dependencies or remove problematic ones. Updated documentation to
detail how to install these optional dependencies.
* Fixed README badge

Refs:
 * https://dev.deluge-torrent.org/ticket/3470
 * https://dev.deluge-torrent.org/ticket/3282
 * https://dev.deluge-torrent.org/ticket/3353
2021-07-31 22:08:23 +01:00
a3332079db [GTKUI] Remove deprecated GTK attributes 2021-07-25 17:42:05 +01:00
0d6eec7a33 [i18n] Refactor loading libintl library
Handle different names for libintl library on MacOS and Windows with
fallback.
2021-07-25 16:47:14 +01:00
f16afc59ba Ignore TypeError with custom Twisted logging
The modification of Python logging _findCaller args in Python 3.8 raises
TypeError in our custom Twisted Logger with Twisted <= 19 versions.

The actual issue for the custom logger was fixed in 18.9 so added a
version check to avoid usage.

Refs:
 - https://twistedmatrix.com/trac/ticket/7927
 - 6b894744e4
2021-07-25 13:28:18 +01:00
e5388048a9 [CI/CD] Add github actions to replace Travis
Due to new limitations for open-source projects on Travis we are
switching to GitHub actions.

* Notes about system site-packages

We had many problems with accessing system python packages on Travis for
libtorrent and GTK and the problems are harder on Github since there is
no more access. For now copying the python libtorrent binary into the
deluge source is the workaround. There is a pip package that could be
used in future.

Fixed failing tests with libtorrent 1.2 which required a non-zero length
file in torrent and workarounds for async alert delay.
2021-07-25 13:27:26 +01:00
5374d237a7 [AutoAdd|3295] Correctly fix auto-adding magnets
Properly fix adding magnets, first attempted in previous commit 2e466101fc

add_torrent_magnet does not return a deferred so wrap in maybeDeferred.

Fixed broken test due to new deluge website icon
2021-07-24 11:25:25 +01:00
2e466101fc [AutoAdd] Fix magnet missing applied labels
The autoadd function does not apply labels to torrents that are added via magnet files.
Those magnet files are also renamed ".Magnet.Invalid".

Here are two threads discussing the issue, which still exists.

    https://forum.deluge-torrent.org/viewtopic.php?t=55539
    https://dev.deluge-torrent.org/ticket/3295

Here is what Deluged.log shows when the problem occurs:

21:51:38 [ERROR   ][deluge_autoadd.core           :333 ] Cannot Autoadd magnet: /Torrents/TorrentFiles/FileName.magnet: Torrent already in session (e1e0f33b656cb74532dcddc04f2ec52771ef1c26).
21:56:38 [ERROR   ][deluge_autoadd.core           :333 ] Cannot Autoadd magnet: /Torrents/TorrentFiles/FileName2.magnet: Torrent already in session (ef839d84d113cc35719b6fd616a4d8e220de7d32).

After looking at the code, what appears to be happening is the magnet link is added, but then a second scan of the folder occurs. Since the magnet file was never renamed, it will attempt to add it again, error out, then rename the file "magnet.invalid".

The only difference between the torrents working properly and magnets having the issue is the two lines I copy-pasted into the magnet IF statement. This should resolve the issue.
2021-04-17 17:24:58 +01:00
8676a0d2a0 [Core] Improve on_alert_tracker_error for lt >= 1.2
libtorrent 1.2 added endpoint struct to each tracker, to prevent false
updates we will need to verify that at least one endpoint to the errored
tracker is working.  if there is at least one working, it will not set
the tracker status to
error and set it to `Announce OK`. otherwise, it will use the error
message from the alert.

Refs: https://dev.deluge-torrent.org/ticket/3384
2021-04-17 16:55:54 +01:00
3ec23ad96b [#3388|WebUI] Fix md5sums in torrent files breaking file listing
Torrents containing md5sum optional hashes are not being decoded and so
causes errors in the json_api when the TorrentInfo is returned:

    Object of type bytes is not JSON serializable

Fixed by removing all optional hashes from the paths returned from
TorrentInfo and only including the required path keys. The optional
hashes are unused by Deluge so simplify by removing.

Fixed Windows path issue in TorrentInfo by ensuring conversion to posix paths.

Refs:

http://wiki.bitcomet.com/inside_bitcomet
http://wiki.depthstrike.com/index.php/P2P:Protocol:Specifications:Optional_Hashes
https://wiki.theory.org/index.php/BitTorrentSpecification
2021-03-24 10:26:08 +00:00
dcd3918f36 [WebUI] Add test for torrent files containing md5sums
Some torrent files built with py3createtorrent fail to produce a
file listing in the WebUI when uploading them.
This made it impossible to add such files.
Specifically this is caused by the additional metadata when using
py3createtorrent with the `--md5` flag.
2021-03-24 10:26:08 +00:00
08c7f1960f [CI/CD] Fix Tox SSL error in Windows Travis job
The tox sdist-make step failed with SSL: CERTIFICATE_VERIFY_FAILED so
fix by install certifi to ensure updated SSL certificates are available.
2021-03-24 10:25:30 +00:00
8a4ec493c0 [CI/CD] Add Travis windows build
* Added APPDATA to tox passenv so it is available to common module.
* Fixed windows path issue in httpdownloader tests
* Skipped torrentmanager test due to the following error from loading a
Linux pickled state file with a different line ending.
    ModuleNotFoundError: No module named 'deluge.core.torrentmanager\r'
* Removed appveyor build
2021-02-23 10:41:46 +00:00
4d970754a4 [#3440] Fix httpdownloader reencoding torrent file downloads
Torrent downloads from rutracker responds with the header:

    Content-Type: application/x-bittorrent; charset=Windows-1251

The problem is that httpdownloader was using the charset to re-encode
the downloaded file, corrupting the binary torrent file download.

Fixed by only re-encoding text content types, since it is very rare
that non-text content types would actually have a non-utf8 codeset and
if there is a requirement we would need to determine it on a type by
type basis.
2021-02-20 21:18:32 +00:00
f331b6c754 [GTKUI] Fix torrentdetails tab bar position not saving
The GTKUI tests were failing and the saved config for the tab bar
position was not being restored.

Fixed by moving the setting of notebook.tabs_pos to TorrentDetail init.

Replaced more deprecated methods that were showing up in tests.
2021-02-20 17:29:36 +00:00
1022448e4f [#3441|GTKUI] Add a torrentdetails tabs position menu
The tabs placement for the torrentdetails notebook might not be to
everyone's liking so add a menu item to configure it.

Default the position back to top.
2021-02-20 15:22:08 +00:00
291540b601 Hide pygame community banner in console
Notifications plugin uses pygame for sound notifications however pygame
show a console message "Hello from the pygame community." whenever
starting deluge from console.

Refs: https://stackoverflow.com/a/55769463/175584
2021-02-20 14:11:14 +00:00
092d07b68e [#3337|Core] Fix lt listen_interfaces not comma-separated
A typo meant that the interfaces supplied to libtorrent were not
comma-separated.

Refs: https://github.com/arvidn/libtorrent/blob/RC_1_1/include/libtorrent/string_util.hpp#L70
2021-02-06 17:26:30 +00:00
da5d5bee20 [#3325|Core] Fix unable to remove magnet with delete_copies enabled
Users were encountering the following error while attempting to delete
magnet torrents and had the config 'Delete copy of torrent file'
enabled. This was due to removing a magnet before the metadata was
downloaded and the torrent.filename was still set to None so raises
exceptions when string operations are performed with it.

  File "/usr/lib/python3/dist-packages/deluge/core/torrent.py", line 1317, in delete_torrentfile
    os.path.join(self.config['torrentfiles_location'], self.filename)
  ...
  TypeError: join() argument must be str or bytes, not 'NoneType'

Fixed by both setting a default empty string for self.filename and only
deleting the torrent file copy if filename is set.
2021-02-05 20:33:19 +00:00
6d9dc9bd42 [WebUI] Add country flag alt/title for accessibility
This allows viewing the country in textual form by hovering the flag
image and displays it if the image couldn't be loaded.
2021-01-29 18:49:24 +00:00
937afd921c [Tests] Fix console tests sometimes failing due to hard coded port
The tests in ConsoleUIWithDaemonBaseTestCase could fail to the hard coded
port 58900 being busy.
Fix by using the proper port found in self.listen_port

Also convert unnecessary use of assertTrue where more appropriate
assert functions should be used, such as assertEqual and assertIn
2021-01-29 18:46:42 +00:00
EFS
a4da8d29f8 [WebUI] Fix tracker icon download error
Encoutering an error when webui attempts to download tracker icon:

    Error occurred downloading file from "http://b'acg.rip'/": invalid
    hostname: b'acg.rip'

Fixed by ensuring the request.tracker_name is decoded from bytes before
looking up the icon name.
2021-01-29 18:31:52 +00:00
8ec5ca9d08 [Console] Fix setting 'Skip' priority on console
Selecting priorities 'Low' and 'Skip' on console will both set the
actual priority to 'Low'.

Fixed typo in previous commit 6655fe67c3
2021-01-29 18:26:35 +00:00
9c90136f57 [#3439] Execute plugin fails to run on Windows
Fixed TypeError: a bytes-like object is required, not 'str'
2021-01-29 18:17:35 +00:00
610a1bb313 [Lint] Update pre-commit hook and isort versions
* Fixed black hook requiring Py3.6 to installed locally. Will now assume
Py3.6+ in installed.
 * Added isort traceback in pre-commit flake8 hook fails
 * Updated versions of Black, Prettier and isort
 * Keep Flake8 at 3.7.9 due to E402 issue: https://gitlab.com/pycqa/flake8/-/issues/638
 * New pyproject config for isort v5 with fixes for Python 2 imports.
 * Fixed travis config to run Python 3.6 for lint run. Replaced the
virtualenv with_system_site_packages config with Travis specific Python
config value so lint run doesn't attempt to append with_system_site_packages
to Python 3.6 command.
2021-01-24 20:40:20 +00:00
23a48dd01c [#3309|GTK] Fix cmp function for None types
Comparisons on Python 3 are much stricter resulting in the following
error comparing with None:

    TypeError: '>' not supported between instances of 'NoneType' and 'str'

Fix this by getting the type of the other value and getting it's default
value.
2020-04-30 14:30:37 +01:00
d02fa72e80 [Console] Fix hostlist status lookup errors
If a host in hostlist failed DNS lookup or other issue it was returning
a tuple instead of deferred. Fix this in hostlist by returning a
defer.succeed.

A race condition with BaseMode was also encountered when
update_hosts_status calls update_select_host_popup and
ConnectionManager does not have a rows attribute. Fix this by init
BaseMode before update_hosts_status and remove already called
update_select_host_popup.
2020-04-27 16:24:33 +01:00
62d8749e74 [#3348] Fix TypeError adding peers to torrents
Python3 has stricter type checking and passing a port as string results
in libtorrent raising a TypeError.

Fixed by casting port to int, along with refactoring to ensure ipv6 is
correctly parsing and a useful error is output to user with invalid ip
or port details.

https://dev.deluge-torrent.org/ticket/3348
2020-04-25 13:16:04 +01:00
76f0bf2e04 Ctl+Q to quit Deluge GTK without killing daemon 2020-04-25 13:08:54 +01:00
635f6d970d [Config] Fix loading config with double-quotes in string
If a password or other string contained a double-quote then the config
would fail to be loaded on startup and reset.

This occurred due to fixing a similar issue with curly braces for #3079
in commit 33e9545cd4 and the checking for double-quotes had unforseen
consequences.

To resolve both these issues the code to check for json objects in
config files was simplified and utilises the json module raw_decode
method to ensure the extracted string indexes are json objects.
2020-04-25 10:49:08 +01:00
672e3c42a8 [Tests] Add pytest markers to tox.ini
Remove pytest warnings due to unknown markers
2020-04-23 17:19:37 +01:00
c1110e4ef3 [Tests] Fix tests failing when deluged fails to listen
Commit b32c5d824 changed the logged message in deluge/core/daemon_entry.py
when libtorrent fails to listen on the given port, without updating the
trigger expression in deluge/tests/common.py:start_core to match the new output.

Fix by updating the trigger match expressions to match the new log output
2020-04-23 17:19:37 +01:00
742c8a941a [Tests] Fix PytestDeprecationWarning from pytest
Accessing pytest.config is deprecated and produces:
PytestDeprecationWarning: the pytest.config global is deprecated. Please use
request.config or pytest_configure (if you're a pytest plugin) instead.

Fix by using a pytest.fixture
2020-04-23 17:19:37 +01:00
3427ae4b90 [GTK] Remove PyGIWarning in gtk3/files_tab.py
Remove warning: PyGIWarning: Gtk was imported without specifying a version first
2020-04-23 17:17:20 +01:00
034db27936 [GTK] Add more width to outgoing ports spinbuttons in network preferences
The spinbuttons would sometimes be truncated.
Fix by increasing the width
2020-04-23 17:17:20 +01:00
1e3c624613 [GTK] Destroy the dialog before running the callback
Currently, the dialog window is displayed until after the callback has returned.
The result is that if a new dialog is opened from the callback, the first dialog
is still displayed until the new dialog is destroyed.

Fix by destroying the dialog before running the callback.
2020-04-23 17:17:20 +01:00
3519f341d4 [GTK] Fix showing correct error on libtorrent import error
The exception string "No module named libtorrent" was changed to
"No module named 'libtorrent'" in python 3.3, which results in a
"unknown Import Error" message being displayed instead of the
message meant for libtorrent import error.

Change to raising LibtorrentImportError in _libtorrent.py and
catch this error to display libtorrent specific import errors.
2020-04-23 17:17:20 +01:00
d6c96d6291 Fix warning related to gettext 2020-04-23 17:14:24 +01:00
15c250e152 Fix template config.ui naming in create_plugin script. 2020-04-20 00:01:16 -07:00
eb57412601 [Tests] Fix tox, pytest and travis issues
* Error occurring with Pytest 5.4 so pin to below that version.
 * Fix minor issues with Travis config.
 * Use full command-switches for pytest in tox config.
 * Remove pin for pip as issue with pip-wheel-metadata was fixed in 19.3
 * Remove tox-venv as causing issues of incompatible packages installed.
   The latest versions of the virtualenv package should handle these
   duties.
2020-04-12 17:37:42 +01:00
2f1c008a26 [Console] Fix AttributeError setting config values
GitHub user JohnDoee reported that config settings are not decoded
correctly, this error can be reproduced with a command like

    deluge-console -c /config/ "config --set download_location /downloads"

    > AttributeError: 'str' object has no attribute 'decode'

The tokenize code was using 'string-escape' to decode strings but there
is no direct replacement in Python 3 but also unnecessary. However the
tokenize code is complex and buggy and not really suitable for the task
of evaluating config values.

A better alternative is to evaluate the config values using the json
decoder with some additional logic to allow for previous syntax usage,
such as parentheses.

Added a comprehensive set of tests to check for potential config values
passed in from command line.
2019-11-28 12:38:02 +00:00
5e06aee5c8 [Logging] Fix findCaller with unknown source
In case no source was found, a 3-tuple was returned instead of a 4-tuple
in Python 3
2019-11-19 17:44:48 +01:00
351664ec07 [Logging] Fix Python 3.8 compatibility
Deluge's logger class extends Python's `logging.Logger`. Since Python
3.8, it takes an additional argument `stacklevel`.
The implementation in Deluge does not support that. Work around the
problem by ignoring additional arguments.
2019-11-19 17:44:47 +01:00
5f1eada3ea [Tests] Skip buggy pytest 5.2.3
Plugins test fails due to:

https://github.com/pytest-dev/pytest/issues/6194
2019-11-15 20:55:22 +00:00
bde4e4443e [Lint] Fix Black and Flake8 issues
For a single element unpack black now also encloses with parentheses to
make it clearer: https://github.com/psf/black/issues/1108

Fix flake8 warnings
2019-11-13 15:44:46 +00:00
ed4bc5fa17 [Core] Fix potential "dictionary changed size during iteration" on shutdown 2019-11-12 15:40:38 +00:00
20afc31f3c [Docs] Fix changlog symlink and markdown issue 2019-11-12 15:37:12 +00:00
9232a52fd6 [Docs] Update dev environment instructions
I'm going through these instructions on a clean Ubuntu 19.04 VM

These are the changes I needed to make to get Deluge to build/run
2019-11-12 15:36:52 +00:00
23b3f144fc [#3298|Core] Fix pickle loading non-ascii state error
When trying to load a torrents.state from version 1.3 users were
encountering the following error:

    UnicodeDecodeError: 'ascii' codec can't decode byte

This was due to the way that Python 2 was pickling state with torrent
filenames that contained non-ascii characters and Python 3 was
unpickling the state using ascii encoding and failing. The fix is to
specify utf-8 encoding when loading torrents.state.
2019-11-12 15:21:56 +00:00
89d62eb509 [GTK] Remove orphaned code
Changes were made to sidebar theming in commit 5a6f202 and this code was
forgotten to be removed.
2019-10-31 10:56:08 +00:00
00176ee2cd [Docs] Typo corrections in testing.md 2019-10-31 10:04:30 +00:00
8737005b82 [GTK] Fix typo in preferences language label 2019-10-31 10:01:40 +00:00
d08c3f72e9 Fix privilege dropping when setting process ownership
`os.setgid()` should be called to set the GID, and it should be called
before `os.setuid()` to prevent reinstatement of privileges.
2019-10-31 09:57:33 +00:00
40ebdf3f39 [GTK] Add missing translation markup
Found some text needing marked for translation.
2019-10-31 09:40:59 +00:00
eeeb7fb69b [GTK] Fix Status tab download speed and uploaded
Previous work on the status tab caused these labels to not be in the
correct position so this commit swaps them back..
2019-10-31 09:38:37 +00:00
3f9ae33793 [Label] Fix Options/Add windows not reopening
When a user clicked ESC key or X button, the Options and Add windows
didn't open again. This happened because the windows were closed and
not hidden, which deleted the instance of those windows.

This fix changed the behavior of the close action to 'hide'.
2019-06-25 11:51:59 +01:00
0c7f53e305 [WebUI] Fix class-header for Deluge.EditTrackersWindow 2019-06-25 11:51:59 +01:00
63a4301a8b [Notifications] Fix unhandled TypeErrors on Python 3
- Notify requires GLib.Variant for set_hint
- Twisted defer.fail only accepts Exceptions.

Fixes: #3267
2019-06-25 10:44:51 +01:00
1b4ac88ce7 [Common] Fix creation of pidfile via command option
Python 3 raises a TypeError for binary file mode and writing text string.

Fixes: #3278
2019-06-25 10:39:49 +01:00
4b29436cd5 [Core] Fix for peer.client UnicodeDecodeError
Some users have been reporting unhandled UnicodeDecodeErrors and the
traces show it occuring in the call to `peer.client`. Although unable to
replicate it seems prudent to put a try..except around the call to
ensure it does not break the UIs.

Refs: https://github.com/arvidn/libtorrent/issues/3858

Closes: #3279
2019-06-24 16:34:15 +01:00
833b5a1f30 [Common] Fix show_file unhandled dbus error
If dbus org.freedesktop.FileManager1 service is missing then show_file
raised an unhandled exception. The service is not available on certain
desktop environments e.g. i3wm.

The solution is to fallback to xdg-open.

Fixes: #3272
2019-06-24 11:44:29 +01:00
24b094a04a [WebUI] Handle torrent add failures
Closes #2253.
2019-06-21 09:09:12 +03:00
3365201011 [Docs] Fixes for spelling
Running on Ubuntu Xenial results in spelling warnings so update wordlist.
2019-06-18 09:07:48 +01:00
c1ba403d4e [Docs] Add service how-tos 2019-06-18 09:07:48 +01:00
8b62e50eb8 [Docs] Add spellchecking with pyenchant
- Use sphinxcontrib.spelling with custom wordlist.
- Skip the checking of the modules documents as they raise
false-positives.
- Add a setup.py spellcheck_docs command.
- Fix spelling and other issues.
- Add a doc favicon.
2019-06-15 21:06:27 +01:00
5b315e90c5 [Docs] Cleanup updating plugin page 2019-06-15 21:01:04 +01:00
b711cd258a Release 2.0.3 2019-06-12 18:47:11 +01:00
e1c4069a72 [Gtk] Refactor presenting window
Include the correct usage for other display servers.

Still not sure how to get the proper timestamp for Wayland or Quartz but
I read that using 0 equals the GDK_CURRENT_TIME which suffices for now.
2019-06-12 16:57:48 +01:00
a2dee79439 [GTK] Improve detecting X11 display server
GdkX11 still imports on Wayland so check display server is X11 before
importing.
2019-06-12 16:05:15 +01:00
7a54db3179 [Docs] Fix typo and url for Windows install 2019-06-12 14:56:21 +01:00
03e7952d26 [GTK] Only import wnck on X11 display
Wnck is only supported on X11 and raises errors in Wayland so only load
it when X11 present.

Fixes: #3265
2019-06-12 10:21:23 +01:00
7ee8750be4 [GTK] Fix peers tab flag tooltip error
Hovering over a country flag resulted in an AttributeError.

This is due to get_tooltip_context now returning a bool value instead of
the tooltip object.

Fixes: #3219
2019-06-12 09:40:51 +01:00
f61001a15d [GTK] Fix missing argument for GtkMenu.popup()
Missed while converting from pygtk to Gtk3

Fixes: #3266
2019-06-12 09:40:14 +01:00
86ddadacf7 [Extractor] Fix startup error
On Python 3 need to create a copy of the dict to iterate

Fixes: #3264
2019-06-12 09:40:14 +01:00
632089940c [Web] Fix unable to change password
The hashlib update method requires bytes and raised a TypeError for salt
passed as text.

Added a test for auth change_password

Fixes: #3262
2019-06-11 20:14:11 +01:00
5d7db3e727 [Web] Change request.base path encoding to utf-8
A user reported a problem with setting base path resulting in this error:

    encoding with 'idna' codec failed (UnicodeError: label too long)

It is likely the base path is longer than 63 chars, which is unusual,
however the idna codec is for domain name not paths so switch to utf-8.

Fixes: #3261
2019-06-11 20:14:11 +01:00
4dd1f63b8b [Web] Fix TypeError with reverse proxy x-deluge-base header
The request header needs decoded otherwise string comparisons fail.

Fixes: #3260
2019-06-11 20:14:11 +01:00
fc134cdffb [Docs] Add more info to release notes 2019-06-11 20:14:11 +01:00
36cb4c5a4f [Docs] Updates to release checklist 2019-06-11 20:14:11 +01:00
676bdb26e0 [Docs] Remove incomplete Windows install instructions
The instructions are in-progress and missing steps so instead point to
the issue ticket for now.
2019-06-11 12:35:04 +01:00
dff778ceeb [i18n] Try loading intl.dll on Windows 2019-06-11 12:35:04 +01:00
bdadd2b515 Fix typo in install instructions for macOS 2019-06-11 12:35:04 +01:00
a34543100c [Web] Fix peers tab failing to set flag location
The request.country returns bytes not a string so decode.
2019-06-11 12:35:04 +01:00
b8b044f451 [lint] Fix pre-commit config key name 2019-06-10 14:24:47 +01:00
493 changed files with 320512 additions and 152179 deletions

116
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,116 @@
name: CI
on:
push:
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
test-linux:
runs-on: ubuntu-20.04
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.8"
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-pip-${{ hashFiles('tox.ini', 'setup.py', 'requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Add libtorrent deb repository
uses: myci-actions/add-deb-repo@8
with:
repo: deb http://ppa.launchpad.net/libtorrent.org/1.2-daily/ubuntu focal main
repo-name: libtorrent
keys: 58E5430D9667FAEFFCA0B93F32309D6B9E009EDB
key-server: keyserver.ubuntu.com
install: python3-libtorrent-dbg
- name: Sets env var for security
if: (github.event_name == 'pull_request' && contains(github.event.pull_request.body, 'security_test')) || (github.event_name == 'push' && contains(github.event.head_commit.message, 'security_test'))
run: echo "SECURITY_TESTS=True" >> $GITHUB_ENV
- name: Install dependencies
run: |
pip install --upgrade pip wheel
pip install -r requirements.txt -r requirements-tests.txt
pip install -e .
- name: Install security dependencies
if: contains(env.SECURITY_TESTS, 'True')
run: |
wget -O- $TESTSSL_URL$TESTSSL_VER | tar xz
mv -t deluge/tests/data testssl.sh-$TESTSSL_VER/testssl.sh testssl.sh-$TESTSSL_VER/etc/;
env:
TESTSSL_VER: 3.0.6
TESTSSL_URL: https://codeload.github.com/drwetter/testssl.sh/tar.gz/refs/tags/v
- name: Setup core dump directory
run: |
sudo mkdir /cores/ && sudo chmod 777 /cores/
echo "/cores/%E.%p" | sudo tee /proc/sys/kernel/core_pattern
- name: Test with pytest
run: |
ulimit -c unlimited # Enable core dumps to be captured
cp /usr/lib/python3/dist-packages/libtorrent*.so $GITHUB_WORKSPACE/deluge
python -c 'from deluge._libtorrent import lt; print(lt.__version__)';
catchsegv python -X dev -m pytest -v -m "not (todo or gtkui)" deluge
- uses: actions/upload-artifact@v2
# capture all crashes as build artifacts
if: failure()
with:
name: crashes
path: /cores
test-windows:
runs-on: windows-latest
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.7"
- name: Cache pip
uses: actions/cache@v2
with:
path: '%LOCALAPPDATA%\pip\Cache'
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-pip-${{ hashFiles('tox.ini', 'setup.py', 'requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel
python -m pip install libtorrent==1.2.*
pip install -r requirements.txt -r requirements-tests.txt
pip install -e .
- name: Test with pytest
run: |
python -c 'import libtorrent as lt; print(lt.__version__)';
pytest -m "not (todo or gtkui or security)" deluge

45
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: Docs
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-python@v2
with:
python-version: "3.8"
- name: Cache pip
uses: actions/cache@v2
with:
# This path is specific to Ubuntu
path: ~/.cache/pip
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-pip-${{ hashFiles('requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Install dependencies
run: |
pip install --upgrade pip wheel
pip install tox
sudo apt-get install enchant
- name: Test with tox
env:
TOX_ENV: docs
run: |
tox -e $TOX_ENV

17
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Linting
on:
push:
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- name: Run pre-commit linting
uses: pre-commit/action@v2.0.2

2
.gitignore vendored
View File

@ -22,3 +22,5 @@ deluge/ui/web/js/extjs/ext-extensions*.js
osx/app
RELEASE-VERSION
.venv*
# used by setuptools to cache downloaded eggs
/.eggs

View File

@ -1,4 +1,4 @@
default_language:
default_language_version:
python: python3
exclude: >
(?x)^(
@ -6,28 +6,29 @@ exclude: >
)$
repos:
- repo: https://github.com/ambv/black
rev: 19.3b0
rev: 20.8b1
hooks:
- id: black
name: Fmt Black
language_version: python3.6
- repo: https://github.com/prettier/prettier
rev: 1.17.0
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
hooks:
- id: prettier
name: Fmt Prettier
# Workaround to list modified files only.
args: [--list-different]
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.7
# v3.7.9 due to E402 issue: https://gitlab.com/pycqa/flake8/-/issues/638
rev: 3.7.9
hooks:
- id: flake8
name: Chk Flake8
additional_dependencies:
- flake8-isort==2.7
- pep8-naming==0.8.2
- flake8-isort==4.0.0
- pep8-naming==0.11.1
args: [--isort-show-traceback]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.2.1
rev: v3.4.0
hooks:
- id: double-quote-string-fixer
name: Fix Double-quotes

View File

@ -289,7 +289,7 @@ callbacks=cb_,_cb
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,future.builtins,future_builtins
redefining-builtins-modules=
[TYPECHECK]
@ -359,11 +359,6 @@ known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
[DESIGN]

View File

@ -1,83 +0,0 @@
dist: xenial
sudo: required
language: python
python:
# Travis Xenial Python to support system_site_packages
- 3.5
cache: pip
virtualenv:
system_site_packages: true
env:
global:
- DISPLAY=:99.0
git:
# Set greater depth to get version from tags.
depth: 1000
matrix:
include:
- name: Unit tests
env: TOX_ENV=py3
- name: Unit tests - libtorrent 1.2
env: TOX_ENV=py3
addons:
apt:
sources: [sourceline: "ppa:libtorrent.org/1.2-daily"]
packages: [python3-libtorrent, python3-venv]
- name: Unit tests - Python 2
env: TOX_ENV=py27
python: 2.7
- if: commit_message =~ SECURITY_TEST
env: TOX_ENV=security
- name: Code linting
env: TOX_ENV=lint
- name: Docs build
env: TOX_ENV=docs
- name: GTK unit tests
env: TOX_ENV=gtkui
- name: Plugins unit tests
env: TOX_ENV=plugins
addons:
apt:
sources:
- sourceline: "ppa:libtorrent.org/rc-1.1-daily"
- deadsnakes
packages:
- python-libtorrent
- python3-libtorrent
# Install py36 specifically for pre-commit to run black formatter.
- python3.6
# Intall python3-venv to provide ensurepip module for tox.
- python3-venv
# Install dependencies
install:
- pip install tox tox-venv
# GTKUI tests
- "if [ $TOX_ENV == 'gtkui' ]; then
sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0;
fi"
# Security tests
- "if [ $TOX_ENV == 'security' ]; then
testssl_url=https://github.com/drwetter/testssl.sh/archive/v2.9.5-5.tar.gz;
wget -O- $testssl_url | tar xz
&& mv -t deluge/tests/data testssl.sh-2.9.5-5/testssl.sh testssl.sh-2.9.5-5/etc/;
fi"
before_script:
- export PYTHONPATH=$PYTHONPATH:$PWD
# Verify libtorrent installed and version
- python -c "import libtorrent as lt; print(lt.__version__)"
# Start xvfb for the GTKUI tests
- "if [ $TOX_ENV == 'gtkui' ]; then
/sbin/start-stop-daemon --start --quiet --background \
--make-pidfile --pidfile /tmp/custom_xvfb_99.pid \
--exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16;
fi"
script:
- tox -e $TOX_ENV

17
AUTHORS
View File

@ -39,14 +39,9 @@ Images Authors:
* files: deluge/ui/data/pixmaps/*.svg, *.png
deluge/ui/web/icons/active.png, alert.png, all.png, checking.png, dht.png,
downloading.png, inactive.png, queued.png, seeding.png, traffic.png
exceptions: deluge/ui/data/pixmaps/deluge.svg and derivatives
copyright: Andrew Resch
license: GPLv3
* files: deluge/ui/data/pixmaps/deluge.svg and derivatives
deluge/ui/web/icons/apple-pre-*.png, deluge*.png
deluge/ui/web/images/deluge*.png
copyright: Andrew Wedderburn
deluge/ui/web/icons/apple-pre-*.png, deluge*.png
copyright: Calum Lind
license: GPLv3
* files: deluge/plugins/blocklist/blocklist/data/*.png
@ -55,11 +50,9 @@ Images Authors:
license: GPLv2
url: http://ftp.acc.umu.se/pub/GNOME/sources/gnome-icon-theme
* files: deluge/ui/data/pixmaps/magnet.png
copyright: Woothemes
license: Freeware
icon pack: WP Woothemes Ultimate
url: http://www.woothemes.com/
* files: deluge/ui/data/pixmaps/magnet*.svg, *.png
copyright: Matias Wilkman
license:
* files: deluge/ui/data/pixmaps/flags/*.png
copyright: Mark James <mjames@gmail.com>

View File

@ -1,5 +1,109 @@
# Changelog
## 2.1.0 (WIP)
- Removed Python 2 support.
## 2.0.5 (2021-12-15)
### WebUI
- Fix js minifying error resulting in WebUI blank screen.
- Silence erronous missing translations warning.
## 2.0.4 (2021-12-12)
### Packaging
- Fix python optional setup.py requirements
### Gtk UI
- Add detection of torrent URL on GTK UI focus
- Fix piecesbar crashing when enabled
- Remove num_blocks_cache_hits in stats
- Fix unhandled error with empty clipboard
- Add torrentdetails tabs position menu (#3441)
- Hide pygame community banner in console
- Fix cmp function for None types (#3309)
- Fix loading config with double-quotes in string
- Fix Status tab download speed and uploaded
### Web UI
- Handle torrent add failures
- Add menu option to copy magnet URI
- Fix md5sums in torrent files breaking file listing (#3388)
- Add country flag alt/title for accessibility
### Console UI
- Fix allowing use of windows-curses on Windows
- Fix hostlist status lookup errors
- Fix AttributeError setting config values
- Fix setting 'Skip' priority
### Core
- Add workaround libtorrent 2.0 file_progress error
- Fix allow enabling any plugin Python version
- Export torrent get_magnet_uri method
- Fix loading magnet with resume_data and no metadata (#3478)
- Fix httpdownloader reencoding torrent file downloads (#3440)
- Fix lt listen_interfaces not comma-separated (#3337)
- Fix unable to remove magnet with delete_copies enabled (#3325)
- Fix Python 3.8 compatibility
- Fix loading config with double-quotes in string
- Fix pickle loading non-ascii state error (#3298)
- Fix creation of pidfile via command option
- Fix for peer.client UnicodeDecodeError
- Fix show_file unhandled dbus error
### Documentation
- Add How-to guides about services.
### Stats plugin
- Fix constant session status key warnings
- Fix cairo error
### Notifications plugin
- Fix email KeyError with status name
- Fix unhandled TypeErrors on Python 3
### Autoadd plugin
- Fix magnet missing applied labels
### Execute plugin
- Fix failing to run on Windows (#3439)
## 2.0.3 (2019-06-12)
### Gtk UI
- Fix errors running on Wayland (#3265).
- Fix Peers Tab tooltip and context menu errors (#3266).
### Web UI
- Fix TypeError in Peers Tab setting country flag.
- Fix reverse proxy header TypeError (#3260).
- Fix request.base 'idna' codec error (#3261).
- Fix unable to change password (#3262).
### Extractor plugin
- Fix potential error starting plugin.
### Documentation
- Fix macOS install typo.
- Fix Windows install instructions.
## 2.0.2 (2019-06-08)
### Packaging
@ -8,13 +112,13 @@
### Core
- Fix Python 2 compatiblity issue with SimpleNamespace.
- Fix Python 2 compatibility issue with SimpleNamespace.
## 2.0.1 (2019-06-07)
### Packaging
- Fix setup.py build error without git installed.
- Fix `setup.py` build error without git installed.
## 2.0.0 (2019-06-06)
@ -32,37 +136,37 @@
there to allow acting upon them.
- Updated SSL/TLS Protocol parameters for better security.
- Make the distinction between adding to the session new unmanaged torrents
and torrents loaded from state. This will break backwards compatability.
and torrents loaded from state. This will break backwards compatibility.
- Pass a copy of an event instead of passing the event arguments to the
event handlers. This will break backwards compatability.
event handlers. This will break backwards compatibility.
- Allow changing ownership of torrents.
- File modifications on the auth file are now detected and when they happen,
the file is reloaded. Upon finding an old auth file with an old format, an
upgrade to the new format is made, file saved, and reloaded.
- Authentication no longer requires a username/password. If one or both of
these is missing, an authentication error will be sent to the client
which sould then ask the username/password to the user.
which should then ask the username/password to the user.
- Implemented sequential downloads.
- Provide information about a torrent's pieces states
- Add Option To Specify Outgoing Connection Interface.
- Fix potential for host_id collision when creating hostlist entries.
### GtkUI
### Gtk UI
- Ported to GTK3 (3rd-party plugins will need updated).
- Allow changing ownership of torrents.
- Host entries in the Connection Manager UI are now editable.
- Implemented sequential downloads UI handling.
- Add optional pieces bar instead of a regular progress bar in torrent status tab.
- Make torrent opening compatible with all unicode paths.
- Make torrent opening compatible with all Unicode paths.
- Fix magnet association button on Windows.
- Add keyboard shortcuts for changing queue position:
- Up: Ctrl+Alt+Up
- Down: Ctrl+Alt+Down
- Top: Ctrl+Alt+Shift+Up
- Bottom: Ctrl+Alt+Shift+Down
- Up: `Ctrl+Alt+Up`
- Down: `Ctrl+Alt+Down`
- Top: `Ctrl+Alt+Shift+Up`
- Bottom: `Ctrl+Alt+Shift+Down`
### WebUI
### Web UI
- Server (deluge-web) now daemonizes by default, use '-d' or '--do-not-daemonize' to disable.
- Fixed the '--base' option to work for regular use, not just with reverse proxies.
@ -70,7 +174,7 @@
### Blocklist Plugin
- Implemented whitelist support to both core and GTK UI.
- Implemented ip filter cleaning before each update. Restarting the deluge
- Implemented IP filter cleaning before each update. Restarting the deluge
daemon is no longer needed.
- If "check_after_days" is 0(zero), the timer is not started anymore. It
would keep updating one call after the other. If the value changed, the

View File

@ -13,7 +13,7 @@ All modules will require the [common](#common) section dependencies.
- [setuptools]
- [intltool] - Optional: Desktop file translation for \*nix.
- [closure-compiler] - Minify javascript (alternative is [slimit])
- [closure-compiler] - Minify javascript (alternative is [rjsmin])
## Common
@ -23,18 +23,17 @@ All modules will require the [common](#common) section dependencies.
- [rencode] _>= 1.0.2_ - Encoding library.
- [PyXDG] - Access freedesktop.org standards for \*nix.
- [xdg-utils] - Provides xdg-open for \*nix.
- [six]
- [zope.interface]
- [chardet] - Optional: Encoding detection.
- [setproctitle] - Optional: Renaming processes.
- [Pillow] - Optional: Support for resizing tracker icons.
- [dbus-python] - Optional: Show item location in filemanager.
#### Linux and BSD
### Linux and BSD
- [distro] - Optional: OS platform information.
#### Windows OS
### Windows OS
- [pywin32]
- [certifi]
@ -52,7 +51,7 @@ All modules will require the [common](#common) section dependencies.
- [librsvg] _>= 2_
- [libappindicator3] w/GIR - Optional: Ubuntu system tray icon.
#### MacOS
### MacOS
- [GtkOSXApplication]
@ -71,7 +70,7 @@ All modules will require the [common](#common) section dependencies.
[setuptools]: https://setuptools.readthedocs.io/en/latest/
[intltool]: https://freedesktop.org/wiki/Software/intltool/
[closure-compiler]: https://developers.google.com/closure/compiler/
[slimit]: https://slimit.readthedocs.io/en/latest/
[rjsmin]: https://pypi.org/project/rjsmin/
[openssl]: https://www.openssl.org/
[pyopenssl]: https://pyopenssl.org
[twisted]: https://twistedmatrix.com
@ -81,14 +80,12 @@ All modules will require the [common](#common) section dependencies.
[distro]: https://github.com/nir0s/distro
[pywin32]: https://github.com/mhammond/pywin32
[certifi]: https://pypi.org/project/certifi/
[py2-ipaddress]: https://pypi.org/project/py2-ipaddress/
[dbus-python]: https://pypi.org/project/dbus-python/
[setproctitle]: https://pypi.org/project/setproctitle/
[gtkosxapplication]: https://github.com/jralls/gtk-mac-integration
[chardet]: https://chardet.github.io/
[rencode]: https://github.com/aresch/rencode
[pyxdg]: https://www.freedesktop.org/wiki/Software/pyxdg/
[six]: https://pythonhosted.org/six/
[xdg-utils]: https://www.freedesktop.org/wiki/Software/xdg-utils/
[gtk+]: https://www.gtk.org/
[pycairo]: https://cairographics.org/pycairo/

View File

@ -23,7 +23,7 @@ recursive-exclude deluge/tests *.pyc
graft deluge/ui/data
recursive-exclude deluge/ui/data *.desktop *.xml
graft deluge/ui/gtkui/glade
graft deluge/ui/gtk3/glade
include deluge/ui/web/index.html
include deluge/ui/web/css/*.css

View File

@ -1,10 +1,10 @@
# Deluge BitTorrent Client
[![build-status]][travis-deluge] [![docs-status]][rtd-deluge]
[![build-status]][github-ci] [![docs-status]][rtd-deluge]
Deluge is a BitTorrent client that utilizes a daemon/client model.
It has various user interfaces available such as the GTK-UI, Web-UI and
a Console-UI. It uses [libtorrent][lt] at it's core to handle the BitTorrent
Console-UI. It uses [libtorrent][lt] at its core to handle the BitTorrent
protocol.
## Install
@ -13,10 +13,17 @@ From [PyPi](https://pypi.org/project/deluge):
pip install deluge
with all optional dependencies:
pip install deluge[all]
From source code:
python setup.py build
python setup.py install
pip install .
with all optional dependencies:
pip install .[all]
See [DEPENDS](DEPENDS.md) and [Installing/Source] for dependency details.
@ -51,13 +58,13 @@ See the [Thinclient guide] to connect to the daemon from another computer.
- [Homepage](https://deluge-torrent.org)
- [User guide][user guide]
- [Forum](https://forum.deluge-torrent.org)
- [IRC Freenode #deluge](irc://irc.freenode.net/deluge)
- [IRC Libera.Chat #deluge](irc://irc.libera.chat/deluge)
[user guide]: https://dev.deluge-torrent.org/wiki/UserGuide
[thinclient guide]: https://dev.deluge-torrent.org/wiki/UserGuide/ThinClient
[installing/source]: https://dev.deluge-torrent.org/wiki/Installing/Source
[build-status]: https://travis-ci.org/deluge-torrent/deluge.svg "Travis Status"
[travis-deluge]: https://travis-ci.org/deluge-torrent/deluge
[docs-status]: https://readthedocs.org/projects/deluge/badge/?version=develop
[rtd-deluge]: https://deluge.readthedocs.io/en/develop/?badge=develop "Documentation Status"
[build-status]: https://github.com/deluge-torrent/deluge/actions/workflows/ci.yml/badge.svg?branch=develop "CI"
[github-ci]: https://github.com/deluge-torrent/deluge/actions/workflows/ci.yml
[docs-status]: https://readthedocs.org/projects/deluge/badge/?version=latest
[rtd-deluge]: https://deluge.readthedocs.io/en/latest/?badge=latest "Documentation Status"
[lt]: https://libtorrent.org

View File

@ -1,53 +0,0 @@
environment:
PYTHON_VERSION: 3.6
PYTHON_ARCH: 64
PYTHON: "C:\\Python36-x64"
APPVEYOR_SAVE_CACHE_ON_ERROR: true
matrix:
- TOXENV: py36
pull_requests:
do_not_increment_build_number: true
install:
# If there is a newer build queued for same PR, cancel this one. Credit: JuliaLang devs
- ps:
if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "python -VV"
- if defined TOXENV (
python -m pip install tox tox_venv
) else (
python -m pip install -rrequirements.txt pygame bbfreeze pefile
)
- "SET PATH=C:\\OpenSSL-v11-Win64\\bin;%PATH%"
- openssl version -v
- python -m pip install deluge-libtorrent
- 'python -c "import libtorrent; print(libtorrent.__version__)"'
cache:
- '%LOCALAPPDATA%\pip\cache'
build: false
test_script:
- if defined TOXENV tox
# Commented out as require GTK3 to create package.
# after_test:
# - if not defined TOXENV python setup.py build && python setup.py install
# - cd %APPVEYOR_BUILD_FOLDER%\\packaging\\win32
# - if not defined TOXENV deluge-bbfreeze.py debug
# - "SET PATH=C:\\Program Files (x86)\\NSIS;%PATH%"
# - if not defined TOXENV makensis deluge-win32-installer.nsi
# - if not defined TOXENV 7z a deluge-win32.zip build-win32 "-x!*.exe"
# artifacts:
# - path: packaging\win32\deluge-win32.zip
# - path: packaging\win32\build-win32\*.exe
#on_success:
#

View File

@ -15,19 +15,22 @@ Example:
>>> from deluge._libtorrent import lt
"""
from __future__ import unicode_literals
from deluge.common import VersionSplit, get_version
from deluge.error import LibtorrentImportError
try:
import deluge.libtorrent as lt
except ImportError:
import libtorrent as lt
try:
import libtorrent as lt
except ImportError as ex:
raise LibtorrentImportError('No libtorrent library found: %s' % (ex))
REQUIRED_VERSION = '1.1.2.0'
LT_VERSION = lt.__version__
if VersionSplit(LT_VERSION) < VersionSplit(REQUIRED_VERSION):
raise ImportError(
raise LibtorrentImportError(
'Deluge %s requires libtorrent >= %s' % (get_version(), REQUIRED_VERSION)
)

View File

@ -7,8 +7,6 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import argparse
import logging
import os
@ -122,7 +120,7 @@ class DelugeTextHelpFormatter(argparse.RawDescriptionHelpFormatter):
"""
if not action.option_strings:
metavar, = self._metavar_formatter(action, action.dest)(1)
(metavar,) = self._metavar_formatter(action, action.dest)(1)
return metavar
else:
parts = []
@ -264,7 +262,7 @@ class ArgParserBase(argparse.ArgumentParser):
args = [a for a in args if a not in withhold]
options, remaining = super(ArgParserBase, self).parse_known_args(args=args)
options.remaining = remaining
# Hanlde common and process group options
# Handle common and process group options
return self._handle_ui_options(options)
def _handle_ui_options(self, options):
@ -325,22 +323,22 @@ class ArgParserBase(argparse.ArgumentParser):
# Write pid file before chuid
if options.pidfile:
with open(options.pidfile, 'wb') as _file:
with open(options.pidfile, 'w') as _file:
_file.write('%d\n' % os.getpid())
if not common.windows_check():
if options.group:
if not options.group.isdigit():
import grp
options.group = grp.getgrnam(options.group)[2]
os.setgid(options.group)
if options.user:
if not options.user.isdigit():
import pwd
options.user = pwd.getpwnam(options.user)[2]
os.setuid(options.user)
if options.group:
if not options.group.isdigit():
import grp
options.group = grp.getgrnam(options.group)[2]
os.setuid(options.group)
return options

View File

@ -9,13 +9,7 @@
# License.
# Written by Petru Paler
# Updated by Calum Lind to support both Python 2 and Python 3.
from __future__ import unicode_literals
from sys import version_info
PY2 = version_info.major == 2
# Updated by Calum Lind to support Python 3.
class BTFailure(Exception):
@ -146,10 +140,6 @@ encode_func[dict] = encode_dict
encode_func[bool] = encode_bool
encode_func[str] = encode_string
encode_func[bytes] = encode_bytes
if PY2:
encode_func[long] = encode_int # noqa: F821
encode_func[str] = encode_bytes
encode_func[unicode] = encode_string # noqa: F821
def bencode(x):

View File

@ -8,8 +8,6 @@
#
"""Common functions for various parts of Deluge to use."""
from __future__ import division, print_function, unicode_literals
import base64
import binascii
import functools
@ -27,6 +25,8 @@ import time
from contextlib import closing
from datetime import datetime
from io import BytesIO, open
from urllib.parse import unquote_plus, urljoin
from urllib.request import pathname2url
import pkg_resources
@ -38,14 +38,6 @@ try:
except ImportError:
chardet = None
try:
from urllib.parse import unquote_plus, urljoin
from urllib.request import pathname2url
except ImportError:
# PY2 fallback
from urlparse import urljoin # pylint: disable=ungrouped-imports
from urllib import pathname2url, unquote_plus # pylint: disable=ungrouped-imports
# Windows workaround for HTTPS requests requiring certificate authority bundle.
# see: https://twistedmatrix.com/trac/ticket/9209
if platform.system() in ('Windows', 'Microsoft'):
@ -81,7 +73,8 @@ TORRENT_STATE = [
# The output formatting for json.dump
JSON_FORMAT = {'indent': 4, 'sort_keys': True, 'ensure_ascii': False}
PY2 = sys.version_info.major == 2
DBUS_FM_ID = 'org.freedesktop.FileManager1'
DBUS_FM_PATH = '/org/freedesktop/FileManager1'
def get_version():
@ -108,10 +101,8 @@ def get_default_config_dir(filename=None):
def save_config_path(resource):
app_data_path = os.environ.get('APPDATA')
if not app_data_path:
try:
import winreg
except ImportError:
import _winreg as winreg # For Python 2.
import winreg
hkey = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders',
@ -175,8 +166,8 @@ def archive_files(arc_name, filepaths, message=None, rotate=10):
from deluge.configmanager import get_config_dir
# Set archive compression to lzma with bz2 fallback.
arc_comp = 'xz' if not PY2 else 'bz2'
# Set archive compression to lzma
arc_comp = 'xz'
archive_dir = os.path.join(get_config_dir(), 'archive')
timestamp = datetime.now().replace(microsecond=0).isoformat().replace(':', '-')
@ -355,27 +346,30 @@ def show_file(path, timestamp=None):
timestamp,
timestamp,
)
if dbus:
bus = dbus.SessionBus()
filemanager1 = bus.get_object(
'org.freedesktop.FileManager1', '/org/freedesktop/FileManager1'
)
paths = [urljoin('file:', pathname2url(path))]
filemanager1.ShowItems(
paths, startup_id, dbus_interface='org.freedesktop.FileManager1'
)
else:
env = os.environ.copy()
env['DESKTOP_STARTUP_ID'] = startup_id.replace('dbus', 'xdg-open')
# No option in xdg to highlight a file so just open parent folder.
subprocess.Popen(['xdg-open', os.path.dirname(path.rstrip('/'))], env=env)
try:
filemanager1 = bus.get_object(DBUS_FM_ID, DBUS_FM_PATH)
except dbus.exceptions.DBusException as ex:
log.debug('Unable to get dbus file manager: %s', ex)
# Fallback to xdg-open
else:
paths = [urljoin('file:', pathname2url(path))]
filemanager1.ShowItems(paths, startup_id, dbus_interface=DBUS_FM_ID)
return
env = os.environ.copy()
env['DESKTOP_STARTUP_ID'] = startup_id.replace('dbus', 'xdg-open')
# No option in xdg to highlight a file so just open parent folder.
subprocess.Popen(['xdg-open', os.path.dirname(path.rstrip('/'))], env=env)
def open_url_in_browser(url):
"""
Opens a url in the desktop's default browser
Opens a URL in the desktop's default browser
:param url: the url to open
:param url: the URL to open
:type url: string
"""
@ -430,7 +424,7 @@ def fsize(fsize_b, precision=1, shortform=False):
'110 KiB'
Note:
This function has been refactored for perfomance with the
This function has been refactored for performance with the
fsize units being translated outside the function.
"""
@ -565,7 +559,7 @@ def ftime(secs):
'6h 23m'
Note:
This function has been refactored for perfomance.
This function has been refactored for performance.
"""
@ -695,7 +689,7 @@ def is_url(url):
"""
A simple test to check if the URL is valid
:param url: the url to test
:param url: the URL to test
:type url: string
:returns: True or False
:rtype: bool
@ -731,9 +725,9 @@ TR_PARAM = 'tr='
def is_magnet(uri):
"""
A check to determine if a uri is a valid bittorrent magnet uri
A check to determine if a URI is a valid bittorrent magnet URI
:param uri: the uri to check
:param uri: the URI to check
:type uri: string
:returns: True or False
:rtype: bool
@ -819,7 +813,7 @@ def get_magnet_info(uri):
def create_magnet_uri(infohash, name=None, trackers=None):
"""Creates a magnet uri
"""Creates a magnet URI
Args:
infohash (str): The info-hash of the torrent.
@ -827,7 +821,7 @@ def create_magnet_uri(infohash, name=None, trackers=None):
trackers (list or dict, optional): A list of trackers or dict or {tracker: tier} pairs.
Returns:
str: A magnet uri string.
str: A magnet URI string.
"""
try:
@ -1007,9 +1001,9 @@ def decode_bytes(byte_str, encoding='utf8'):
if encoding.lower() not in ['utf8', 'utf-8']:
encodings.insert(0, lambda: (encoding, 'strict'))
for l in encodings:
for enc in encodings:
try:
return byte_str.decode(*l())
return byte_str.decode(*enc())
except UnicodeDecodeError:
pass
return ''
@ -1138,6 +1132,7 @@ AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
def create_auth_file():
import stat
import deluge.configmanager
auth_file = deluge.configmanager.get_config_dir('auth')
@ -1153,6 +1148,7 @@ def create_auth_file():
def create_localclient_account(append=False):
import random
from hashlib import sha1 as sha
import deluge.configmanager
auth_file = deluge.configmanager.get_config_dir('auth')
@ -1175,7 +1171,7 @@ def create_localclient_account(append=False):
def get_localhost_auth():
"""Grabs the localclient auth line from the 'auth' file and creates a localhost uri.
"""Grabs the localclient auth line from the 'auth' file and creates a localhost URI.
Returns:
tuple: With the username and password to login as.
@ -1231,15 +1227,10 @@ def set_env_variable(name, value):
http://sourceforge.net/p/gramps/code/HEAD/tree/branches/maintenance/gramps32/src/TransUtils.py
"""
# Update Python's copy of the environment variables
try:
os.environ[name] = value
except UnicodeEncodeError:
# Python 2
os.environ[name] = value.encode('utf8')
os.environ[name] = value
if windows_check():
from ctypes import windll
from ctypes import cdll
from ctypes import cdll, windll
# Update the copy maintained by Windows (so SysInternals Process Explorer sees it)
result = windll.kernel32.SetEnvironmentVariableW(name, value)
@ -1264,45 +1255,22 @@ def set_env_variable(name, value):
def unicode_argv():
""" Gets sys.argv as list of unicode objects on any platform."""
if windows_check():
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
# On platforms other than Windows, we have to find the likely encoding of the args and decode
# First check if sys.stdout or stdin have encoding set
encoding = getattr(sys.stdout, 'encoding') or getattr(sys.stdin, 'encoding')
# If that fails, check what the locale is set to
encoding = encoding or locale.getpreferredencoding()
# As a last resort, just default to utf-8
encoding = encoding or 'utf-8'
get_cmd_linew = cdll.kernel32.GetCommandLineW
get_cmd_linew.argtypes = []
get_cmd_linew.restype = LPCWSTR
arg_list = []
for arg in sys.argv:
try:
arg_list.append(arg.decode(encoding))
except AttributeError:
arg_list.append(arg)
cmdline_to_argvw = windll.shell32.CommandLineToArgvW
cmdline_to_argvw.argtypes = [LPCWSTR, POINTER(c_int)]
cmdline_to_argvw.restype = POINTER(LPWSTR)
cmd = get_cmd_linew()
argc = c_int(0)
argv = cmdline_to_argvw(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in range(start, argc.value)]
else:
# On other platforms, we have to find the likely encoding of the args and decode
# First check if sys.stdout or stdin have encoding set
encoding = getattr(sys.stdout, 'encoding') or getattr(sys.stdin, 'encoding')
# If that fails, check what the locale is set to
encoding = encoding or locale.getpreferredencoding()
# As a last resort, just default to utf-8
encoding = encoding or 'utf-8'
arg_list = []
for arg in sys.argv:
try:
arg_list.append(arg.decode(encoding))
except AttributeError:
arg_list.append(arg)
return arg_list
return arg_list
def run_profiled(func, *args, **kwargs):

View File

@ -7,13 +7,10 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import logging
import traceback
from collections import defaultdict
from six import string_types
from twisted.internet import reactor
from twisted.internet.defer import DeferredList, fail, maybeDeferred, succeed
from twisted.internet.task import LoopingCall, deferLater
@ -293,7 +290,8 @@ class ComponentRegistry(object):
obj (Component): a component object to deregister
Returns:
Deferred: a deferred object that will fire once the Component has been sucessfully deregistered
Deferred: a deferred object that will fire once the Component has been
successfully deregistered
"""
if obj in self.components.values():
@ -324,7 +322,7 @@ class ComponentRegistry(object):
# Start all the components if names is empty
if not names:
names = list(self.components)
elif isinstance(names, string_types):
elif isinstance(names, str):
names = [names]
def on_depends_started(result, name):
@ -358,7 +356,7 @@ class ComponentRegistry(object):
"""
if not names:
names = list(self.components)
elif isinstance(names, string_types):
elif isinstance(names, str):
names = [names]
def on_dependents_stopped(result, name):
@ -398,7 +396,7 @@ class ComponentRegistry(object):
"""
if not names:
names = list(self.components)
elif isinstance(names, string_types):
elif isinstance(names, str):
names = [names]
deferreds = []
@ -424,7 +422,7 @@ class ComponentRegistry(object):
"""
if not names:
names = list(self.components)
elif isinstance(names, string_types):
elif isinstance(names, str):
names = [names]
deferreds = []
@ -448,7 +446,7 @@ class ComponentRegistry(object):
def on_stopped(result):
return DeferredList(
[comp._component_shutdown() for comp in self.components.values()]
[comp._component_shutdown() for comp in list(self.components.values())]
)
return self.stop(list(self.components)).addCallback(on_stopped)

View File

@ -39,18 +39,15 @@ this can only be done for the 'config file version' and not for the 'format'
version as this will be done internally.
"""
from __future__ import unicode_literals
import json
import logging
import os
import pickle
import shutil
from codecs import getwriter
from io import open
from tempfile import NamedTemporaryFile
import six.moves.cPickle as pickle # noqa: N813
from deluge.common import JSON_FORMAT, get_default_config_dir
log = logging.getLogger(__name__)
@ -74,38 +71,33 @@ def prop(func):
return property(doc=func.__doc__, **func())
def find_json_objects(s):
"""Find json objects in a string.
def find_json_objects(text, decoder=json.JSONDecoder()):
"""Find json objects in text.
Args:
s (str): the string to find json objects in
text (str): The text to find json objects within.
Returns:
list: A list of tuples containing start and end locations of json
objects in string `s`. e.g. [(start, end), ...]
objects in the text. e.g. [(start, end), ...]
"""
objects = []
opens = 0
start = s.find('{')
offset = start
offset = 0
while True:
try:
start = text.index('{', offset)
except ValueError:
break
if start < 0:
return []
quoted = False
for index, c in enumerate(s[offset:]):
if c == '"':
quoted = not quoted
elif quoted:
continue
elif c == '{':
opens += 1
elif c == '}':
opens -= 1
if opens == 0:
objects.append((start, index + offset + 1))
start = index + offset + 1
try:
__, index = decoder.raw_decode(text[start:])
except json.decoder.JSONDecodeError:
offset = start + 1
else:
offset = start + index
objects.append((start, offset))
return objects
@ -209,9 +201,9 @@ class Config(object):
global callLater
if callLater is None:
# Must import here and not at the top or it will throw ReactorAlreadyInstalledError
from twisted.internet.reactor import (
from twisted.internet.reactor import ( # pylint: disable=redefined-outer-name
callLater,
) # pylint: disable=redefined-outer-name
)
# Run the set_function for this key if any
try:
for func in self.__set_functions[key]:
@ -309,9 +301,9 @@ class Config(object):
global callLater
if callLater is None:
# Must import here and not at the top or it will throw ReactorAlreadyInstalledError
from twisted.internet.reactor import (
from twisted.internet.reactor import ( # pylint: disable=redefined-outer-name
callLater,
) # pylint: disable=redefined-outer-name
)
# We set the save_timer for 5 seconds if not already set
if not self._save_timer or not self._save_timer.active():

View File

@ -7,8 +7,6 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import logging
import os

View File

@ -15,10 +15,8 @@ This should typically only be used by the Core. Plugins should utilize the
`:mod:EventManager` for similar functionality.
"""
from __future__ import unicode_literals
import logging
import types
from types import SimpleNamespace
from twisted.internet import reactor
@ -28,14 +26,6 @@ from deluge.common import decode_bytes
log = logging.getLogger(__name__)
try:
SimpleNamespace = types.SimpleNamespace # Python 3.3+
except AttributeError:
class SimpleNamespace(object): # Python 2.7
def __init__(self, **attr):
self.__dict__.update(attr)
class AlertManager(component.Component):
"""AlertManager fetches and processes libtorrent alerts"""

View File

@ -8,8 +8,6 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import logging
import os
import shutil
@ -101,7 +99,7 @@ class AuthManager(component.Component):
int: The auth level for this user.
Raises:
AuthenticationRequired: If aditional details are required to authenticate.
AuthenticationRequired: If additional details are required to authenticate.
BadLoginError: If the username does not exist or password does not match.
"""

View File

@ -8,8 +8,6 @@
# See LICENSE for more details.
#
from __future__ import division, unicode_literals
import glob
import logging
import os
@ -17,8 +15,8 @@ import shutil
import tempfile
import threading
from base64 import b64decode, b64encode
from urllib.request import URLError, urlopen
from six import string_types
from twisted.internet import defer, reactor, task
from twisted.web.client import Agent, readBody
@ -56,12 +54,6 @@ from deluge.event import (
)
from deluge.httpdownloader import download_file
try:
from urllib.request import urlopen, URLError
except ImportError:
# PY2 fallback
from urllib2 import urlopen, URLError
log = logging.getLogger(__name__)
DEPR_SESSION_STATUS_KEYS = {
@ -267,7 +259,7 @@ class Core(component.Component):
version (str): The version string in PEP440 dotted notation.
Returns:
str: The formattted peer_id with Deluge prefix e.g. '--DE200s--'
str: The formatted peer_id with Deluge prefix e.g. '--DE200s--'
"""
split = deluge.common.VersionSplit(version)
@ -358,8 +350,8 @@ class Core(component.Component):
if blocks_read:
self.session_status['read_hit_ratio'] = (
self.session_status['disk.num_blocks_cache_hits'] / blocks_read
)
blocks_read - self.session_status['disk.num_read_ops']
) / blocks_read
else:
self.session_status['read_hit_ratio'] = 0.0
@ -405,7 +397,7 @@ class Core(component.Component):
# Exported Methods
@export
def add_torrent_file_async(self, filename, filedump, options, save_state=True):
"""Adds a torrent file to the session asynchonously.
"""Adds a torrent file to the session asynchronously.
Args:
filename (str): The filename of the torrent.
@ -442,8 +434,8 @@ class Core(component.Component):
Used by UIs to get magnet files for selection before adding to session.
Args:
magnet (str): The magnet uri.
timeout (int): Number of seconds to wait before cancelling request.
magnet (str): The magnet URI.
timeout (int): Number of seconds to wait before canceling request.
Returns:
Deferred: A tuple of (torrent_id (str), metadata (dict)) for the magnet.
@ -456,7 +448,7 @@ class Core(component.Component):
return result
d = self.torrentmanager.prefetch_metadata(magnet, timeout)
# Use a seperate callback chain to handle existing prefetching magnet.
# Use a separate callback chain to handle existing prefetching magnet.
result_d = defer.Deferred()
d.addBoth(on_metadata, result_d)
return result_d
@ -488,10 +480,11 @@ class Core(component.Component):
@export
def add_torrent_files(self, torrent_files):
"""Adds multiple torrent files to the session asynchonously.
"""Adds multiple torrent files to the session asynchronously.
Args:
torrent_files (list of tuples): Torrent files as tuple of (filename, filedump, options).
torrent_files (list of tuples): Torrent files as tuple of
``(filename, filedump, options)``.
Returns:
Deferred
@ -517,10 +510,10 @@ class Core(component.Component):
@export
def add_torrent_url(self, url, options, headers=None):
"""
Adds a torrent from a url. Deluge will attempt to fetch the torrent
from url prior to adding it to the session.
Adds a torrent from a URL. Deluge will attempt to fetch the torrent
from the URL prior to adding it to the session.
:param url: the url pointing to the torrent file
:param url: the URL pointing to the torrent file
:type url: string
:param options: the options to apply to the torrent on add
:type options: dict
@ -529,7 +522,7 @@ class Core(component.Component):
:returns: a Deferred which returns the torrent_id as a str or None
"""
log.info('Attempting to add url %s', url)
log.info('Attempting to add URL %s', url)
def on_download_success(filename):
# We got the file, so add it to the session
@ -543,7 +536,7 @@ class Core(component.Component):
def on_download_fail(failure):
# Log the error and pass the failure onto the client
log.error('Failed to add torrent from url %s', url)
log.error('Failed to add torrent from URL %s', url)
return failure
tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent')
@ -566,7 +559,7 @@ class Core(component.Component):
:rtype: string
"""
log.debug('Attempting to add by magnet uri: %s', uri)
log.debug('Attempting to add by magnet URI: %s', uri)
return self.torrentmanager.add(magnet=uri, options=options)
@ -652,7 +645,7 @@ class Core(component.Component):
)
status[key] = self.session_status[new_key]
else:
log.warning('Session status key not valid: %s', key)
log.debug('Session status key not valid: %s', key)
return status
@export
@ -665,7 +658,7 @@ class Core(component.Component):
def pause_torrent(self, torrent_id):
"""Pauses a torrent"""
log.debug('Pausing: %s', torrent_id)
if not isinstance(torrent_id, string_types):
if not isinstance(torrent_id, str):
self.pause_torrents(torrent_id)
else:
self.torrentmanager[torrent_id].pause()
@ -716,7 +709,7 @@ class Core(component.Component):
def resume_torrent(self, torrent_id):
"""Resumes a torrent"""
log.debug('Resuming: %s', torrent_id)
if not isinstance(torrent_id, string_types):
if not isinstance(torrent_id, str):
self.resume_torrents(torrent_id)
else:
self.torrentmanager[torrent_id].resume()
@ -746,7 +739,7 @@ class Core(component.Component):
import traceback
traceback.print_exc()
# Torrent was probaly removed meanwhile
# Torrent was probably removed meanwhile
return {}
# Ask the plugin manager to fill in the plugin keys
@ -894,12 +887,13 @@ class Core(component.Component):
Args:
torrent_ids (list): A list of torrent_ids to set the options for.
options (dict): A dict of torrent options to set. See torrent.TorrentOptions class for valid keys.
options (dict): A dict of torrent options to set. See
``torrent.TorrentOptions`` class for valid keys.
"""
if 'owner' in options and not self.authmanager.has_account(options['owner']):
raise DelugeError('Username "%s" is not known.' % options['owner'])
if isinstance(torrent_ids, string_types):
if isinstance(torrent_ids, str):
torrent_ids = [torrent_ids]
for torrent_id in torrent_ids:
@ -907,9 +901,13 @@ class Core(component.Component):
@export
def set_torrent_trackers(self, torrent_id, trackers):
"""Sets a torrents tracker list. trackers will be [{"url", "tier"}]"""
"""Sets a torrents tracker list. trackers will be ``[{"url", "tier"}]``"""
return self.torrentmanager[torrent_id].set_trackers(trackers)
@export
def get_magnet_uri(self, torrent_id):
return self.torrentmanager[torrent_id].get_magnet_uri()
@deprecated
@export
def set_torrent_max_connections(self, torrent_id, value):
@ -985,7 +983,7 @@ class Core(component.Component):
@export
def get_path_size(self, path):
"""Returns the size of the file or folder 'path' and -1 if the path is
unaccessible (non-existent or insufficient privs)"""
inaccessible (non-existent or insufficient privileges)"""
return deluge.common.get_path_size(path)
@export
@ -1058,8 +1056,8 @@ class Core(component.Component):
def upload_plugin(self, filename, filedump):
"""This method is used to upload new plugins to the daemon. It is used
when connecting to the daemon remotely and installing a new plugin on
the client side. 'plugin_data' is a xmlrpc.Binary object of the file data,
ie, plugin_file.read()"""
the client side. ``plugin_data`` is a ``xmlrpc.Binary`` object of the file data,
i.e. ``plugin_file.read()``"""
try:
filedump = b64decode(filedump)
@ -1075,14 +1073,14 @@ class Core(component.Component):
@export
def rescan_plugins(self):
"""
Rescans the plugin folders for new plugins
Re-scans the plugin folders for new plugins
"""
component.get('CorePluginManager').scan_for_plugins()
@export
def rename_files(self, torrent_id, filenames):
"""
Rename files in torrent_id. Since this is an asynchronous operation by
Rename files in ``torrent_id``. Since this is an asynchronous operation by
libtorrent, watch for the TorrentFileRenamedEvent to know when the
files have been renamed.
@ -1258,7 +1256,7 @@ class Core(component.Component):
@export
def get_external_ip(self):
"""
Returns the external ip address recieved from libtorrent.
Returns the external IP address received from libtorrent.
"""
return self.external_ip

View File

@ -8,8 +8,6 @@
#
"""The Deluge daemon"""
from __future__ import unicode_literals
import logging
import os
import socket
@ -200,6 +198,7 @@ class Daemon(object):
if rpc not in self.get_method_list():
return False
return self.rpcserver.get_session_auth_level() >= self.rpcserver.get_rpc_auth_level(
rpc
return (
self.rpcserver.get_session_auth_level()
>= self.rpcserver.get_rpc_auth_level(rpc)
)

View File

@ -7,8 +7,6 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
from __future__ import print_function, unicode_literals
import os
import sys
from logging import DEBUG, FileHandler, getLogger

View File

@ -7,8 +7,6 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import logging
import deluge.component as component

View File

@ -7,12 +7,8 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import logging
from six import string_types
import deluge.component as component
from deluge.common import TORRENT_STATE
@ -100,9 +96,7 @@ def tracker_error_filter(torrent_ids, values):
class FilterManager(component.Component):
"""FilterManager
"""
"""FilterManager"""
def __init__(self, core):
component.Component.__init__(self, 'FilterManager')
@ -138,7 +132,7 @@ class FilterManager(component.Component):
# Sanitize input: filter-value must be a list of strings
for key, value in filter_dict.items():
if isinstance(value, string_types):
if isinstance(value, str):
filter_dict[key] = [value]
# Optimized filter for id

View File

@ -9,8 +9,6 @@
"""PluginManager for Core"""
from __future__ import unicode_literals
import logging
from twisted.internet import defer

View File

@ -8,13 +8,13 @@
#
from __future__ import unicode_literals
import logging
import os
import platform
import random
import threading
from urllib.parse import quote_plus
from urllib.request import urlopen
from twisted.internet.task import LoopingCall
@ -29,13 +29,6 @@ try:
except ImportError:
GeoIP = None
try:
from urllib.parse import quote_plus
from urllib.request import urlopen
except ImportError:
from urllib import quote_plus
from urllib2 import urlopen
log = logging.getLogger(__name__)
DEFAULT_PREFS = {
@ -231,7 +224,7 @@ class PreferencesManager(component.Component):
self.core.apply_session_settings(
{
'listen_system_port_fallback': self.config['listen_use_sys_port'],
'listen_interfaces': ''.join(interfaces),
'listen_interfaces': ','.join(interfaces),
}
)

View File

@ -8,17 +8,13 @@
#
"""RPCServer Module"""
from __future__ import unicode_literals
import logging
import os
import stat
import sys
import traceback
from collections import namedtuple
from types import FunctionType
from OpenSSL import crypto
from twisted.internet import defer, reactor
from twisted.internet.protocol import Factory, connectionDone
@ -29,7 +25,7 @@ from deluge.core.authmanager import (
AUTH_LEVEL_DEFAULT,
AUTH_LEVEL_NONE,
)
from deluge.crypto_utils import get_context_factory
from deluge.crypto_utils import check_ssl_keys, get_context_factory
from deluge.error import (
DelugeError,
IncompatibleClient,
@ -588,59 +584,3 @@ class RPCServer(component.Component):
def stop(self):
self.factory.state = 'stopping'
def check_ssl_keys():
"""
Check for SSL cert/key and create them if necessary
"""
ssl_dir = deluge.configmanager.get_config_dir('ssl')
if not os.path.exists(ssl_dir):
# The ssl folder doesn't exist so we need to create it
os.makedirs(ssl_dir)
generate_ssl_keys()
else:
for f in ('daemon.pkey', 'daemon.cert'):
if not os.path.exists(os.path.join(ssl_dir, f)):
generate_ssl_keys()
break
def generate_ssl_keys():
"""
This method generates a new SSL key/cert.
"""
from deluge.common import PY2
digest = 'sha256' if not PY2 else b'sha256'
# Generate key pair
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 2048)
# Generate cert request
req = crypto.X509Req()
subj = req.get_subject()
setattr(subj, 'CN', 'Deluge Daemon')
req.set_pubkey(pkey)
req.sign(pkey, digest)
# Generate certificate
cert = crypto.X509()
cert.set_serial_number(0)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 3) # Three Years
cert.set_issuer(req.get_subject())
cert.set_subject(req.get_subject())
cert.set_pubkey(req.get_pubkey())
cert.sign(pkey, digest)
# Write out files
ssl_dir = deluge.configmanager.get_config_dir('ssl')
with open(os.path.join(ssl_dir, 'daemon.pkey'), 'wb') as _file:
_file.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
with open(os.path.join(ssl_dir, 'daemon.cert'), 'wb') as _file:
_file.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
# Make the files only readable by this user
for f in ('daemon.pkey', 'daemon.cert'):
os.chmod(os.path.join(ssl_dir, f), stat.S_IREAD | stat.S_IWRITE)

View File

@ -14,11 +14,10 @@ Attributes:
"""
from __future__ import division, unicode_literals
import logging
import os
import socket
from urllib.parse import urlparse
from twisted.internet.defer import Deferred, DeferredList
@ -34,18 +33,6 @@ from deluge.event import (
TorrentTrackerStatusEvent,
)
try:
from urllib.parse import urlparse
except ImportError:
# PY2 fallback
from urlparse import urlparse # pylint: disable=ungrouped-imports
try:
from future_builtins import zip
except ImportError:
# Ignore on Py3.
pass
log = logging.getLogger(__name__)
LT_TORRENT_STATE_MAP = {
@ -206,12 +193,12 @@ class Torrent(object):
options (dict): The torrent options.
state (TorrentState): The torrent state.
filename (str): The filename of the torrent file.
magnet (str): The magnet uri.
magnet (str): The magnet URI.
Attributes:
torrent_id (str): The torrent_id for this torrent
handle: Holds the libtorrent torrent handle
magnet (str): The magnet uri used to add this torrent (if available).
magnet (str): The magnet URI used to add this torrent (if available).
status: Holds status info so that we don"t need to keep getting it from libtorrent.
torrent_info: store the torrent info.
has_metadata (bool): True if the metadata for the torrent is available, False otherwise.
@ -266,6 +253,9 @@ class Torrent(object):
self.is_finished = False
self.filename = filename
if not self.filename:
self.filename = ''
self.forced_error = None
self.statusmsg = None
self.state = None
@ -428,14 +418,14 @@ class Torrent(object):
# Setting the priorites for all the pieces of this torrent
self.handle.prioritize_pieces(priorities)
def set_sequential_download(self, set_sequencial):
def set_sequential_download(self, sequential):
"""Sets whether to download the pieces of the torrent in order.
Args:
set_sequencial (bool): Enable sequencial downloading.
sequential (bool): Enable sequential downloading.
"""
self.options['sequential_download'] = set_sequencial
self.handle.set_sequential_download(set_sequencial)
self.options['sequential_download'] = sequential
self.handle.set_sequential_download(sequential)
def set_auto_managed(self, auto_managed):
"""Set auto managed mode, i.e. will be started or queued automatically.
@ -810,7 +800,11 @@ class Torrent(object):
if peer.flags & peer.connecting or peer.flags & peer.handshake:
continue
client = decode_bytes(peer.client)
try:
client = decode_bytes(peer.client)
except UnicodeDecodeError:
# libtorrent on Py3 can raise UnicodeDecodeError for peer_info.client
client = 'unknown'
try:
country = component.get('Core').geoip_instance.country_code_by_addr(
@ -867,11 +861,18 @@ class Torrent(object):
"""
if not self.has_metadata:
return []
return [
progress / _file.size if _file.size else 0.0
for progress, _file in zip(
try:
files_progresses = zip(
self.handle.file_progress(), self.torrent_info.files()
)
except Exception:
# Handle libtorrent >=2.0.0,<=2.0.4 file_progress error
files_progresses = zip(iter(lambda: 0, 1), self.torrent_info.files())
return [
progress / _file.size if _file.size else 0.0
for progress, _file in files_progresses
]
def get_tracker_host(self):
@ -913,7 +914,7 @@ class Torrent(object):
return ''
def get_magnet_uri(self):
"""Returns a magnet uri for this torrent"""
"""Returns a magnet URI for this torrent"""
return lt.make_magnet_uri(self.handle)
def get_name(self):
@ -987,6 +988,8 @@ class Torrent(object):
call to get_status based on the session_id
update (bool): If True the status will be updated from libtorrent
if False, the cached values will be returned
all_keys (bool): If True return all keys while ignoring the keys param
if False, return only the requested keys
Returns:
dict: a dictionary of the status keys and their values
@ -1208,8 +1211,8 @@ class Torrent(object):
bool: True is successful, otherwise False
"""
try:
self.handle.connect_peer((peer_ip, peer_port), 0)
except RuntimeError as ex:
self.handle.connect_peer((peer_ip, int(peer_port)), 0)
except (RuntimeError, ValueError) as ex:
log.debug('Unable to connect to peer: %s', ex)
return False
return True
@ -1312,7 +1315,7 @@ class Torrent(object):
torrent_files = [
os.path.join(get_config_dir(), 'state', self.torrent_id + '.torrent')
]
if delete_copies:
if delete_copies and self.filename:
torrent_files.append(
os.path.join(self.config['torrentfiles_location'], self.filename)
)
@ -1336,8 +1339,8 @@ class Torrent(object):
def scrape_tracker(self):
"""Scrape the tracker
A scrape request queries the tracker for statistics such as total
number of incomplete peers, complete peers, number of downloads etc.
A scrape request queries the tracker for statistics such as total
number of incomplete peers, complete peers, number of downloads etc.
"""
try:
self.handle.scrape_tracker()
@ -1384,7 +1387,7 @@ class Torrent(object):
This basically does a file rename on all of the folders children.
Args:
folder (str): The orignal folder name
folder (str): The original folder name
new_folder (str): The new folder name
Returns:

View File

@ -8,24 +8,28 @@
#
"""TorrentManager handles Torrent objects"""
from __future__ import unicode_literals
import datetime
import logging
import operator
import os
import pickle
import time
from collections import namedtuple
from tempfile import gettempdir
import six.moves.cPickle as pickle # noqa: N813
from twisted.internet import defer, error, reactor, threads
from twisted.internet.defer import Deferred, DeferredList
from twisted.internet.task import LoopingCall
import deluge.component as component
from deluge._libtorrent import lt
from deluge.common import archive_files, decode_bytes, get_magnet_info, is_magnet
from deluge._libtorrent import LT_VERSION, lt
from deluge.common import (
VersionSplit,
archive_files,
decode_bytes,
get_magnet_info,
is_magnet,
)
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
from deluge.core.torrent import Torrent, TorrentOptions, sanitize_filepath
@ -89,7 +93,7 @@ class TorrentState: # pylint: disable=old-style-class
super_seeding=False,
name=None,
):
# Build the class atrribute list from args
# Build the class attribute list from args
for key, value in locals().items():
if key == 'self':
continue
@ -340,11 +344,11 @@ class TorrentManager(component.Component):
return torrent_info
def prefetch_metadata(self, magnet, timeout):
"""Download the metadata for a magnet uri.
"""Download the metadata for a magnet URI.
Args:
magnet (str): A magnet uri to download the metadata for.
timeout (int): Number of seconds to wait before cancelling.
magnet (str): A magnet URI to download the metadata for.
timeout (int): Number of seconds to wait before canceling.
Returns:
Deferred: A tuple of (torrent_id (str), metadata (dict))
@ -434,6 +438,11 @@ class TorrentManager(component.Component):
add_torrent_params['url'] = magnet.strip().encode('utf8')
add_torrent_params['name'] = magnet_info['name']
torrent_id = magnet_info['info_hash']
# Workaround lt 1.2 bug for magnet resume data with no metadata
if resume_data and VersionSplit(LT_VERSION) >= VersionSplit('1.2.10.0'):
add_torrent_params['info_hash'] = bytes(
bytearray.fromhex(torrent_id)
)
else:
raise AddTorrentError(
'Unable to add magnet, invalid magnet info: %s' % magnet
@ -509,7 +518,7 @@ class TorrentManager(component.Component):
save_state (bool, optional): If True save the session state after adding torrent, defaults to True.
filedump (str, optional): bencoded filedump of a torrent file.
filename (str, optional): The filename of the torrent file.
magnet (str, optional): The magnet uri.
magnet (str, optional): The magnet URI.
resume_data (lt.entry, optional): libtorrent fast resume data.
Returns:
@ -574,7 +583,7 @@ class TorrentManager(component.Component):
save_state (bool, optional): If True save the session state after adding torrent, defaults to True.
filedump (str, optional): bencoded filedump of a torrent file.
filename (str, optional): The filename of the torrent file.
magnet (str, optional): The magnet uri.
magnet (str, optional): The magnet URI.
resume_data (lt.entry, optional): libtorrent fast resume data.
Returns:
@ -642,7 +651,7 @@ class TorrentManager(component.Component):
# Resume AlertManager if paused for adding torrent to libtorrent.
component.resume('AlertManager')
# Store the orignal resume_data, in case of errors.
# Store the original resume_data, in case of errors.
if resume_data:
self.resume_data[torrent.torrent_id] = resume_data
@ -809,7 +818,7 @@ class TorrentManager(component.Component):
try:
with open(filepath, 'rb') as _file:
state = pickle.load(_file)
state = pickle.load(_file, encoding='utf8')
except (IOError, EOFError, pickle.UnpicklingError) as ex:
message = 'Unable to load {}: {}'.format(filepath, ex)
log.error(message)
@ -1022,7 +1031,7 @@ class TorrentManager(component.Component):
)
def on_torrent_resume_save(dummy_result, torrent_id):
"""Recieved torrent resume_data alert so remove from waiting list"""
"""Received torrent resume_data alert so remove from waiting list"""
self.waiting_on_resume_data.pop(torrent_id, None)
deferreds = []
@ -1240,7 +1249,7 @@ class TorrentManager(component.Component):
def on_alert_add_torrent(self, alert):
"""Alert handler for libtorrent add_torrent_alert"""
if not alert.handle.is_valid():
log.warning('Torrent handle is invalid!')
log.warning('Torrent handle is invalid: %s', alert.error.message())
return
try:
@ -1389,7 +1398,22 @@ class TorrentManager(component.Component):
log.debug(
'Tracker Error Alert: %s [%s]', decode_bytes(alert.message()), error_message
)
torrent.set_tracker_status('Error: ' + error_message)
if VersionSplit(LT_VERSION) >= VersionSplit('1.2.0.0'):
# libtorrent 1.2 added endpoint struct to each tracker. to prevent false updates
# we will need to verify that at least one endpoint to the errored tracker is working
for tracker in torrent.handle.trackers():
if tracker['url'] == alert.url:
if any(
endpoint['last_error']['value'] == 0
for endpoint in tracker['endpoints']
):
torrent.set_tracker_status('Announce OK')
else:
torrent.set_tracker_status('Error: ' + error_message)
break
else:
# preserve old functionality for libtorrent < 1.2
torrent.set_tracker_status('Error: ' + error_message)
def on_alert_storage_moved(self, alert):
"""Alert handler for libtorrent storage_moved_alert"""
@ -1596,18 +1620,9 @@ class TorrentManager(component.Component):
self.handle_torrents_status_callback(self.torrents_status_requests.pop())
def on_alert_external_ip(self, alert):
"""Alert handler for libtorrent external_ip_alert
Note:
The alert.message IPv4 address format is:
'external IP received: 0.0.0.0'
and IPv6 address format is:
'external IP received: 0:0:0:0:0:0:0:0'
"""
external_ip = decode_bytes(alert.message()).split(' ')[-1]
log.info('on_alert_external_ip: %s', external_ip)
component.get('EventManager').emit(ExternalIPEvent(external_ip))
"""Alert handler for libtorrent external_ip_alert"""
log.info('on_alert_external_ip: %s', alert.external_address)
component.get('EventManager').emit(ExternalIPEvent(alert.external_address))
def on_alert_performance(self, alert):
"""Alert handler for libtorrent performance_alert"""

View File

@ -7,8 +7,10 @@
# See LICENSE for more details.
#
from __future__ import division, print_function, unicode_literals
import os
import stat
from OpenSSL import crypto
from OpenSSL.crypto import FILETYPE_PEM
from twisted.internet.ssl import (
AcceptableCiphers,
@ -18,6 +20,8 @@ from twisted.internet.ssl import (
TLSVersion,
)
import deluge.configmanager
# A TLS ciphers list.
# Sources for more information on TLS ciphers:
# - https://wiki.mozilla.org/Security/Server_Side_TLS
@ -77,3 +81,57 @@ def get_context_factory(cert_path, pkey_path):
ctx.set_options(SSL_OP_NO_RENEGOTIATION)
return cert_options
def check_ssl_keys():
"""
Check for SSL cert/key and create them if necessary
"""
ssl_dir = deluge.configmanager.get_config_dir('ssl')
if not os.path.exists(ssl_dir):
# The ssl folder doesn't exist so we need to create it
os.makedirs(ssl_dir)
generate_ssl_keys()
else:
for f in ('daemon.pkey', 'daemon.cert'):
if not os.path.exists(os.path.join(ssl_dir, f)):
generate_ssl_keys()
break
def generate_ssl_keys():
"""
This method generates a new SSL key/cert.
"""
digest = 'sha256'
# Generate key pair
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 2048)
# Generate cert request
req = crypto.X509Req()
subj = req.get_subject()
setattr(subj, 'CN', 'Deluge Daemon')
req.set_pubkey(pkey)
req.sign(pkey, digest)
# Generate certificate
cert = crypto.X509()
cert.set_serial_number(0)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 3) # Three Years
cert.set_issuer(req.get_subject())
cert.set_subject(req.get_subject())
cert.set_pubkey(req.get_pubkey())
cert.sign(pkey, digest)
# Write out files
ssl_dir = deluge.configmanager.get_config_dir('ssl')
with open(os.path.join(ssl_dir, 'daemon.pkey'), 'wb') as _file:
_file.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
with open(os.path.join(ssl_dir, 'daemon.cert'), 'wb') as _file:
_file.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
# Make the files only readable by this user
for f in ('daemon.pkey', 'daemon.cert'):
os.chmod(os.path.join(ssl_dir, f), stat.S_IREAD | stat.S_IWRITE)

View File

@ -7,8 +7,6 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import inspect
import re
import warnings
@ -56,7 +54,7 @@ def overrides(*args):
if inspect.isfunction(args[0]):
return _overrides(stack, args[0])
else:
# One or more classes are specifed, so return a function that will be
# One or more classes are specified, so return a function that will be
# called with the real function as argument
def ret_func(func, **kwargs):
return _overrides(stack, func, explicit_base_classes=args)
@ -107,7 +105,7 @@ def _overrides(stack, method, explicit_base_classes=None):
for c in base_classes + check_classes:
classes[c] = get_class(c)
# Verify that the excplicit override class is one of base classes
# Verify that the explicit override class is one of base classes
if explicit_base_classes:
from itertools import product
@ -146,7 +144,7 @@ def _overrides(stack, method, explicit_base_classes=None):
def deprecated(func):
"""This is a decorator which can be used to mark function as deprecated.
It will result in a warning being emmitted when the function is used.
It will result in a warning being emitted when the function is used.
"""

View File

@ -9,9 +9,6 @@
#
from __future__ import unicode_literals
class DelugeError(Exception):
def __new__(cls, *args, **kwargs):
inst = super(DelugeError, cls).__new__(cls, *args, **kwargs)
@ -94,3 +91,7 @@ class AuthenticationRequired(_UsernameBasedPasstroughError):
class AuthManagerError(_UsernameBasedPasstroughError):
pass
class LibtorrentImportError(ImportError):
pass

View File

@ -14,10 +14,6 @@ This module describes the types of events that can be generated by the daemon
and subsequently emitted to the clients.
"""
from __future__ import unicode_literals
import six
known_events = {}
@ -32,7 +28,7 @@ class DelugeEventMetaClass(type):
known_events[name] = cls
class DelugeEvent(six.with_metaclass(DelugeEventMetaClass, object)):
class DelugeEvent(metaclass=DelugeEventMetaClass):
"""
The base class for all events.

View File

@ -7,8 +7,6 @@
# See LICENSE for more details.
#
from __future__ import unicode_literals
import cgi
import logging
import os.path
@ -151,9 +149,12 @@ class HTTPDownloaderAgent(object):
self.filename = new_file_name
cont_type = headers.getRawHeaders(b'content-type')[0].decode()
params = cgi.parse_header(cont_type)[1]
encoding = params.get('charset', None)
cont_type_header = headers.getRawHeaders(b'content-type')[0].decode()
cont_type, params = cgi.parse_header(cont_type_header)
# Only re-ecode text content types.
encoding = None
if cont_type.startswith('text/'):
encoding = params.get('charset', None)
response.deliverBody(
BodyHandler(response.request, finished, body_length, self, encoding)
)

6178
deluge/i18n/af.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,53 +7,53 @@ msgid ""
msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-06-06 11:53+0100\n"
"PO-Revision-Date: 2019-01-17 20:26+0000\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
"PO-Revision-Date: 2019-07-23 10:47+0000\n"
"Last-Translator: scootergrisen <scootergrisen@gmail.com>\n"
"Language-Team: Danish <da@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2019-06-06 11:12+0000\n"
"X-Generator: Launchpad (build 18978)\n"
"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
#: deluge/common.py:405
#: deluge/common.py:411
msgid "B"
msgstr "B"
#: deluge/common.py:406
#: deluge/common.py:412
msgid "KiB"
msgstr "KiB"
#: deluge/common.py:407
#: deluge/common.py:413
msgid "MiB"
msgstr "MiB"
#: deluge/common.py:408
#: deluge/common.py:414
msgid "GiB"
msgstr "GiB"
#: deluge/common.py:409
#: deluge/common.py:415
msgid "TiB"
msgstr "TiB"
#: deluge/common.py:410
#: deluge/common.py:416
msgid "K"
msgstr "K"
#: deluge/common.py:411
#: deluge/common.py:417
msgid "M"
msgstr "M"
#: deluge/common.py:412
#: deluge/common.py:418
msgid "G"
msgstr "G"
#: deluge/common.py:413
#: deluge/common.py:419
msgid "T"
msgstr "T"
#: deluge/common.py:509 deluge/ui/gtk3/statusbar.py:442
#: deluge/common.py:515 deluge/ui/gtk3/statusbar.py:442
#: deluge/ui/gtk3/statusbar.py:455 deluge/ui/gtk3/statusbar.py:464
#: deluge/ui/gtk3/statusbar.py:477 deluge/ui/gtk3/statusbar.py:484
#: deluge/ui/gtk3/statusbar.py:526 deluge/ui/gtk3/statusbar.py:542
@ -64,7 +64,7 @@ msgstr "T"
msgid "K/s"
msgstr "K/s"
#: deluge/common.py:509 deluge/ui/gtk3/menubar.py:449
#: deluge/common.py:515 deluge/ui/gtk3/menubar.py:449
#: deluge/ui/gtk3/menubar.py:455
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:80
#: deluge/ui/console/widgets/statusbars.py:104
@ -78,27 +78,27 @@ msgstr "K/s"
msgid "KiB/s"
msgstr "KiB/s"
#: deluge/common.py:515
#: deluge/common.py:521
msgid "M/s"
msgstr "M/s"
#: deluge/common.py:515
#: deluge/common.py:521
msgid "MiB/s"
msgstr "MiB/s"
#: deluge/common.py:521
#: deluge/common.py:527
msgid "G/s"
msgstr "G/s"
#: deluge/common.py:521
#: deluge/common.py:527
msgid "GiB/s"
msgstr "GiB/s"
#: deluge/common.py:527
#: deluge/common.py:533
msgid "T/s"
msgstr "T/s"
#: deluge/common.py:527
#: deluge/common.py:533
msgid "TiB/s"
msgstr "TiB/s"
@ -191,7 +191,7 @@ msgstr ""
msgid "Config keys to be unmodified by `set_config` RPC"
msgstr ""
#: deluge/ui/common.py:37 deluge/ui/gtk3/filtertreeview.py:135
#: deluge/ui/common.py:37 deluge/ui/gtk3/filtertreeview.py:130
#: deluge/ui/web/js/deluge-all/UI.js:18
msgid "All"
msgstr "Alle"
@ -232,7 +232,7 @@ msgid "Queued"
msgstr "Sat i kø"
#: deluge/ui/common.py:45 deluge/ui/common.py:122
#: deluge/ui/gtk3/statusbar.py:396 deluge/ui/gtk3/filtertreeview.py:136
#: deluge/ui/gtk3/statusbar.py:396 deluge/ui/gtk3/filtertreeview.py:131
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:330
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:94
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:114
@ -241,7 +241,9 @@ msgstr "Sat i kø"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:417
#: deluge/ui/web/js/deluge-all/UI.js:27
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:121
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:301
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:98
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:291
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:316
msgid "Error"
msgstr "Fejl"
@ -265,7 +267,7 @@ msgid "State"
msgstr "Status"
#: deluge/ui/common.py:54 deluge/ui/gtk3/createtorrentdialog.py:72
#: deluge/ui/gtk3/addtorrentdialog.py:118 deluge/ui/gtk3/files_tab.py:113
#: deluge/ui/gtk3/addtorrentdialog.py:123 deluge/ui/gtk3/files_tab.py:113
#: deluge/ui/gtk3/torrentview.py:283
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:174
#: deluge/ui/console/modes/preferences/preference_panes.py:738
@ -449,7 +451,7 @@ msgstr "Sti til flyt fuldførte"
msgid "Move On Completed Path"
msgstr ""
#: deluge/ui/common.py:115 deluge/ui/gtk3/filtertreeview.py:140
#: deluge/ui/common.py:115 deluge/ui/gtk3/filtertreeview.py:135
#: deluge/ui/gtk3/torrentview.py:416
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:499
#: deluge/ui/web/js/deluge-all/FilterPanel.js:32
@ -1621,7 +1623,7 @@ msgid "Daemon not running"
msgstr "Dæmon kører ikke"
#: deluge/ui/gtk3/createtorrentdialog.py:62
#: deluge/ui/gtk3/addtorrentdialog.py:105 deluge/ui/gtk3/files_tab.py:92
#: deluge/ui/gtk3/addtorrentdialog.py:110 deluge/ui/gtk3/files_tab.py:92
#: deluge/ui/web/js/deluge-all/details/FilesTab.js:18
#: deluge/ui/web/js/deluge-all/add/FilesTab.js:28
msgid "Filename"
@ -1640,7 +1642,7 @@ msgstr "Vælg en fil"
#: deluge/ui/gtk3/createtorrentdialog.py:132
#: deluge/ui/gtk3/createtorrentdialog.py:169
#: deluge/ui/gtk3/createtorrentdialog.py:258
#: deluge/ui/gtk3/addtorrentdialog.py:690 deluge/ui/gtk3/dialogs.py:203
#: deluge/ui/gtk3/addtorrentdialog.py:698 deluge/ui/gtk3/dialogs.py:203
#: deluge/ui/gtk3/dialogs.py:261 deluge/ui/gtk3/dialogs.py:273
#: deluge/ui/gtk3/dialogs.py:364 deluge/ui/gtk3/dialogs.py:427
#: deluge/ui/gtk3/preferences.py:1158
@ -1664,7 +1666,7 @@ msgstr ""
#: deluge/ui/gtk3/createtorrentdialog.py:134
#: deluge/ui/gtk3/createtorrentdialog.py:171
#: deluge/ui/gtk3/addtorrentdialog.py:692 deluge/ui/gtk3/preferences.py:1160
#: deluge/ui/gtk3/addtorrentdialog.py:700 deluge/ui/gtk3/preferences.py:1160
msgid "_Open"
msgstr ""
@ -1685,29 +1687,29 @@ msgid "_Save"
msgstr ""
#: deluge/ui/gtk3/createtorrentdialog.py:271
#: deluge/ui/gtk3/addtorrentdialog.py:704
#: deluge/ui/gtk3/addtorrentdialog.py:712
msgid "Torrent files"
msgstr "Torrent-filer"
#: deluge/ui/gtk3/createtorrentdialog.py:275
#: deluge/ui/gtk3/addtorrentdialog.py:708
#: deluge/ui/gtk3/addtorrentdialog.py:716
msgid "All files"
msgstr "Alle filer"
#: deluge/ui/gtk3/mainwindow.py:185
#: deluge/ui/gtk3/mainwindow.py:192
msgid "Enter your password to show Deluge..."
msgstr "Indtast din adgangskode for at vise Deluge..."
#: deluge/ui/gtk3/mainwindow.py:244
#: deluge/ui/gtk3/mainwindow.py:251
msgid "Enter your password to Quit Deluge..."
msgstr "Indtast din adgangskode for at afslutte Deluge..."
#: deluge/ui/gtk3/mainwindow.py:336
#: deluge/ui/gtk3/mainwindow.py:343
#, python-brace-format
msgid "D: {download_rate} U: {upload_rate} - Deluge"
msgstr ""
#: deluge/ui/gtk3/mainwindow.py:350 deluge/ui/gtk3/aboutdialog.py:26
#: deluge/ui/gtk3/mainwindow.py:357 deluge/ui/gtk3/aboutdialog.py:26
#: deluge/ui/gtk3/aboutdialog.py:27 deluge/ui/gtk3/systemtray.py:96
#: deluge/ui/gtk3/systemtray.py:184 deluge/ui/gtk3/systemtray.py:244
#: deluge/ui/data/share/applications/deluge.desktop.in.h:1
@ -1716,6 +1718,16 @@ msgstr ""
msgid "Deluge"
msgstr "Deluge"
#: deluge/ui/gtk3/path_combo_chooser.py:393
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:20
msgid "Edit path"
msgstr ""
#: deluge/ui/gtk3/path_combo_chooser.py:395
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:21
msgid "Remove path"
msgstr ""
#: deluge/ui/gtk3/options_tab.py:136
msgid "_Apply to selected"
msgstr ""
@ -1806,59 +1818,59 @@ msgstr "Server:"
msgid "libtorrent:"
msgstr "libtorrent:"
#: deluge/ui/gtk3/addtorrentdialog.py:97 deluge/ui/gtk3/queuedtorrents.py:51
#: deluge/ui/gtk3/addtorrentdialog.py:102 deluge/ui/gtk3/queuedtorrents.py:51
msgid "Torrent"
msgstr "Torrent"
#: deluge/ui/gtk3/addtorrentdialog.py:224
#: deluge/ui/gtk3/addtorrentdialog.py:232
#, python-format
msgid "Add Torrents (%d)"
msgstr ""
#: deluge/ui/gtk3/addtorrentdialog.py:230
#: deluge/ui/gtk3/addtorrentdialog.py:238
msgid "Duplicate torrent(s)"
msgstr ""
#: deluge/ui/gtk3/addtorrentdialog.py:232
#: deluge/ui/gtk3/addtorrentdialog.py:240
#, python-format
msgid ""
"You cannot add the same torrent twice. %d torrents were already added."
msgstr ""
#: deluge/ui/gtk3/addtorrentdialog.py:247
#: deluge/ui/gtk3/addtorrentdialog.py:255
msgid "Invalid File"
msgstr "Ugyldig fil"
#: deluge/ui/gtk3/addtorrentdialog.py:282
#: deluge/ui/gtk3/addtorrentdialog.py:290
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:8
msgid "Please wait for files..."
msgstr ""
#: deluge/ui/gtk3/addtorrentdialog.py:288
#: deluge/ui/gtk3/addtorrentdialog.py:296
msgid "Unable to download files for this magnet"
msgstr ""
#: deluge/ui/gtk3/addtorrentdialog.py:686
#: deluge/ui/gtk3/addtorrentdialog.py:694
msgid "Choose a .torrent file"
msgstr "Vælg en .torrent-fil"
#: deluge/ui/gtk3/addtorrentdialog.py:769
#: deluge/ui/gtk3/addtorrentdialog.py:777
msgid "Invalid URL"
msgstr "Ugyldig URL"
#: deluge/ui/gtk3/addtorrentdialog.py:770
#: deluge/ui/gtk3/addtorrentdialog.py:778
msgid "is not a valid URL."
msgstr "er ikke et gyldigt URL."
#: deluge/ui/gtk3/addtorrentdialog.py:776
#: deluge/ui/gtk3/addtorrentdialog.py:784
msgid "Downloading..."
msgstr "Downloader..."
#: deluge/ui/gtk3/addtorrentdialog.py:811
#: deluge/ui/gtk3/addtorrentdialog.py:819
msgid "Download Failed"
msgstr "Download mislykkedes"
#: deluge/ui/gtk3/addtorrentdialog.py:812
#: deluge/ui/gtk3/addtorrentdialog.py:820
msgid "Failed to download:"
msgstr "Fejlslagne download(s):"
@ -2153,29 +2165,29 @@ msgstr "Ned"
msgid "Up"
msgstr "Op"
#: deluge/ui/gtk3/gtkui.py:318
#: deluge/ui/gtk3/gtkui.py:313
msgid ""
"A Deluge daemon (deluged) is already running.\n"
"To use Standalone mode, stop local daemon and restart Deluge."
msgstr ""
#: deluge/ui/gtk3/gtkui.py:324
#: deluge/ui/gtk3/gtkui.py:319
msgid ""
"Only Thin Client mode is available because libtorrent is not installed.\n"
"To use Standalone mode, please install libtorrent package."
msgstr ""
#: deluge/ui/gtk3/gtkui.py:330 deluge/ui/gtk3/gtkui.py:336
#: deluge/ui/gtk3/gtkui.py:325 deluge/ui/gtk3/gtkui.py:331
msgid ""
"Only Thin Client mode is available due to unknown Import Error.\n"
"To use Standalone mode, please see logs for error details."
msgstr ""
#: deluge/ui/gtk3/gtkui.py:354
#: deluge/ui/gtk3/gtkui.py:349
msgid "Continue in Thin Client mode?"
msgstr ""
#: deluge/ui/gtk3/gtkui.py:355
#: deluge/ui/gtk3/gtkui.py:350
msgid "Change User Interface Mode"
msgstr ""
@ -2214,7 +2226,7 @@ msgstr "Version"
#: deluge/ui/gtk3/connectionmanager.py:219
#: deluge/ui/gtk3/glade/connection_manager.ui.h:8
msgid "_Start Daemon"
msgstr ""
msgstr "_Start dæmon"
#: deluge/ui/gtk3/connectionmanager.py:250
msgid "_Stop Daemon"
@ -2297,6 +2309,15 @@ msgstr ""
msgid "You must now restart the deluge UI for the changes to take effect."
msgstr ""
#: deluge/ui/gtk3/preferences.py:940
msgid "Thinclient"
msgstr ""
#: deluge/ui/gtk3/preferences.py:940
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:18
msgid "Standalone"
msgstr ""
#: deluge/ui/gtk3/preferences.py:942
msgid "Switching Deluge Client Mode..."
msgstr ""
@ -2365,39 +2386,39 @@ msgstr ""
msgid "An error occurred while removing account"
msgstr ""
#: deluge/ui/gtk3/filtertreeview.py:127
#: deluge/ui/gtk3/filtertreeview.py:122
#: deluge/ui/web/js/deluge-all/FilterPanel.js:28
msgid "States"
msgstr "Tilstande"
#: deluge/ui/gtk3/filtertreeview.py:133
#: deluge/ui/gtk3/filtertreeview.py:128
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:23
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:21
#: deluge/ui/web/js/deluge-all/FilterPanel.js:30
msgid "Trackers"
msgstr "Trackere"
#: deluge/ui/gtk3/filtertreeview.py:137 deluge/ui/gtk3/filtertreeview.py:143
#: deluge/ui/gtk3/filtertreeview.py:132 deluge/ui/gtk3/filtertreeview.py:138
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:7
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:33
msgid "None"
msgstr "Ingen"
#: deluge/ui/gtk3/filtertreeview.py:142
#: deluge/ui/gtk3/filtertreeview.py:137
msgid "Admin"
msgstr "Admin"
#: deluge/ui/gtk3/filtertreeview.py:164
#: deluge/ui/gtk3/filtertreeview.py:159
#: deluge/ui/web/js/deluge-all/FilterPanel.js:34
msgid "Labels"
msgstr "Etiketter"
#: deluge/ui/gtk3/filtertreeview.py:209
#: deluge/ui/gtk3/filtertreeview.py:204
#: deluge/plugins/Label/deluge_label/gtkui/submenu.py:28
msgid "No Label"
msgstr "Ingen Etiket"
#: deluge/ui/gtk3/filtertreeview.py:211
#: deluge/ui/gtk3/filtertreeview.py:206
msgid "No Owner"
msgstr ""
@ -2536,7 +2557,7 @@ msgstr "Afslut og _stop dæmon"
#: deluge/ui/gtk3/glade/main_window.ui.h:5
#: deluge/ui/gtk3/glade/tray_menu.ui.h:8
msgid "_Quit"
msgstr ""
msgstr "_Afslut"
#: deluge/ui/gtk3/glade/main_window.ui.h:6
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:192
@ -2794,10 +2815,6 @@ msgstr ""
msgid "I2P"
msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:18
msgid "Standalone"
msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:19
msgid "The standalone self-contained application"
msgstr ""
@ -2912,7 +2929,7 @@ msgid "System Default"
msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:48
msgid "<b>Languge</b>"
msgid "<b>Language</b>"
msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:49
@ -3740,14 +3757,6 @@ msgstr ""
msgid "Ctrl+D"
msgstr ""
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:20
msgid "Edit path"
msgstr ""
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:21
msgid "Remove path"
msgstr ""
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:22
msgid "Toggle hidden files"
msgstr ""
@ -5528,36 +5537,36 @@ msgstr "Pop op-notifikation er ikke slået til."
msgid "libnotify is not installed"
msgstr ""
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:183
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:185
msgid "Failed to popup notification"
msgstr ""
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:186
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:188
msgid "Notification popup shown"
msgstr "Notifikations-pop op vist"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:190
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:192
msgid "Sound notification not enabled"
msgstr "Lydnotifikation ikke slået til"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:192
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:194
msgid "pygame is not installed"
msgstr "pygame er ikke installeret"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:204
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:206
#, python-format
msgid "Sound notification failed %s"
msgstr "Lydnotifikation fejlede %s"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:208
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:210
msgid "Sound notification Success"
msgstr "Lydpåmindelse lykkedes"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:232
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:234
msgid "Finished Torrent"
msgstr "Afsluttet torrent"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:236
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:238
#, python-format
msgid ""
"The torrent \"%(name)s\" including %(num_files)i file(s) has finished "
@ -5566,12 +5575,12 @@ msgstr ""
"Torrenten \"%(name)s\" inklusiv %(num_files)i fil(er) er færdige med at "
"downloade."
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:285
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:315
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:287
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:317
msgid "Notifications"
msgstr "Notifikationer"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:661
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:663
msgid "Choose Sound File"
msgstr "Vælg lydfil"
@ -6173,6 +6182,10 @@ msgstr "Adresse"
msgid "Cookies"
msgstr "Cookies"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:99
msgid "Failed to download torrent"
msgstr ""
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:133
msgid "File"
msgstr "Fil"
@ -6181,11 +6194,15 @@ msgstr "Fil"
msgid "Infohash"
msgstr "Infohash"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:259
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:260
msgid "Uploading your torrent..."
msgstr "Uploader din torrent..."
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:302
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:292
msgid "Failed to upload torrent"
msgstr ""
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:317
msgid "Not a valid torrent"
msgstr "Ikke en gyldig torrent"
@ -6220,171 +6237,3 @@ msgstr ""
#: deluge/ui/web/render/tab_status.html:26
msgid "Date Added:"
msgstr "Dato tilføjet:"
#~ msgid "pynotify is not installed"
#~ msgstr "pynotify er ikke installeret"
#~ msgid "pynotify failed to show notification"
#~ msgstr "pynotify kunne ikke vise notifikation"
#~ msgid "<b><i><big>Notifications</big></i></b>"
#~ msgstr "<b><i><big>Notifikationer</big></i></b>"
#~ msgid "_Normal Priority"
#~ msgstr "_Normal prioritet"
#~ msgid "_High Priority"
#~ msgstr "_Høj prioritet"
#~ msgid "Associate Magnet links with Deluge"
#~ msgstr "Associer Magnet-links med Deluge"
#~ msgid "Bulgarian"
#~ msgstr "Bulgarsk"
#~ msgid "Arabic"
#~ msgstr "Arabisk"
#~ msgid "German"
#~ msgstr "Tysk"
#~ msgid "Danish"
#~ msgstr "Dansk"
#~ msgid "Bosnian"
#~ msgstr "Bosnisk"
#~ msgid "Czech"
#~ msgstr "Tjekkisk"
#~ msgid "Belarusian"
#~ msgstr "Hviderussisk"
#~ msgid "Bengali"
#~ msgstr "Bengalsk"
#~ msgid "Greek"
#~ msgstr "Græsk"
#~ msgid "English (Australia)"
#~ msgstr "Engelsk (Australien)"
#~ msgid "English (Canada)"
#~ msgstr "Engelsk (Canada)"
#~ msgid "English"
#~ msgstr "Engelsk"
#~ msgid "Spanish"
#~ msgstr "Spansk"
#~ msgid "English (United Kingdom)"
#~ msgstr "Engelsk (Storbritannien)"
#~ msgid "Esperanto"
#~ msgstr "Esperanto"
#~ msgid "Afrikaans"
#~ msgstr "Afrikaans"
#~ msgid "Irish"
#~ msgstr "Irsk"
#~ msgid "French"
#~ msgstr "Fransk"
#~ msgid "Finnish"
#~ msgstr "Finsk"
#~ msgid "Persian"
#~ msgstr "Persisk"
#~ msgid "Croatian"
#~ msgstr "Kroatisk"
#~ msgid "Indonesian"
#~ msgstr "Indonesisk"
#~ msgid "Icelandic"
#~ msgstr "Islandsk"
#~ msgid "Italian"
#~ msgstr "Italiensk"
#~ msgid "Interlingua"
#~ msgstr "Interlingua"
#~ msgid "Japanese"
#~ msgstr "Japansk"
#~ msgid "Macedonian"
#~ msgstr "Makedonsk"
#~ msgid "Korean"
#~ msgstr "Koreansk"
#~ msgid "Latin"
#~ msgstr "Latinsk"
#~ msgid "Kurdish"
#~ msgstr "Kurdisk"
#~ msgid "Mongolian"
#~ msgstr "Mongolsk"
#~ msgid "Polish"
#~ msgstr "Polsk"
#~ msgid "Burmese"
#~ msgstr "Burmesisk"
#~ msgid "Slovenian"
#~ msgstr "Slovensk"
#~ msgid "Slovak"
#~ msgstr "Slovakisk"
#~ msgid "Russian"
#~ msgstr "Russisk"
#~ msgid "Portuguese"
#~ msgstr "Portugisisk"
#~ msgid "Serbian"
#~ msgstr "Serbisk"
#~ msgid "Albanian"
#~ msgstr "Albansk"
#~ msgid "Traditional Chinese"
#~ msgstr "Kinesisk (traditionel)"
#~ msgid "Simplified Chinese"
#~ msgstr "Kinesisk (forenklet)"
#~ msgid "Vietnamese"
#~ msgstr "Vietnamesisk"
#~ msgid "Chinese (Hong Kong)"
#~ msgstr "Kinesisk (Hong Kong)"
#~ msgid "Chinese (Simplified)"
#~ msgstr "Kinesisk (forenklet)"
#~ msgid "Chinese (Taiwan)"
#~ msgstr "Kinesisk (Taiwan)"
#~ msgid "Ignore"
#~ msgstr "Ignorer"
#~ msgid "Estonian"
#~ msgstr "Estisk"
#~ msgid "Hebrew"
#~ msgstr "Hebraisk"
#~ msgid "Hungarian"
#~ msgstr "Ungarsk"
#~ msgid "Dutch"
#~ msgstr "Hollandsk"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6164
deluge/i18n/fo.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6164
deluge/i18n/ga.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6172
deluge/i18n/km.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6164
deluge/i18n/ky.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,6 @@
# This file is public domain.
#
from __future__ import unicode_literals
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'

6164
deluge/i18n/lb.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6164
deluge/i18n/ml.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6172
deluge/i18n/nap.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6180
deluge/i18n/nn.po Normal file

File diff suppressed because it is too large Load Diff

6171
deluge/i18n/oc.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More