summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.github/workflows/cd.yml104
-rw-r--r--.github/workflows/ci.yml94
-rw-r--r--.github/workflows/docs.yml23
-rw-r--r--.github/workflows/lint.yml6
-rw-r--r--.gitignore6
-rw-r--r--.pre-commit-config.yaml29
-rw-r--r--.pylintrc7
-rw-r--r--.readthedocs.yml11
-rw-r--r--.travis.yml98
-rw-r--r--CHANGELOG.md157
-rw-r--r--DEPENDS.md21
-rw-r--r--MANIFEST.in2
-rw-r--r--README.md5
-rw-r--r--deluge/_libtorrent.py7
-rw-r--r--deluge/argparserbase.py21
-rw-r--r--deluge/bencode.py15
-rw-r--r--deluge/common.py356
-rw-r--r--deluge/component.py87
-rw-r--r--deluge/config.py178
-rw-r--r--deluge/configmanager.py5
-rw-r--r--deluge/conftest.py214
-rw-r--r--deluge/core/alertmanager.py141
-rw-r--r--deluge/core/authmanager.py28
-rw-r--r--deluge/core/core.py531
-rw-r--r--deluge/core/daemon.py13
-rw-r--r--deluge/core/daemon_entry.py3
-rw-r--r--deluge/core/eventmanager.py3
-rw-r--r--deluge/core/filtermanager.py7
-rw-r--r--deluge/core/pluginmanager.py3
-rw-r--r--deluge/core/preferencesmanager.py34
-rw-r--r--deluge/core/rpcserver.py84
-rw-r--r--deluge/core/torrent.py169
-rw-r--r--deluge/core/torrentmanager.py259
-rw-r--r--deluge/crypto_utils.py61
-rw-r--r--deluge/decorators.py73
-rw-r--r--deluge/error.py18
-rw-r--r--deluge/event.py9
-rw-r--r--deluge/httpdownloader.py20
-rw-r--r--deluge/i18n/af.po12
-rw-r--r--deluge/i18n/ar.po12
-rw-r--r--deluge/i18n/ast.po12
-rw-r--r--deluge/i18n/be.po1910
-rw-r--r--deluge/i18n/bg.po12
-rw-r--r--deluge/i18n/bn.po12
-rw-r--r--deluge/i18n/bs.po12
-rw-r--r--deluge/i18n/ca.po79
-rw-r--r--deluge/i18n/cs.po12
-rw-r--r--deluge/i18n/cy.po12
-rw-r--r--deluge/i18n/da.po12
-rw-r--r--deluge/i18n/de.po66
-rw-r--r--deluge/i18n/el.po12
-rw-r--r--deluge/i18n/en_AU.po12
-rw-r--r--deluge/i18n/en_CA.po12
-rw-r--r--deluge/i18n/en_GB.po939
-rw-r--r--deluge/i18n/eo.po12
-rw-r--r--deluge/i18n/es.po12
-rw-r--r--deluge/i18n/et.po12
-rw-r--r--deluge/i18n/eu.po12
-rw-r--r--deluge/i18n/fa.po12
-rw-r--r--deluge/i18n/fi.po230
-rw-r--r--deluge/i18n/fo.po12
-rw-r--r--deluge/i18n/fr.po14
-rw-r--r--deluge/i18n/fy.po12
-rw-r--r--deluge/i18n/ga.po12
-rw-r--r--deluge/i18n/gl.po12
-rw-r--r--deluge/i18n/he.po12
-rw-r--r--deluge/i18n/hi.po98
-rw-r--r--deluge/i18n/hr.po14
-rw-r--r--deluge/i18n/hu.po12
-rw-r--r--deluge/i18n/id.po12
-rw-r--r--deluge/i18n/is.po12
-rw-r--r--deluge/i18n/it.po12
-rw-r--r--deluge/i18n/iu.po12
-rw-r--r--deluge/i18n/ja.po12
-rw-r--r--deluge/i18n/ka.po12
-rw-r--r--deluge/i18n/kk.po12
-rw-r--r--deluge/i18n/km.po12
-rw-r--r--deluge/i18n/kn.po12
-rw-r--r--deluge/i18n/ko.po12
-rw-r--r--deluge/i18n/ku.po12
-rw-r--r--deluge/i18n/ky.po12
-rw-r--r--deluge/i18n/la.po12
-rw-r--r--deluge/i18n/languages.py3
-rw-r--r--deluge/i18n/lb.po12
-rw-r--r--deluge/i18n/lt.po12
-rw-r--r--deluge/i18n/lv.po18
-rw-r--r--deluge/i18n/mk.po12
-rw-r--r--deluge/i18n/ml.po12
-rw-r--r--deluge/i18n/mo.po6164
-rw-r--r--deluge/i18n/ms.po12
-rw-r--r--deluge/i18n/nap.po12
-rw-r--r--deluge/i18n/nb.po12
-rw-r--r--deluge/i18n/nds.po12
-rw-r--r--deluge/i18n/nl.po42
-rw-r--r--deluge/i18n/nn.po12
-rw-r--r--deluge/i18n/oc.po12
-rw-r--r--deluge/i18n/pl.po110
-rw-r--r--deluge/i18n/pms.po12
-rw-r--r--deluge/i18n/pt.po12
-rw-r--r--deluge/i18n/pt_BR.po12
-rw-r--r--deluge/i18n/ro.po12
-rw-r--r--deluge/i18n/ru.po1306
-rw-r--r--deluge/i18n/si.po12
-rw-r--r--deluge/i18n/sk.po12
-rw-r--r--deluge/i18n/sl.po12
-rw-r--r--deluge/i18n/sr.po12
-rw-r--r--deluge/i18n/sv.po14
-rw-r--r--deluge/i18n/ta.po12
-rw-r--r--deluge/i18n/te.po12
-rw-r--r--deluge/i18n/th.po12
-rw-r--r--deluge/i18n/tl.po12
-rw-r--r--deluge/i18n/tlh.po12
-rw-r--r--deluge/i18n/tr.po12
-rw-r--r--deluge/i18n/uk.po125
-rw-r--r--deluge/i18n/ur.po12
-rw-r--r--deluge/i18n/util.py31
-rw-r--r--deluge/i18n/vi.po12
-rw-r--r--deluge/i18n/zh_CN.po26
-rw-r--r--deluge/i18n/zh_HK.po12
-rw-r--r--deluge/i18n/zh_TW.po12
-rw-r--r--deluge/log.py49
-rw-r--r--deluge/maketorrent.py7
-rw-r--r--deluge/metafile.py418
-rw-r--r--deluge/path_chooser_common.py12
-rw-r--r--deluge/pluginmanagerbase.py59
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/__init__.py9
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/common.py3
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/core.py21
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd.js31
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.js5
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui20
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py9
-rw-r--r--deluge/plugins/AutoAdd/deluge_autoadd/webui.py3
-rw-r--r--deluge/plugins/AutoAdd/setup.py1
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/__init__.py9
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/common.py14
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/core.py10
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/data/blocklist.js2
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.pngbin1176 -> 1091 bytes
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui6
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/decompressers.py3
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/detect.py3
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/gtkui.py5
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py10
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/readers.py5
-rw-r--r--deluge/plugins/Blocklist/deluge_blocklist/webui.py4
-rw-r--r--deluge/plugins/Blocklist/setup.py1
-rw-r--r--deluge/plugins/Execute/deluge_execute/__init__.py9
-rw-r--r--deluge/plugins/Execute/deluge_execute/common.py3
-rw-r--r--deluge/plugins/Execute/deluge_execute/core.py3
-rw-r--r--deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui2
-rw-r--r--deluge/plugins/Execute/deluge_execute/gtkui.py7
-rw-r--r--deluge/plugins/Execute/deluge_execute/webui.py4
-rw-r--r--deluge/plugins/Execute/setup.py1
-rw-r--r--deluge/plugins/Extractor/deluge_extractor/__init__.py9
-rw-r--r--deluge/plugins/Extractor/deluge_extractor/common.py3
-rw-r--r--deluge/plugins/Extractor/deluge_extractor/core.py10
-rw-r--r--deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui2
-rw-r--r--deluge/plugins/Extractor/deluge_extractor/gtkui.py5
-rw-r--r--deluge/plugins/Extractor/deluge_extractor/webui.py4
-rw-r--r--deluge/plugins/Extractor/setup.py1
-rw-r--r--deluge/plugins/Label/deluge_label/__init__.py9
-rw-r--r--deluge/plugins/Label/deluge_label/common.py3
-rw-r--r--deluge/plugins/Label/deluge_label/core.py7
-rw-r--r--deluge/plugins/Label/deluge_label/data/label.js20
-rw-r--r--deluge/plugins/Label/deluge_label/data/label_add.ui2
-rw-r--r--deluge/plugins/Label/deluge_label/data/label_options.ui12
-rw-r--r--deluge/plugins/Label/deluge_label/gtkui/__init__.py3
-rw-r--r--deluge/plugins/Label/deluge_label/gtkui/label_config.py5
-rw-r--r--deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py16
-rw-r--r--deluge/plugins/Label/deluge_label/gtkui/submenu.py5
-rw-r--r--deluge/plugins/Label/deluge_label/test.py3
-rw-r--r--deluge/plugins/Label/deluge_label/webui.py3
-rw-r--r--deluge/plugins/Label/setup.py1
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/__init__.py9
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/common.py5
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/core.py6
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/data/config.ui10
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/gtkui.py3
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/test.py11
-rw-r--r--deluge/plugins/Notifications/deluge_notifications/webui.py4
-rwxr-xr-xdeluge/plugins/Notifications/setup.py1
-rw-r--r--deluge/plugins/Scheduler/deluge_scheduler/__init__.py9
-rw-r--r--deluge/plugins/Scheduler/deluge_scheduler/common.py3
-rw-r--r--deluge/plugins/Scheduler/deluge_scheduler/core.py3
-rw-r--r--deluge/plugins/Scheduler/deluge_scheduler/gtkui.py5
-rw-r--r--deluge/plugins/Scheduler/deluge_scheduler/webui.py4
-rw-r--r--deluge/plugins/Scheduler/setup.py1
-rw-r--r--deluge/plugins/Stats/deluge_stats/__init__.py9
-rw-r--r--deluge/plugins/Stats/deluge_stats/common.py3
-rw-r--r--deluge/plugins/Stats/deluge_stats/core.py3
-rw-r--r--deluge/plugins/Stats/deluge_stats/graph.py155
-rw-r--r--deluge/plugins/Stats/deluge_stats/gtkui.py14
-rw-r--r--deluge/plugins/Stats/deluge_stats/tests/test_stats.py60
-rw-r--r--deluge/plugins/Stats/deluge_stats/webui.py4
-rw-r--r--deluge/plugins/Stats/setup.py1
-rw-r--r--deluge/plugins/Toggle/deluge_toggle/__init__.py9
-rw-r--r--deluge/plugins/Toggle/deluge_toggle/common.py3
-rw-r--r--deluge/plugins/Toggle/deluge_toggle/core.py3
-rw-r--r--deluge/plugins/Toggle/deluge_toggle/gtkui.py3
-rw-r--r--deluge/plugins/Toggle/deluge_toggle/webui.py4
-rw-r--r--deluge/plugins/Toggle/setup.py1
-rw-r--r--deluge/plugins/WebUi/deluge_webui/__init__.py9
-rw-r--r--deluge/plugins/WebUi/deluge_webui/common.py3
-rw-r--r--deluge/plugins/WebUi/deluge_webui/core.py3
-rw-r--r--deluge/plugins/WebUi/deluge_webui/data/config.ui2
-rw-r--r--deluge/plugins/WebUi/deluge_webui/gtkui.py3
-rw-r--r--deluge/plugins/WebUi/deluge_webui/tests/test_plugin_webui.py31
-rw-r--r--deluge/plugins/WebUi/setup.py1
-rw-r--r--deluge/plugins/init.py5
-rw-r--r--deluge/plugins/pluginbase.py26
-rwxr-xr-xdeluge/scripts/create_deluge_pngs71
-rwxr-xr-xdeluge/scripts/create_icons.py201
-rw-r--r--deluge/scripts/create_plugin.py38
-rw-r--r--deluge/scripts/deluge_remote.py3
-rw-r--r--deluge/tests/__init__.py2
-rw-r--r--deluge/tests/basetest.py59
-rw-r--r--deluge/tests/common.py70
-rw-r--r--deluge/tests/common_web.py26
-rw-r--r--deluge/tests/daemon_base.py31
-rw-r--r--deluge/tests/data/dir_with_single_file.torrent1
-rw-r--r--deluge/tests/data/seo.icobin1150 -> 0 bytes
-rw-r--r--deluge/tests/data/seo.svg1
-rw-r--r--deluge/tests/data/v2_hybrid.torrentbin0 -> 613 bytes
-rw-r--r--deluge/tests/data/v2_test.torrentbin0 -> 345 bytes
-rw-r--r--deluge/tests/test_alertmanager.py93
-rw-r--r--deluge/tests/test_authmanager.py10
-rw-r--r--deluge/tests/test_bencode.py18
-rw-r--r--deluge/tests/test_client.py88
-rw-r--r--deluge/tests/test_common.py233
-rw-r--r--deluge/tests/test_component.py365
-rw-r--r--deluge/tests/test_config.py156
-rw-r--r--deluge/tests/test_core.py273
-rw-r--r--deluge/tests/test_decorators.py20
-rw-r--r--deluge/tests/test_error.py33
-rw-r--r--deluge/tests/test_files_tab.py66
-rw-r--r--deluge/tests/test_httpdownloader.py182
-rw-r--r--deluge/tests/test_json_api.py184
-rw-r--r--deluge/tests/test_log.py10
-rw-r--r--deluge/tests/test_maketorrent.py33
-rw-r--r--deluge/tests/test_maybe_coroutine.py207
-rw-r--r--deluge/tests/test_metafile.py84
-rw-r--r--deluge/tests/test_plugin_metadata.py36
-rw-r--r--deluge/tests/test_rpcserver.py47
-rw-r--r--deluge/tests/test_security.py80
-rw-r--r--deluge/tests/test_sessionproxy.py50
-rw-r--r--deluge/tests/test_torrent.py142
-rw-r--r--deluge/tests/test_torrentmanager.py114
-rw-r--r--deluge/tests/test_torrentview.py171
-rw-r--r--deluge/tests/test_tracker_icons.py72
-rw-r--r--deluge/tests/test_transfer.py49
-rw-r--r--deluge/tests/test_ui_common.py175
-rw-r--r--deluge/tests/test_ui_console.py71
-rw-r--r--deluge/tests/test_ui_entry.py327
-rw-r--r--deluge/tests/test_ui_gtk3.py22
-rw-r--r--deluge/tests/test_web_api.py118
-rw-r--r--deluge/tests/test_web_auth.py11
-rw-r--r--deluge/tests/test_webserver.py83
-rw-r--r--deluge/tests/twisted/plugins/delugereporter.py50
-rw-r--r--deluge/transfer.py19
-rw-r--r--deluge/ui/client.py45
-rw-r--r--deluge/ui/common.py234
-rw-r--r--deluge/ui/console/__init__.py13
-rw-r--r--deluge/ui/console/cmdline/command.py8
-rw-r--r--deluge/ui/console/cmdline/commands/__init__.py3
-rw-r--r--deluge/ui/console/cmdline/commands/add.py13
-rw-r--r--deluge/ui/console/cmdline/commands/cache.py5
-rw-r--r--deluge/ui/console/cmdline/commands/config.py9
-rw-r--r--deluge/ui/console/cmdline/commands/connect.py12
-rw-r--r--deluge/ui/console/cmdline/commands/debug.py3
-rw-r--r--deluge/ui/console/cmdline/commands/gui.py3
-rw-r--r--deluge/ui/console/cmdline/commands/halt.py3
-rw-r--r--deluge/ui/console/cmdline/commands/help.py3
-rw-r--r--deluge/ui/console/cmdline/commands/info.py40
-rw-r--r--deluge/ui/console/cmdline/commands/manage.py9
-rw-r--r--deluge/ui/console/cmdline/commands/move.py5
-rw-r--r--deluge/ui/console/cmdline/commands/pause.py3
-rw-r--r--deluge/ui/console/cmdline/commands/plugin.py3
-rw-r--r--deluge/ui/console/cmdline/commands/quit.py3
-rw-r--r--deluge/ui/console/cmdline/commands/recheck.py3
-rw-r--r--deluge/ui/console/cmdline/commands/resume.py3
-rw-r--r--deluge/ui/console/cmdline/commands/rm.py9
-rw-r--r--deluge/ui/console/cmdline/commands/status.py12
-rw-r--r--deluge/ui/console/cmdline/commands/update_tracker.py3
-rw-r--r--deluge/ui/console/console.py12
-rw-r--r--deluge/ui/console/eventlog.py125
-rw-r--r--deluge/ui/console/main.py468
-rw-r--r--deluge/ui/console/modes/add_util.py5
-rw-r--r--deluge/ui/console/modes/addtorrents.py15
-rw-r--r--deluge/ui/console/modes/basemode.py33
-rw-r--r--deluge/ui/console/modes/cmdline.py23
-rw-r--r--deluge/ui/console/modes/connectionmanager.py43
-rw-r--r--deluge/ui/console/modes/eventview.py7
-rw-r--r--deluge/ui/console/modes/preferences/__init__.py2
-rw-r--r--deluge/ui/console/modes/preferences/preference_panes.py15
-rw-r--r--deluge/ui/console/modes/preferences/preferences.py5
-rw-r--r--deluge/ui/console/modes/torrentdetail.py39
-rw-r--r--deluge/ui/console/modes/torrentlist/__init__.py5
-rw-r--r--deluge/ui/console/modes/torrentlist/add_torrents_popup.py5
-rw-r--r--deluge/ui/console/modes/torrentlist/filtersidebar.py3
-rw-r--r--deluge/ui/console/modes/torrentlist/queue_mode.py5
-rw-r--r--deluge/ui/console/modes/torrentlist/search_mode.py8
-rw-r--r--deluge/ui/console/modes/torrentlist/torrentactions.py8
-rw-r--r--deluge/ui/console/modes/torrentlist/torrentlist.py9
-rw-r--r--deluge/ui/console/modes/torrentlist/torrentview.py11
-rw-r--r--deluge/ui/console/modes/torrentlist/torrentviewcolumns.py3
-rw-r--r--deluge/ui/console/parser.py15
-rw-r--r--deluge/ui/console/utils/colors.py11
-rw-r--r--deluge/ui/console/utils/column.py3
-rw-r--r--deluge/ui/console/utils/common.py3
-rw-r--r--deluge/ui/console/utils/config.py118
-rw-r--r--deluge/ui/console/utils/curses_util.py7
-rw-r--r--deluge/ui/console/utils/format_utils.py19
-rw-r--r--deluge/ui/console/widgets/__init__.py2
-rw-r--r--deluge/ui/console/widgets/fields.py42
-rw-r--r--deluge/ui/console/widgets/inputpane.py3
-rw-r--r--deluge/ui/console/widgets/popup.py10
-rw-r--r--deluge/ui/console/widgets/sidebar.py3
-rw-r--r--deluge/ui/console/widgets/statusbars.py20
-rw-r--r--deluge/ui/console/widgets/window.py5
-rw-r--r--deluge/ui/coreconfig.py3
-rw-r--r--deluge/ui/countries.py3
-rw-r--r--deluge/ui/data/icons/hicolor/128x128/apps/deluge.pngbin4365 -> 1536 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.pngbin551 -> 418 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/16x16/apps/deluge.pngbin551 -> 418 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/192x192/apps/deluge.pngbin6606 -> 2191 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.pngbin778 -> 492 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/22x22/apps/deluge.pngbin778 -> 492 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.pngbin877 -> 529 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/24x24/apps/deluge.pngbin877 -> 529 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/256x256/apps/deluge.pngbin8907 -> 2738 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/32x32/apps/deluge.pngbin1126 -> 597 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/36x36/apps/deluge.pngbin1270 -> 656 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/48x48/apps/deluge.pngbin1798 -> 797 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/512x512/apps/deluge.pngbin20135 -> 5090 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/64x64/apps/deluge.pngbin2324 -> 1021 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/72x72/apps/deluge.pngbin2535 -> 1016 bytes
-rw-r--r--deluge/ui/data/icons/hicolor/96x96/apps/deluge.pngbin3323 -> 1268 bytes
-rw-r--r--deluge/ui/data/pixmaps/active16.pngbin505 -> 411 bytes
-rw-r--r--deluge/ui/data/pixmaps/alert16.pngbin462 -> 331 bytes
-rw-r--r--deluge/ui/data/pixmaps/all16.pngbin533 -> 533 bytes
-rw-r--r--deluge/ui/data/pixmaps/checking16.pngbin519 -> 323 bytes
-rw-r--r--deluge/ui/data/pixmaps/deluge-about.pngbin6117 -> 4693 bytes
-rw-r--r--deluge/ui/data/pixmaps/deluge.icobin112928 -> 108469 bytes
-rw-r--r--deluge/ui/data/pixmaps/deluge.pngbin1798 -> 797 bytes
-rw-r--r--deluge/ui/data/pixmaps/deluge16.pngbin552 -> 418 bytes
-rw-r--r--deluge/ui/data/pixmaps/dht16.pngbin582 -> 515 bytes
-rw-r--r--deluge/ui/data/pixmaps/downloading16.pngbin465 -> 356 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ad.pngbin444 -> 439 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ae.pngbin275 -> 273 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/af.pngbin415 -> 412 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ag.pngbin431 -> 430 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/al.pngbin427 -> 422 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/am.pngbin322 -> 319 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/an.pngbin359 -> 353 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ao.pngbin381 -> 377 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/aq.pngbin582 -> 580 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ar.pngbin354 -> 352 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/as.pngbin514 -> 511 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/at.pngbin286 -> 284 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/au.pngbin554 -> 552 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/aw.pngbin376 -> 375 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ax.pngbin470 -> 463 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/az.pngbin411 -> 410 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ba.pngbin449 -> 447 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bd.pngbin355 -> 349 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bf.pngbin333 -> 332 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bh.pngbin331 -> 329 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bi.pngbin548 -> 529 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bj.pngbin307 -> 304 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bm.pngbin475 -> 472 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bn.pngbin486 -> 472 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bo.pngbin335 -> 332 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bs.pngbin386 -> 380 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bt.pngbin460 -> 452 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bv.pngbin380 -> 378 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bw.pngbin313 -> 311 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/by.pngbin365 -> 361 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/bz.pngbin457 -> 455 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ca.pngbin459 -> 455 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cc.pngbin480 -> 472 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cf.pngbin443 -> 437 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cg.pngbin366 -> 363 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ci.pngbin300 -> 296 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ck.pngbin474 -> 463 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cl.pngbin316 -> 313 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cm.pngbin335 -> 333 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cn.pngbin339 -> 338 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/co.pngbin322 -> 321 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cr.pngbin339 -> 336 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cu.pngbin416 -> 415 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cv.pngbin403 -> 401 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cx.pngbin459 -> 456 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/cy.pngbin318 -> 312 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/de.pngbin353 -> 351 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/dj.pngbin411 -> 410 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/dk.pngbin342 -> 340 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/dm.pngbin488 -> 483 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/do.pngbin361 -> 360 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/dz.pngbin442 -> 437 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ec.pngbin345 -> 340 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/eg.pngbin344 -> 334 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/eh.pngbin377 -> 375 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/er.pngbin480 -> 473 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/es.pngbin336 -> 333 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/et.pngbin424 -> 421 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/fi.pngbin355 -> 352 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/fj.pngbin485 -> 484 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/fo.pngbin367 -> 362 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/fr.pngbin363 -> 362 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ga.pngbin331 -> 330 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gb.pngbin526 -> 525 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gd.pngbin446 -> 441 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ge.pngbin468 -> 460 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gf.pngbin363 -> 362 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gg.pngbin445 -> 442 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gh.pngbin320 -> 318 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gi.pngbin354 -> 352 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gl.pngbin337 -> 335 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gn.pngbin309 -> 305 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gp.pngbin343 -> 339 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gq.pngbin391 -> 387 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gr.pngbin377 -> 373 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gt.pngbin327 -> 320 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gu.pngbin366 -> 363 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gw.pngbin339 -> 337 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/gy.pngbin500 -> 495 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/hk.pngbin380 -> 375 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/hm.pngbin554 -> 552 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/hn.pngbin394 -> 391 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/hr.pngbin368 -> 363 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ht.pngbin319 -> 316 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/id.pngbin291 -> 286 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/in.pngbin360 -> 357 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/iq.pngbin391 -> 382 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ir.pngbin385 -> 378 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/is.pngbin395 -> 393 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/it.pngbin277 -> 276 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/jp.pngbin299 -> 296 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ke.pngbin423 -> 416 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/kg.pngbin341 -> 339 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/kh.pngbin409 -> 404 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/km.pngbin432 -> 427 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/kp.pngbin409 -> 408 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/kr.pngbin484 -> 476 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/kw.pngbin341 -> 339 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ky.pngbin505 -> 501 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/kz.pngbin441 -> 438 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/la.pngbin395 -> 391 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lb.pngbin374 -> 370 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lc.pngbin454 -> 450 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/li.pngbin382 -> 378 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lk.pngbin454 -> 451 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lr.pngbin356 -> 353 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ls.pngbin480 -> 477 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lt.pngbin336 -> 331 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lu.pngbin320 -> 318 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/lv.pngbin329 -> 326 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ly.pngbin269 -> 268 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ma.pngbin286 -> 285 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mc.pngbin251 -> 247 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/md.pngbin393 -> 392 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/me.pngbin371 -> 369 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mg.pngbin304 -> 301 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mh.pngbin500 -> 492 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mk.pngbin433 -> 430 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ml.pngbin314 -> 309 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mm.pngbin334 -> 330 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mn.pngbin328 -> 326 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mo.pngbin440 -> 431 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mp.pngbin460 -> 457 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mq.pngbin524 -> 522 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mr.pngbin398 -> 393 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ms.pngbin472 -> 467 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mu.pngbin350 -> 347 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mv.pngbin385 -> 381 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mw.pngbin356 -> 353 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mx.pngbin409 -> 406 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/my.pngbin453 -> 451 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/mz.pngbin418 -> 413 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/na.pngbin531 -> 526 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/nc.pngbin450 -> 444 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ne.pngbin370 -> 365 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ng.pngbin334 -> 332 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ni.pngbin358 -> 355 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/nl.pngbin300 -> 297 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/no.pngbin380 -> 378 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/np.pngbin330 -> 322 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/nr.pngbin376 -> 373 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/nu.pngbin449 -> 443 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/nz.pngbin500 -> 495 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/om.pngbin334 -> 331 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pa.pngbin379 -> 375 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pf.pngbin373 -> 367 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pg.pngbin414 -> 409 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ph.pngbin406 -> 398 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pk.pngbin439 -> 434 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pl.pngbin236 -> 235 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pm.pngbin552 -> 543 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pr.pngbin420 -> 418 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ps.pngbin334 -> 333 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pt.pngbin391 -> 387 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/pw.pngbin403 -> 398 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/py.pngbin334 -> 333 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/qa.pngbin325 -> 320 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/re.pngbin363 -> 362 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ro.pngbin322 -> 315 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/rs.pngbin362 -> 357 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/rw.pngbin365 -> 361 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sa.pngbin418 -> 415 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sc.pngbin464 -> 457 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sd.pngbin349 -> 343 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/se.pngbin374 -> 373 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sg.pngbin338 -> 330 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sh.pngbin498 -> 495 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/si.pngbin370 -> 367 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sj.pngbin380 -> 378 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sk.pngbin419 -> 417 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sl.pngbin311 -> 308 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sn.pngbin351 -> 347 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/so.pngbin366 -> 361 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sr.pngbin360 -> 359 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/st.pngbin415 -> 409 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sv.pngbin357 -> 356 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sy.pngbin312 -> 311 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/sz.pngbin486 -> 481 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tc.pngbin485 -> 484 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/td.pngbin370 -> 369 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tf.pngbin386 -> 379 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/th.pngbin324 -> 317 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tj.pngbin353 -> 351 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tl.pngbin377 -> 373 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tm.pngbin438 -> 431 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tn.pngbin358 -> 352 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/to.pngbin292 -> 290 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tp.pngbin445 -> 443 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tr.pngbin358 -> 356 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tt.pngbin460 -> 456 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/tz.pngbin488 -> 485 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ua.pngbin297 -> 294 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ug.pngbin374 -> 372 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/um.pngbin443 -> 442 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/us.pngbin455 -> 452 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/uy.pngbin396 -> 394 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/uz.pngbin390 -> 386 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/va.pngbin401 -> 396 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/vc.pngbin398 -> 397 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ve.pngbin384 -> 383 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/vg.pngbin485 -> 482 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/vi.pngbin503 -> 498 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/vn.pngbin310 -> 307 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/vu.pngbin423 -> 420 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/wf.pngbin429 -> 422 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ws.pngbin346 -> 342 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/ye.pngbin289 -> 287 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/yt.pngbin446 -> 439 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/yu.pngbin385 -> 378 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/za.pngbin497 -> 493 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/zm.pngbin349 -> 348 bytes
-rw-r--r--deluge/ui/data/pixmaps/flags/zw.pngbin440 -> 435 bytes
-rw-r--r--deluge/ui/data/pixmaps/inactive16.pngbin434 -> 283 bytes
-rw-r--r--deluge/ui/data/pixmaps/magnet16.pngbin303 -> 345 bytes
-rw-r--r--deluge/ui/data/pixmaps/magnet_add16.pngbin392 -> 414 bytes
-rw-r--r--deluge/ui/data/pixmaps/magnet_copy16.pngbin446 -> 469 bytes
-rw-r--r--deluge/ui/data/pixmaps/queued16.pngbin410 -> 341 bytes
-rw-r--r--deluge/ui/data/pixmaps/seeding16.pngbin505 -> 332 bytes
-rw-r--r--deluge/ui/data/pixmaps/tracker_all16.pngbin1097 -> 1022 bytes
-rw-r--r--deluge/ui/data/pixmaps/tracker_warning16.pngbin683 -> 626 bytes
-rw-r--r--deluge/ui/data/pixmaps/traffic16.pngbin394 -> 359 bytes
-rw-r--r--deluge/ui/data/share/applications/deluge.desktop.in1
-rw-r--r--deluge/ui/data/share/metainfo/deluge.metainfo.xml.in (renamed from deluge/ui/data/share/appdata/deluge.appdata.xml.in)0
-rw-r--r--deluge/ui/gtk3/__init__.py8
-rw-r--r--deluge/ui/gtk3/aboutdialog.py7
-rw-r--r--deluge/ui/gtk3/addtorrentdialog.py36
-rw-r--r--deluge/ui/gtk3/common.py76
-rw-r--r--deluge/ui/gtk3/connectionmanager.py14
-rw-r--r--deluge/ui/gtk3/createtorrentdialog.py5
-rw-r--r--deluge/ui/gtk3/details_tab.py7
-rw-r--r--deluge/ui/gtk3/dialogs.py121
-rw-r--r--deluge/ui/gtk3/edittrackersdialog.py70
-rw-r--r--deluge/ui/gtk3/files_tab.py12
-rw-r--r--deluge/ui/gtk3/filtertreeview.py11
-rw-r--r--deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui2
-rw-r--r--deluge/ui/gtk3/glade/add_torrent_dialog.ui12
-rw-r--r--deluge/ui/gtk3/glade/add_torrent_dialog.url.ui2
-rw-r--r--deluge/ui/gtk3/glade/connect_peer_dialog.ui2
-rw-r--r--deluge/ui/gtk3/glade/connection_manager.addhost.ui137
-rw-r--r--deluge/ui/gtk3/glade/connection_manager.ui2
-rw-r--r--deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui2
-rw-r--r--deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui2
-rw-r--r--deluge/ui/gtk3/glade/create_torrent_dialog.ui5
-rw-r--r--deluge/ui/gtk3/glade/edit_trackers.edit.ui2
-rw-r--r--deluge/ui/gtk3/glade/main_window.tabs.ui10
-rw-r--r--deluge/ui/gtk3/glade/main_window.ui40
-rw-r--r--deluge/ui/gtk3/glade/other_dialog.ui2
-rw-r--r--deluge/ui/gtk3/glade/path_combo_chooser.ui3
-rw-r--r--deluge/ui/gtk3/glade/preferences_dialog.ui202
-rw-r--r--deluge/ui/gtk3/glade/torrent_menu.ui16
-rw-r--r--deluge/ui/gtk3/gtkui.py17
-rw-r--r--deluge/ui/gtk3/ipcinterface.py13
-rw-r--r--deluge/ui/gtk3/listview.py21
-rw-r--r--deluge/ui/gtk3/mainwindow.py35
-rw-r--r--deluge/ui/gtk3/menubar.py94
-rw-r--r--deluge/ui/gtk3/menubar_osx.py57
-rw-r--r--deluge/ui/gtk3/new_release_dialog.py5
-rw-r--r--deluge/ui/gtk3/options_tab.py10
-rw-r--r--deluge/ui/gtk3/path_chooser.py5
-rwxr-xr-xdeluge/ui/gtk3/path_combo_chooser.py41
-rw-r--r--deluge/ui/gtk3/peers_tab.py15
-rw-r--r--deluge/ui/gtk3/piecesbar.py104
-rw-r--r--deluge/ui/gtk3/pluginmanager.py3
-rw-r--r--deluge/ui/gtk3/preferences.py141
-rw-r--r--deluge/ui/gtk3/queuedtorrents.py3
-rw-r--r--deluge/ui/gtk3/removetorrentdialog.py5
-rw-r--r--deluge/ui/gtk3/sidebar.py3
-rw-r--r--deluge/ui/gtk3/status_tab.py7
-rw-r--r--deluge/ui/gtk3/statusbar.py27
-rw-r--r--deluge/ui/gtk3/systemtray.py18
-rw-r--r--deluge/ui/gtk3/tab_data_funcs.py11
-rw-r--r--deluge/ui/gtk3/toolbar.py3
-rw-r--r--deluge/ui/gtk3/torrentdetails.py5
-rw-r--r--deluge/ui/gtk3/torrentview.py20
-rw-r--r--deluge/ui/gtk3/torrentview_data_funcs.py16
-rw-r--r--deluge/ui/gtk3/trackers_tab.py7
-rw-r--r--deluge/ui/hostlist.py73
-rw-r--r--deluge/ui/sessionproxy.py20
-rw-r--r--deluge/ui/tracker_icons.py139
-rw-r--r--deluge/ui/ui.py8
-rw-r--r--deluge/ui/ui_entry.py7
-rw-r--r--deluge/ui/web/__init__.py2
-rw-r--r--deluge/ui/web/auth.py7
-rw-r--r--deluge/ui/web/common.py7
-rw-r--r--deluge/ui/web/css/deluge.css2
-rw-r--r--deluge/ui/web/docs/template/resources/deluge.pngbin722 -> 641 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/form.pngbin478 -> 444 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/grid/hmenu-lock.pngbin648 -> 588 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/grid/hmenu-unlock.pngbin697 -> 650 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/panel/top-bottom.pngbin218 -> 216 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/shadow-c.pngbin118 -> 108 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/shadow-lr.pngbin135 -> 125 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/shadow.pngbin311 -> 260 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/slider/slider-bg.pngbin300 -> 288 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/slider/slider-thumb.pngbin933 -> 731 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/slider/slider-v-bg.pngbin288 -> 273 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/slider/slider-v-thumb.pngbin883 -> 688 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/tabs/tab-strip-bg.pngbin259 -> 207 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/window/left-corners.pngbin272 -> 247 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/window/left-right.pngbin135 -> 123 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/window/right-corners.pngbin340 -> 300 bytes
-rw-r--r--deluge/ui/web/docs/template/resources/images/default/window/top-bottom.pngbin211 -> 199 bytes
-rw-r--r--deluge/ui/web/icons/active.pngbin505 -> 411 bytes
-rw-r--r--deluge/ui/web/icons/add.pngbin358 -> 337 bytes
-rw-r--r--deluge/ui/web/icons/add_file.pngbin520 -> 506 bytes
-rw-r--r--deluge/ui/web/icons/add_url.pngbin815 -> 806 bytes
-rw-r--r--deluge/ui/web/icons/alert.pngbin462 -> 331 bytes
-rw-r--r--deluge/ui/web/icons/all.pngbin533 -> 533 bytes
-rw-r--r--deluge/ui/web/icons/back.pngbin443 -> 420 bytes
-rw-r--r--deluge/ui/web/icons/bottom.pngbin346 -> 332 bytes
-rw-r--r--deluge/ui/web/icons/checking.pngbin519 -> 323 bytes
-rw-r--r--deluge/ui/web/icons/connection_manager.pngbin464 -> 463 bytes
-rw-r--r--deluge/ui/web/icons/connections.pngbin560 -> 543 bytes
-rw-r--r--deluge/ui/web/icons/create.pngbin541 -> 528 bytes
-rw-r--r--deluge/ui/web/icons/deluge-192.pngbin6604 -> 2191 bytes
-rw-r--r--deluge/ui/web/icons/deluge-32.pngbin1126 -> 597 bytes
-rw-r--r--deluge/ui/web/icons/deluge-512.pngbin20126 -> 5090 bytes
-rw-r--r--deluge/ui/web/icons/deluge-apple-180.pngbin5367 -> 1984 bytes
-rw-r--r--deluge/ui/web/icons/deluge.pngbin552 -> 418 bytes
-rw-r--r--deluge/ui/web/icons/dht.pngbin582 -> 515 bytes
-rw-r--r--deluge/ui/web/icons/document.pngbin406 -> 381 bytes
-rw-r--r--deluge/ui/web/icons/down.pngbin427 -> 408 bytes
-rw-r--r--deluge/ui/web/icons/downloading.pngbin465 -> 356 bytes
-rw-r--r--deluge/ui/web/icons/drive.pngbin381 -> 356 bytes
-rw-r--r--deluge/ui/web/icons/edit_trackers.pngbin589 -> 578 bytes
-rw-r--r--deluge/ui/web/icons/error.pngbin730 -> 727 bytes
-rw-r--r--deluge/ui/web/icons/expand_all.pngbin612 -> 582 bytes
-rw-r--r--deluge/ui/web/icons/favicon.icobin15086 -> 15086 bytes
-rw-r--r--deluge/ui/web/icons/find_more.pngbin557 -> 532 bytes
-rw-r--r--deluge/ui/web/icons/forward.pngbin436 -> 419 bytes
-rw-r--r--deluge/ui/web/icons/help.pngbin772 -> 767 bytes
-rw-r--r--deluge/ui/web/icons/high.pngbin505 -> 495 bytes
-rw-r--r--deluge/ui/web/icons/home.pngbin550 -> 533 bytes
-rw-r--r--deluge/ui/web/icons/inactive.pngbin434 -> 283 bytes
-rw-r--r--deluge/ui/web/icons/install_plugin.pngbin674 -> 671 bytes
-rw-r--r--deluge/ui/web/icons/login.pngbin469 -> 460 bytes
-rw-r--r--deluge/ui/web/icons/logout.pngbin599 -> 588 bytes
-rw-r--r--deluge/ui/web/icons/low.pngbin358 -> 344 bytes
-rw-r--r--deluge/ui/web/icons/magnet.pngbin303 -> 345 bytes
-rw-r--r--deluge/ui/web/icons/magnet_add.pngbin392 -> 414 bytes
-rw-r--r--deluge/ui/web/icons/magnet_copy.pngbin446 -> 469 bytes
-rw-r--r--deluge/ui/web/icons/move.pngbin524 -> 511 bytes
-rw-r--r--deluge/ui/web/icons/no_download.pngbin581 -> 569 bytes
-rw-r--r--deluge/ui/web/icons/normal.pngbin436 -> 419 bytes
-rw-r--r--deluge/ui/web/icons/ok.pngbin766 -> 760 bytes
-rw-r--r--deluge/ui/web/icons/pause.pngbin331 -> 301 bytes
-rw-r--r--deluge/ui/web/icons/preferences.pngbin694 -> 676 bytes
-rw-r--r--deluge/ui/web/icons/queue.pngbin432 -> 413 bytes
-rw-r--r--deluge/ui/web/icons/queued.pngbin410 -> 341 bytes
-rw-r--r--deluge/ui/web/icons/recheck.pngbin557 -> 532 bytes
-rw-r--r--deluge/ui/web/icons/remove.pngbin194 -> 189 bytes
-rw-r--r--deluge/ui/web/icons/seeding.pngbin505 -> 332 bytes
-rw-r--r--deluge/ui/web/icons/start.pngbin358 -> 344 bytes
-rw-r--r--deluge/ui/web/icons/top.pngbin325 -> 303 bytes
-rw-r--r--deluge/ui/web/icons/traffic.pngbin394 -> 359 bytes
-rw-r--r--deluge/ui/web/icons/up.pngbin420 -> 407 bytes
-rw-r--r--deluge/ui/web/icons/update.pngbin675 -> 663 bytes
-rw-r--r--deluge/ui/web/icons/upload_slots.pngbin422 -> 409 bytes
-rw-r--r--deluge/ui/web/icons/warning.pngbin521 -> 510 bytes
-rw-r--r--deluge/ui/web/js/deluge-all/AboutWindow.js3
-rw-r--r--deluge/ui/web/js/deluge-all/AddTrackerWindow.js3
-rw-r--r--deluge/ui/web/js/deluge-all/ConnectionManager.js21
-rw-r--r--deluge/ui/web/js/deluge-all/CopyMagnetWindow.js2
-rw-r--r--deluge/ui/web/js/deluge-all/Deluge.js5
-rw-r--r--deluge/ui/web/js/deluge-all/EditTrackersWindow.js1
-rw-r--r--deluge/ui/web/js/deluge-all/FilterPanel.js2
-rw-r--r--deluge/ui/web/js/deluge-all/Formatters.js301
-rw-r--r--deluge/ui/web/js/deluge-all/Sidebar.js18
-rw-r--r--deluge/ui/web/js/deluge-all/TorrentGrid.js16
-rw-r--r--deluge/ui/web/js/deluge-all/UI.js31
-rw-r--r--deluge/ui/web/js/deluge-all/add/AddWindow.js18
-rw-r--r--deluge/ui/web/js/deluge-all/add/FilesTab.js1
-rw-r--r--deluge/ui/web/js/deluge-all/add/OptionsPanel.js5
-rw-r--r--deluge/ui/web/js/deluge-all/details/DetailsTab.js4
-rw-r--r--deluge/ui/web/js/deluge-all/details/FilesTab.js5
-rw-r--r--deluge/ui/web/js/deluge-all/details/OptionsTab.js4
-rw-r--r--deluge/ui/web/js/deluge-all/details/PeersTab.js2
-rw-r--r--deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js5
-rw-r--r--deluge/ui/web/js/deluge-all/preferences/DaemonPage.js2
-rw-r--r--deluge/ui/web/js/deluge-all/preferences/InterfacePage.js56
-rw-r--r--deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js1
-rw-r--r--deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js5
-rw-r--r--deluge/ui/web/json_api.py81
-rw-r--r--deluge/ui/web/pluginmanager.py28
-rw-r--r--deluge/ui/web/server.py119
-rw-r--r--deluge/ui/web/themes/images/access/grid/hmenu-lock.pngbin484 -> 477 bytes
-rw-r--r--deluge/ui/web/themes/images/access/grid/hmenu-unlock.pngbin548 -> 539 bytes
-rw-r--r--deluge/ui/web/themes/images/access/slider/slider-bg.pngbin185 -> 169 bytes
-rw-r--r--deluge/ui/web/themes/images/access/slider/slider-thumb.pngbin512 -> 500 bytes
-rw-r--r--deluge/ui/web/themes/images/access/slider/slider-v-thumb.pngbin481 -> 478 bytes
-rw-r--r--deluge/ui/web/themes/images/access/window/right-corners.pngbin138 -> 137 bytes
-rw-r--r--deluge/ui/web/themes/images/access/window/top-bottom.pngbin105 -> 104 bytes
-rw-r--r--deluge/ui/web/themes/images/default/grid/hmenu-lock.pngbin484 -> 477 bytes
-rw-r--r--deluge/ui/web/themes/images/default/grid/hmenu-unlock.pngbin548 -> 539 bytes
-rw-r--r--deluge/ui/web/themes/images/default/slider/slider-bg.pngbin191 -> 176 bytes
-rw-r--r--deluge/ui/web/themes/images/default/slider/slider-thumb.pngbin626 -> 619 bytes
-rw-r--r--deluge/ui/web/themes/images/default/slider/slider-v-thumb.pngbin601 -> 576 bytes
-rw-r--r--deluge/ui/web/themes/images/default/window/right-corners.pngbin138 -> 137 bytes
-rw-r--r--deluge/ui/web/themes/images/default/window/top-bottom.pngbin119 -> 116 bytes
-rw-r--r--deluge/ui/web/themes/images/gray/slider/slider-thumb.pngbin376 -> 348 bytes
-rw-r--r--deluge/ui/web/themes/images/gray/window/left-corners.pngbin205 -> 201 bytes
-rw-r--r--deluge/ui/web/themes/images/gray/window/right-corners.pngbin204 -> 202 bytes
-rw-r--r--deluge/ui/web/web.py8
-rw-r--r--docs/man/deluge-console.12
-rw-r--r--docs/requirements.txt9
-rw-r--r--docs/source/conf.py59
-rw-r--r--docs/source/contributing/index.md12
-rw-r--r--docs/source/contributing/testing.md34
l---------docs/source/depends.md1
-rw-r--r--docs/source/devguide/how-to/index.md12
-rw-r--r--docs/source/devguide/how-to/update-1.3-plugin.md15
-rw-r--r--docs/source/devguide/index.md13
-rw-r--r--docs/source/devguide/packaging/index.md10
-rw-r--r--docs/source/devguide/packaging/release.md21
-rw-r--r--docs/source/devguide/tutorials/index.md7
-rw-r--r--docs/source/how-to/index.md16
-rw-r--r--docs/source/how-to/launchd-service.md4
-rw-r--r--docs/source/how-to/systemd-service.md8
-rw-r--r--docs/source/intro/01-install.md126
-rw-r--r--docs/source/intro/index.md7
-rw-r--r--docs/source/releases/index.md9
-rwxr-xr-xgen_web_gettext.py5
-rwxr-xr-xgenerate_pot.py11
-rwxr-xr-xminify_web_js.py19
-rwxr-xr-xmsgfmt.py19
-rwxr-xr-xpackaging/source/make_release.py10
-rw-r--r--packaging/systemd/deluge-web.service2
-rw-r--r--packaging/systemd/user/deluge-web.service16
-rw-r--r--packaging/systemd/user/deluged.service13
-rw-r--r--packaging/win/README.md34
-rw-r--r--packaging/win/deluge-win-installer.nsi (renamed from packaging/win32/deluge-win32-installer.nsi)72
-rw-r--r--packaging/win/delugewin.spec183
-rw-r--r--packaging/win/installer-side.bmp (renamed from packaging/win32/installer-side.bmp)bin206038 -> 206038 bytes
-rw-r--r--packaging/win/installer-top.bmp (renamed from packaging/win32/installer-top.bmp)bin34254 -> 34254 bytes
-rw-r--r--packaging/win/pyi_rth_gtk_csd.py3
-rw-r--r--packaging/win/setup_nsis.py49
-rw-r--r--packaging/win32/DelugeStart Theme/etc/gtk-2.0/gtkrc25
-rw-r--r--packaging/win32/DelugeStart Theme/lib/gtk-2.0/2.10.0/engines/libmurrine.dllbin156686 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check1.pngbin112 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check2.pngbin200 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check3.pngbin112 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check4.pngbin196 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check5.pngbin183 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/checklight.pngbin81 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option1.pngbin121 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option2.pngbin168 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option3.pngbin124 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option4.pngbin171 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Icons/close.pngbin198 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-horiz.pngbin82 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-vert.pngbin82 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-insens.pngbin84 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-prelight.pngbin125 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz.pngbin127 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-insens.pngbin84 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-prelight.pngbin128 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert.pngbin132 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-horiz.pngbin86 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-vert.pngbin84 -> 0 bytes
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/gtkrc519
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/checkradiobutton179
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/menu-menubar50
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/notebook29
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/scrollbar125
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/tooltips21
-rw-r--r--packaging/win32/DelugeStart Theme/share/themes/DelugeStart/index.theme12
-rw-r--r--packaging/win32/Win32 README.txt22
-rw-r--r--packaging/win32/deluge-bbfreeze.py264
-rw-r--r--pyproject.toml28
-rw-r--r--requirements-ci.txt4
-rw-r--r--requirements-dev.txt1
-rw-r--r--requirements-tests.txt4
-rw-r--r--requirements.txt6
-rw-r--r--setup.cfg22
-rwxr-xr-xsetup.py70
-rw-r--r--tox.ini23
-rwxr-xr-xversion.py7
826 files changed, 16418 insertions, 10219 deletions
diff --git a/.gitattributes b/.gitattributes
index b73f27d2f..d3e940016 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,3 +3,4 @@
.gitignore export-ignore
*.py diff=python
ext-all.js diff=minjs
+*.state -merge -text
diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 000000000..8ef901006
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,104 @@
+name: Package
+
+on:
+ push:
+ tags:
+ - "deluge-*"
+ - "!deluge*-dev*"
+ branches:
+ - develop
+ pull_request:
+ types: [labeled, opened, synchronize, reopened]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+ inputs:
+ ref:
+ description: "Enter a tag or commit to package"
+ default: ""
+
+jobs:
+ windows_package:
+ runs-on: windows-2022
+ if: (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'package'))
+ strategy:
+ matrix:
+ arch: [x64, x86]
+ python: ["3.9"]
+ libtorrent: [2.0.7, 1.2.19]
+
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ # Checkout Deluge source to subdir to enable packaging any tag/commit
+ - name: Checkout Deluge source
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.inputs.ref }}
+ fetch-depth: 0
+ path: deluge_src
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python}}
+ architecture: ${{ matrix.arch }}
+ cache: pip
+
+ - name: Prepare pip
+ run: python -m pip install wheel setuptools==68.*
+
+ - name: Install GTK
+ run: |
+ $WebClient = New-Object System.Net.WebClient
+ $WebClient.DownloadFile("https://github.com/deluge-torrent/gvsbuild-release/releases/download/latest/gvsbuild-py${{ matrix.python }}-vs16-${{ matrix.arch }}.zip","C:\GTK.zip")
+ 7z x C:\GTK.zip -oc:\GTK
+ echo "C:\GTK\release\lib" | Out-File -FilePath $env:GITHUB_PATH -Append
+ echo "C:\GTK\release\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
+ echo "C:\GTK\release" | Out-File -FilePath $env:GITHUB_PATH -Append
+ python -m pip install --no-index --find-links="C:\GTK\release\python" pycairo PyGObject
+
+ - name: Install Python dependencies
+ # Pillow no longer provides 32-bit wheels for Windows
+ # so specify only-binary to install old version.
+ run: >
+ python -m pip install
+ --only-binary=pillow
+ twisted[tls]==22.8.0
+ libtorrent==${{ matrix.libtorrent }}
+ pyinstaller
+ pygame
+ -r requirements.txt
+
+ - name: Install Deluge
+ working-directory: deluge_src
+ run: |
+ python -m pip install .
+ python setup.py install_scripts
+
+ - name: Freeze Deluge
+ working-directory: packaging/win
+ run: |
+ pyinstaller --clean delugewin.spec --distpath freeze
+
+ - name: Verify Deluge exes
+ working-directory: packaging/win/freeze/Deluge/
+ run: |
+ deluge-debug.exe -v
+ deluged-debug.exe -v
+ deluge-web-debug.exe -v
+ deluge-console -v
+
+ - name: Make Deluge Installer
+ working-directory: ./packaging/win
+ run: |
+ python setup_nsis.py
+ makensis /Darch=${{ matrix.arch }} deluge-win-installer.nsi
+
+ - uses: actions/upload-artifact@v3
+ with:
+ name: deluge-py${{ matrix.python }}-lt${{ matrix.libtorrent }}-${{ matrix.arch }}
+ path: packaging/win/*.exe
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a6ca45f94..1f0675c2c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,60 +6,64 @@ on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
-
+ inputs:
+ core-dump:
+ description: "Set to 1 to enable retrieving core dump from crashes"
+ default: "0"
jobs:
test-linux:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
+ strategy:
+ matrix:
+ python-version: ["3.7", "3.10"]
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
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
+ uses: actions/setup-python@v4
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 }}-
+ python-version: ${{ matrix.python-version }}
+ cache: "pip"
+ cache-dependency-path: "requirements*.txt"
- - 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 --upgrade pip wheel setuptools
+ pip install -r requirements-ci.txt
pip install -e .
- - name: Setup core dump directory
+ - 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 catch and store
+ if: github.event.inputs.core-dump == '1'
run: |
sudo mkdir /cores/ && sudo chmod 777 /cores/
echo "/cores/%E.%p" | sudo tee /proc/sys/kernel/core_pattern
+ ulimit -c unlimited
+ sudo apt install glibc-tools
+ echo "DEBUG_PREFIX=catchsegv python -X dev -m" >> $GITHUB_ENV
- 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 or security)" deluge
+ $DEBUG_PREFIX pytest -v -m "not (todo or gtkui)" deluge
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v3
# capture all crashes as build artifacts
if: failure()
with:
@@ -67,37 +71,31 @@ jobs:
path: /cores
test-windows:
- runs-on: windows-latest
+ runs-on: windows-2022
+ strategy:
+ matrix:
+ python-version: ["3.7", "3.10"]
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: "3.6"
-
- - name: Cache pip
- uses: actions/cache@v2
+ uses: actions/setup-python@v4
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 }}-
+ python-version: ${{ matrix.python-version }}
+ cache: "pip"
+ cache-dependency-path: "requirements*.txt"
- name: Install dependencies
run: |
- python -m pip install --upgrade pip wheel certifi
- python -m pip install deluge-libtorrent
- pip install -r requirements.txt -r requirements-tests.txt
+ pip install --upgrade pip wheel setuptools
+ pip install -r requirements-ci.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
+ pytest -v -m "not (todo or gtkui or security)" deluge
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index cafdfa53c..9afa06936 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -15,30 +15,23 @@ jobs:
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- - uses: actions/setup-python@v2
- with:
- python-version: "3.8"
- - name: Cache pip
- uses: actions/cache@v2
+
+ - uses: actions/setup-python@v4
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 }}-
+ python-version: "3.10"
+ cache: "pip"
+ cache-dependency-path: "requirements*.txt"
- name: Install dependencies
run: |
pip install --upgrade pip wheel
pip install tox
- sudo apt-get install enchant
+ sudo apt-get install enchant-2
- - name: Test with tox
+ - name: Build docs with tox
env:
TOX_ENV: docs
run: |
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 3a347fd98..6c55c6b77 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -11,7 +11,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-python@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-python@v4
- name: Run pre-commit linting
- uses: pre-commit/action@v2.0.2
+ uses: pre-commit/action@v3.0.0
diff --git a/.gitignore b/.gitignore
index 66826bbde..5a5989bdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,15 +10,17 @@ docs/source/modules/deluge*.rst
__pycache__/
*.py[cod]
*.tar.*
-_trial_temp
.tox/
deluge/i18n/*/
deluge.pot
deluge/ui/web/js/*.js
deluge/ui/web/js/extjs/ext-extensions*.js
*.desktop
-*.appdata.xml
+*.metainfo.xml
.build_data*
osx/app
RELEASE-VERSION
.venv*
+# used by setuptools to cache downloaded eggs
+/.eggs
+_pytest_temp/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 133d536dd..4d0d922ef 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,32 +3,35 @@ default_language_version:
exclude: >
(?x)^(
deluge/ui/web/docs/template/.*|
+ deluge/tests/data/.*svg|
)$
repos:
- - repo: https://github.com/ambv/black
- rev: 20.8b1
+ - repo: https://github.com/psf/black
+ rev: 23.1.0
hooks:
- id: black
name: Fmt Black
- repo: https://github.com/pre-commit/mirrors-prettier
- rev: v2.2.1
+ rev: v2.7.1
hooks:
- id: prettier
name: Fmt Prettier
# Workaround to list modified files only.
args: [--list-different]
- - repo: https://gitlab.com/pycqa/flake8
- # v3.7.9 due to E402 issue: https://gitlab.com/pycqa/flake8/-/issues/638
- rev: 3.7.9
+ - repo: https://github.com/pycqa/isort
+ rev: 5.12.0
+ hooks:
+ - id: isort
+ name: Fmt isort
+ - repo: https://github.com/pycqa/flake8
+ rev: 6.0.0
hooks:
- id: flake8
name: Chk Flake8
additional_dependencies:
- - flake8-isort==4.0.0
- - pep8-naming==0.11.1
- args: [--isort-show-traceback]
+ - pep8-naming==0.12.1
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v3.4.0
+ rev: v4.4.0
hooks:
- id: double-quote-string-fixer
name: Fix Double-quotes
@@ -40,3 +43,9 @@ repos:
args: [--fix=auto]
- id: trailing-whitespace
name: Fix Trailing whitespace
+ - repo: https://github.com/asottile/pyupgrade
+ rev: v3.3.1
+ hooks:
+ - id: pyupgrade
+ args: [--py36-plus]
+ stages: [manual]
diff --git a/.pylintrc b/.pylintrc
index becbe3f28..75fa0e6e4 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -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]
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 8d94a56fc..90b567f29 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -5,6 +5,14 @@
# Required
version: 2
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.10"
+ jobs:
+ post_checkout:
+ - git fetch --unshallow || true
+
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
@@ -14,9 +22,8 @@ formats: all
# Optionally set the version of Python and requirements required to build your docs
python:
- version: 3.7
install:
- requirements: requirements.txt
- requirements: docs/requirements.txt
- - method: setuptools
+ - method: pip
path: .
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 086b664c9..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,98 +0,0 @@
-os: linux
-dist: xenial
-
-language: python
-python:
- # Travis Xenial Python to support system_site_packages
- - 3.5_with_system_site_packages
-cache: pip
-
-env:
- global:
- - DISPLAY=:99.0
-
-git:
- # Set greater depth to get version from tags.
- depth: 1000
-
-jobs:
- 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_with_system_site_packages
- - if: commit_message =~ SECURITY_TEST
- env: TOX_ENV=security
- - name: Code linting
- env: TOX_ENV=lint
- python: 3.6
- - name: Docs build
- env: TOX_ENV=docs
- - name: GTK unit tests
- env: TOX_ENV=gtkui
- - name: Plugins unit tests
- env: TOX_ENV=plugins
- - name: Windows Unit tests
- os: windows
- language: shell
- before_install:
- # Python version must match available deluge-libtorrent
- - choco install python --version 3.6.8
- - python --version
- - python -m pip install --upgrade pip certifi
- - python -m pip install deluge-libtorrent
- env:
- - PATH=/c/Python36:/c/Python36/Scripts:$PATH
- - TOX_ENV=py3
-
-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
- # Spellchecking
- - enchant
-
-# Install dependencies
-install:
- - pip install tox
- # 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
- - "if [ $TOX_ENV != 'lint' ]; then
- python -c 'import libtorrent as lt; print(lt.__version__)';
- fi"
- # 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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4555df06..d878a488d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,170 @@
# Changelog
-## 2.0.4 (WIP)
+## 2.2.x (TBA)
+
+### Breaking changes
+
+- Python 3.6 support removed (Python >= 3.7)
+
+## 2.1.1 (2022-07-10)
+
+### Core
+
+- Fix missing trackers added via magnet
+- Fix handling magnets with tracker tiers
+
+## 2.1.0 (2022-06-28)
+
+### Breaking changes
+
+- Python 2 support removed (Python >= 3.6)
+- libtorrent minimum requirement increased (>= 1.2).
+
+### Core
+
+- Add support for SVG tracker icons.
+- Fix tracker icon error handling.
+- Fix cleaning-up tracker icon temp files.
+- Fix Plugin manager to handle new metadata 2.1.
+- Hide passwords in config logs.
+- Fix cleaning-up temp files in add_torrent_url.
+- Fix KeyError in sessionproxy after torrent delete.
+- Remove libtorrent deprecated functions.
+- Fix file_completed_alert handling.
+- Add plugin keys to get_torrents_status.
+- Add support for pygeoip dependency.
+- Fix crash logging to Windows protected folder.
+- Add is_interface and is_interface_name to validate network interfaces.
+- Fix is_url and is_infohash error with None value.
+- Fix load_libintl error.
+- Add support for IPv6 in host lists.
+- Add systemd user services.
+- Fix refresh and expire the torrent status cache.
+- Fix crash when logging errors initializing gettext.
+
+### Web UI
+
+- Fix ETA column sorting in correct order (#3413).
+- Fix defining foreground and background colors.
+- Accept charset in content-type for json messages.
+- Fix 'Complete Seen' and 'Completed' sorting.
+- Fix encoding HTML entities for torrent attributes to prevent XSS.
+
+### Gtk UI
+
+- Fix download location textbox width.
+- Fix obscured port number in Connection Manager.
+- Increase connection manager default height.
+- Fix bug with setting move completed in Options tab.
+- Fix adding daemon accounts.
+- Add workaround for crash on Windows with ico or gif icons.
+- Hide account password length in log.
+- Added a torrent menu option for magnet copy.
+- Fix unable to prefetch magnet in thinclient mode.
+- Use GtkSpinner when testing open port.
+- Update About Dialog year.
+- Fix Edit Torrents dialogs close issues.
+- Fix ETA being copied to neighboring empty cells.
+- Disable GTK CSD by default on Windows.
+
+### Console UI
+
+- Fix curses.init_pair raise ValueError on Py3.10.
+- Swap j and k key's behavior to fit vim mode.
+- Fix torrent details status error.
+- Fix incorrect test for when a host is online.
+- Add the torrent label to info command.
+
+### AutoAdd
+
+- Fix handling torrent decode errors.
+- Fix error dialog not being shown on error.
+
+### Blocklist
+
+- Add frequency unit to interval label.
+
+### Notifications
+
+- Fix UnicodeEncodeError upon non-ascii torrent name.
+
+## 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
diff --git a/DEPENDS.md b/DEPENDS.md
index 8f39eeb2d..41a7ec09f 100644
--- a/DEPENDS.md
+++ b/DEPENDS.md
@@ -7,13 +7,13 @@ All modules will require the [common](#common) section dependencies.
## Prerequisite
-- [Python] _>= 3.5_
+- [Python] _>= 3.6_
## Build
- [setuptools]
- [intltool] - Optional: Desktop file translation for \*nix.
-- [closure-compiler] - Minify javascript (alternative is [slimit])
+- [closure-compiler] - Minify javascript (alternative is [rjsmin])
## Common
@@ -23,26 +23,26 @@ 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.
+- [ifaddr] - Optional: Verify network interfaces.
-#### Linux and BSD
+### Linux and BSD
- [distro] - Optional: OS platform information.
-#### Windows OS
+### Windows OS
- [pywin32]
- [certifi]
## Core (deluged daemon)
-- [libtorrent] _>= 1.1.1_
-- [GeoIP] - Optional: IP address location lookup. (_Debian: `python-geoip`_)
+- [libtorrent] _>= 1.2.0_
+- [GeoIP] or [pygeoip] - Optional: IP address country lookup. (_Debian: `python-geoip`_)
## GTK UI
@@ -52,7 +52,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 +71,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 +81,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/
@@ -99,3 +97,4 @@ All modules will require the [common](#common) section dependencies.
[libnotify]: https://developer.gnome.org/libnotify/
[python-appindicator]: https://packages.ubuntu.com/xenial/python-appindicator
[librsvg]: https://wiki.gnome.org/action/show/Projects/LibRsvg
+[ifaddr]: https://pypi.org/project/ifaddr/
diff --git a/MANIFEST.in b/MANIFEST.in
index d2d970fe6..11a920d65 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -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
diff --git a/README.md b/README.md
index f0a2139c3..a153d21c5 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
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
@@ -58,7 +58,8 @@ 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)
+- [Discord](https://discord.gg/nwaHSE6tqn)
[user guide]: https://dev.deluge-torrent.org/wiki/UserGuide
[thinclient guide]: https://dev.deluge-torrent.org/wiki/UserGuide/ThinClient
diff --git a/deluge/_libtorrent.py b/deluge/_libtorrent.py
index 0020184e5..642855c52 100644
--- a/deluge/_libtorrent.py
+++ b/deluge/_libtorrent.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -15,8 +14,6 @@ Example:
>>> from deluge._libtorrent import lt
"""
-from __future__ import unicode_literals
-
from deluge.common import VersionSplit, get_version
from deluge.error import LibtorrentImportError
@@ -29,10 +26,10 @@ except ImportError:
raise LibtorrentImportError('No libtorrent library found: %s' % (ex))
-REQUIRED_VERSION = '1.1.2.0'
+REQUIRED_VERSION = '1.2.0.0'
LT_VERSION = lt.__version__
if VersionSplit(LT_VERSION) < VersionSplit(REQUIRED_VERSION):
raise LibtorrentImportError(
- 'Deluge %s requires libtorrent >= %s' % (get_version(), REQUIRED_VERSION)
+ f'Deluge {get_version()} requires libtorrent >= {REQUIRED_VERSION}'
)
diff --git a/deluge/argparserbase.py b/deluge/argparserbase.py
index 5eec1228f..5dc433086 100644
--- a/deluge/argparserbase.py
+++ b/deluge/argparserbase.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import argparse
import logging
import os
@@ -95,7 +92,7 @@ def _get_version_detail():
except ImportError:
pass
version_str += 'Python: %s\n' % platform.python_version()
- version_str += 'OS: %s %s\n' % (platform.system(), common.get_os_version())
+ version_str += f'OS: {platform.system()} {common.get_os_version()}\n'
return version_str
@@ -109,8 +106,8 @@ class DelugeTextHelpFormatter(argparse.RawDescriptionHelpFormatter):
line instead. This way list formatting is not mangled by textwrap.wrap.
"""
wrapped_lines = []
- for l in text.splitlines():
- wrapped_lines.extend(textwrap.wrap(l, width, subsequent_indent=' '))
+ for line in text.splitlines():
+ wrapped_lines.extend(textwrap.wrap(line, width, subsequent_indent=' '))
return wrapped_lines
def _format_action_invocation(self, action):
@@ -137,7 +134,7 @@ class DelugeTextHelpFormatter(argparse.RawDescriptionHelpFormatter):
default = action.dest.upper()
args_string = self._format_args(action, default)
opt = ', '.join(action.option_strings)
- parts.append('%s %s' % (opt, args_string))
+ parts.append(f'{opt} {args_string}')
return ', '.join(parts)
@@ -165,7 +162,7 @@ class ArgParserBase(argparse.ArgumentParser):
self.log_stream = kwargs['log_stream']
del kwargs['log_stream']
- super(ArgParserBase, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.common_setup = False
self.process_arg_group = False
@@ -202,7 +199,7 @@ class ArgParserBase(argparse.ArgumentParser):
self.group.add_argument(
'-L',
'--loglevel',
- choices=[l for k in deluge.log.levels for l in (k, k.upper())],
+ choices=[level for k in deluge.log.levels for level in (k, k.upper())],
help=_('Set the log level (none, error, warning, info, debug)'),
metavar='<level>',
)
@@ -246,7 +243,7 @@ class ArgParserBase(argparse.ArgumentParser):
argparse.Namespace: The parsed arguments.
"""
- options = super(ArgParserBase, self).parse_args(args=args)
+ options = super().parse_args(args=args)
return self._handle_ui_options(options)
def parse_known_ui_args(self, args, withhold=None):
@@ -262,9 +259,9 @@ class ArgParserBase(argparse.ArgumentParser):
"""
if withhold:
args = [a for a in args if a not in withhold]
- options, remaining = super(ArgParserBase, self).parse_known_args(args=args)
+ options, remaining = super().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):
diff --git a/deluge/bencode.py b/deluge/bencode.py
index 0c2674b9b..df8cc85c2 100644
--- a/deluge/bencode.py
+++ b/deluge/bencode.py
@@ -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):
@@ -90,8 +84,7 @@ def bdecode(x):
return r
-class Bencached(object):
-
+class Bencached:
__slots__ = ['bencoded']
def __init__(self, s):
@@ -146,10 +139,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):
diff --git a/deluge/common.py b/deluge/common.py
index e90d166c0..be655447f 100644
--- a/deluge/common.py
+++ b/deluge/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007,2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,43 +7,41 @@
#
"""Common functions for various parts of Deluge to use."""
-from __future__ import division, print_function, unicode_literals
-
import base64
import binascii
import functools
import glob
-import locale
import logging
import numbers
import os
import platform
import re
+import socket
import subprocess
import sys
import tarfile
import time
from contextlib import closing
from datetime import datetime
-from io import BytesIO, open
-
-import pkg_resources
+from importlib import resources
+from io import BytesIO
+from pathlib import Path
+from urllib.parse import unquote_plus, urljoin
+from urllib.request import pathname2url
from deluge.decorators import deprecated
from deluge.error import InvalidPathError
try:
- import chardet
+ from importlib.metadata import distribution
except ImportError:
- chardet = None
+ from pkg_resources import get_distribution as distribution
+
try:
- from urllib.parse import unquote_plus, urljoin
- from urllib.request import pathname2url
+ import chardet
except ImportError:
- # PY2 fallback
- from urllib import pathname2url, unquote_plus # pylint: disable=ungrouped-imports
- from urlparse import urljoin # pylint: disable=ungrouped-imports
+ chardet = None
# Windows workaround for HTTPS requests requiring certificate authority bundle.
# see: https://twistedmatrix.com/trac/ticket/9209
@@ -53,6 +50,11 @@ if platform.system() in ('Windows', 'Microsoft'):
os.environ['SSL_CERT_FILE'] = where()
+try:
+ import ifaddr
+except ImportError:
+ ifaddr = None
+
if platform.system() not in ('Windows', 'Microsoft', 'Darwin'):
# gi makes dbus available on Window but don't import it as unused.
@@ -84,7 +86,8 @@ JSON_FORMAT = {'indent': 4, 'sort_keys': True, 'ensure_ascii': False}
DBUS_FM_ID = 'org.freedesktop.FileManager1'
DBUS_FM_PATH = '/org/freedesktop/FileManager1'
-PY2 = sys.version_info.major == 2
+# Retained for plugin backward compatibility
+PY2 = False
def get_version():
@@ -93,7 +96,7 @@ def get_version():
Returns:
str: The version of Deluge.
"""
- return pkg_resources.get_distribution('Deluge').version
+ return distribution('Deluge').version
def get_default_config_dir(filename=None):
@@ -111,10 +114,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',
@@ -147,14 +148,14 @@ def get_default_download_dir():
try:
user_dirs_path = os.path.join(xdg_config_home, 'user-dirs.dirs')
- with open(user_dirs_path, 'r', encoding='utf8') as _file:
+ with open(user_dirs_path, encoding='utf8') as _file:
for line in _file:
if not line.startswith('#') and line.startswith('XDG_DOWNLOAD_DIR'):
download_dir = os.path.expandvars(
line.partition('=')[2].rstrip().strip('"')
)
break
- except IOError:
+ except OSError:
pass
if not download_dir:
@@ -178,8 +179,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(':', '-')
@@ -275,7 +276,7 @@ def get_os_version():
os_version = list(platform.mac_ver())
os_version[1] = '' # versioninfo always empty.
elif distro:
- os_version = distro.linux_distribution()
+ os_version = (distro.name(), distro.version(), distro.codename())
else:
os_version = (platform.release(),)
@@ -295,20 +296,22 @@ def get_pixmap(fname):
return resource_filename('deluge', os.path.join('ui', 'data', 'pixmaps', fname))
-def resource_filename(module, path):
- """Get filesystem path for a resource.
-
- This function contains a work-around for pkg_resources.resource_filename
- not returning the correct path with multiple packages installed.
+def resource_filename(module: str, path: str) -> str:
+ """Get filesystem path for a non-python resource.
- So if there's a second deluge package, installed globally and another in
- develop mode somewhere else, while pkg_resources.get_distribution('Deluge')
- returns the proper deluge instance, pkg_resources.resource_filename
- does not, it returns the first found on the python path, which is wrong.
+ Abstracts getting module resource files. Originally created to
+ workaround pkg_resources.resource_filename limitations with
+ multiple Deluge packages installed.
"""
- return pkg_resources.get_distribution('Deluge').get_resource_filename(
- pkg_resources._manager, os.path.join(*(module.split('.') + [path]))
- )
+ path = Path(path)
+
+ try:
+ with resources.as_file(resources.files(module) / path) as resource_file:
+ return str(resource_file)
+ except AttributeError:
+ # Python <= 3.8
+ with resources.path(module, path.parts[0]) as resource_file:
+ return str(resource_file.joinpath(*path.parts[1:]))
def open_file(path, timestamp=None):
@@ -420,43 +423,49 @@ def translate_size_units():
def fsize(fsize_b, precision=1, shortform=False):
- """Formats the bytes value into a string with KiB, MiB or GiB units.
+ """Formats the bytes value into a string with KiB, MiB, GiB or TiB units.
Args:
fsize_b (int): The filesize in bytes.
- precision (int): The filesize float precision.
+ precision (int): The output float precision, 1 by default.
+ shortform (bool): The output short|long form, False (long form) by default.
Returns:
- str: A formatted string in KiB, MiB or GiB units.
+ str: A formatted string in KiB, MiB, GiB or TiB units.
Examples:
>>> fsize(112245)
'109.6 KiB'
>>> fsize(112245, precision=0)
'110 KiB'
+ >>> fsize(112245, shortform=True)
+ '109.6 K'
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.
+ Notice that short forms K|M|G|T are synonymous here with
+ KiB|MiB|GiB|TiB. They are powers of 1024, not 1000.
+
"""
- if fsize_b >= 1024 ** 4:
+ if fsize_b >= 1024**4:
return '%.*f %s' % (
precision,
- fsize_b / 1024 ** 4,
+ fsize_b / 1024**4,
tib_txt_short if shortform else tib_txt,
)
- elif fsize_b >= 1024 ** 3:
+ elif fsize_b >= 1024**3:
return '%.*f %s' % (
precision,
- fsize_b / 1024 ** 3,
+ fsize_b / 1024**3,
gib_txt_short if shortform else gib_txt,
)
- elif fsize_b >= 1024 ** 2:
+ elif fsize_b >= 1024**2:
return '%.*f %s' % (
precision,
- fsize_b / 1024 ** 2,
+ fsize_b / 1024**2,
mib_txt_short if shortform else mib_txt,
)
elif fsize_b >= 1024:
@@ -474,7 +483,7 @@ def fpcnt(dec, precision=2):
Args:
dec (float): The ratio in the range [0.0, 1.0].
- precision (int): The percentage float precision.
+ precision (int): The output float precision, 2 by default.
Returns:
str: A formatted string representing a percentage.
@@ -498,6 +507,8 @@ def fspeed(bps, precision=1, shortform=False):
Args:
bps (int): The speed in bytes per second.
+ precision (int): The output float precision, 1 by default.
+ shortform (bool): The output short|long form, False (long form) by default.
Returns:
str: A formatted string representing transfer speed.
@@ -506,30 +517,34 @@ def fspeed(bps, precision=1, shortform=False):
>>> fspeed(43134)
'42.1 KiB/s'
+ Note:
+ Notice that short forms K|M|G|T are synonymous here with
+ KiB|MiB|GiB|TiB. They are powers of 1024, not 1000.
+
"""
- if bps < 1024 ** 2:
+ if bps < 1024**2:
return '%.*f %s' % (
precision,
bps / 1024,
_('K/s') if shortform else _('KiB/s'),
)
- elif bps < 1024 ** 3:
+ elif bps < 1024**3:
return '%.*f %s' % (
precision,
- bps / 1024 ** 2,
+ bps / 1024**2,
_('M/s') if shortform else _('MiB/s'),
)
- elif bps < 1024 ** 4:
+ elif bps < 1024**4:
return '%.*f %s' % (
precision,
- bps / 1024 ** 3,
+ bps / 1024**3,
_('G/s') if shortform else _('GiB/s'),
)
else:
return '%.*f %s' % (
precision,
- bps / 1024 ** 4,
+ bps / 1024**4,
_('T/s') if shortform else _('TiB/s'),
)
@@ -542,7 +557,7 @@ def fpeer(num_peers, total_peers):
total_peers (int): The total number of peers.
Returns:
- str: A formatted string 'num_peers (total_peers)' or total_peers < 0, just 'num_peers'.
+ str: A formatted string 'num_peers (total_peers)' or if total_peers < 0, just 'num_peers'.
Examples:
>>> fpeer(10, 20)
@@ -552,9 +567,9 @@ def fpeer(num_peers, total_peers):
"""
if total_peers > -1:
- return '{:d} ({:d})'.format(num_peers, total_peers)
+ return f'{num_peers:d} ({total_peers:d})'
else:
- return '{:d}'.format(num_peers)
+ return f'{num_peers:d}'
def ftime(secs):
@@ -571,7 +586,7 @@ def ftime(secs):
'6h 23m'
Note:
- This function has been refactored for perfomance.
+ This function has been refactored for performance.
"""
@@ -580,27 +595,27 @@ def ftime(secs):
if secs <= 0:
time_str = ''
elif secs < 60:
- time_str = '{}s'.format(secs)
+ time_str = f'{secs}s'
elif secs < 3600:
- time_str = '{}m {}s'.format(secs // 60, secs % 60)
+ time_str = f'{secs // 60}m {secs % 60}s'
elif secs < 86400:
- time_str = '{}h {}m'.format(secs // 3600, secs // 60 % 60)
+ time_str = f'{secs // 3600}h {secs // 60 % 60}m'
elif secs < 604800:
- time_str = '{}d {}h'.format(secs // 86400, secs // 3600 % 24)
+ time_str = f'{secs // 86400}d {secs // 3600 % 24}h'
elif secs < 31449600:
- time_str = '{}w {}d'.format(secs // 604800, secs // 86400 % 7)
+ time_str = f'{secs // 604800}w {secs // 86400 % 7}d'
else:
- time_str = '{}y {}w'.format(secs // 31449600, secs // 604800 % 52)
-
+ time_str = f'{secs // 31449600}y {secs // 604800 % 52}w'
return time_str
def fdate(seconds, date_only=False, precision_secs=False):
- """Formats a date time string in the locale's date representation based on the systems timezone.
+ """Formats a date time string in the locale's date representation based on the system's timezone.
Args:
seconds (float): Time in seconds since the Epoch.
- precision_secs (bool): Include seconds in time format.
+ date_only (bool): Whether to include only the date, False by default.
+ precision_secs (bool): Include seconds in time format, False by default.
Returns:
str: A string in the locale's datetime representation or "" if seconds < 0
@@ -625,10 +640,14 @@ def tokenize(text):
Returns:
list: A list of strings and/or numbers.
- This function is used to implement robust tokenization of user input
- It automatically coerces integer and floating point numbers, ignores
- whitespace and knows how to separate numbers from strings even without
- whitespace.
+ Note:
+ This function is used to implement robust tokenization of user input
+ It automatically coerces integer and floating point numbers, ignores
+ whitespace and knows how to separate numbers from strings even without
+ whitespace.
+
+ Possible optimization: move the 2 regexes outside of function.
+
"""
tokenized_input = []
for token in re.split(r'(\d+(?:\.\d+)?)', text):
@@ -644,17 +663,21 @@ def tokenize(text):
size_units = [
{'prefix': 'b', 'divider': 1, 'singular': 'byte', 'plural': 'bytes'},
- {'prefix': 'KiB', 'divider': 1024 ** 1},
- {'prefix': 'MiB', 'divider': 1024 ** 2},
- {'prefix': 'GiB', 'divider': 1024 ** 3},
- {'prefix': 'TiB', 'divider': 1024 ** 4},
- {'prefix': 'PiB', 'divider': 1024 ** 5},
- {'prefix': 'KB', 'divider': 1000 ** 1},
- {'prefix': 'MB', 'divider': 1000 ** 2},
- {'prefix': 'GB', 'divider': 1000 ** 3},
- {'prefix': 'TB', 'divider': 1000 ** 4},
- {'prefix': 'PB', 'divider': 1000 ** 5},
- {'prefix': 'm', 'divider': 1000 ** 2},
+ {'prefix': 'KiB', 'divider': 1024**1},
+ {'prefix': 'MiB', 'divider': 1024**2},
+ {'prefix': 'GiB', 'divider': 1024**3},
+ {'prefix': 'TiB', 'divider': 1024**4},
+ {'prefix': 'PiB', 'divider': 1024**5},
+ {'prefix': 'k', 'divider': 1000**1},
+ {'prefix': 'm', 'divider': 1000**2},
+ {'prefix': 'g', 'divider': 1000**3},
+ {'prefix': 't', 'divider': 1000**4},
+ {'prefix': 'p', 'divider': 1000**5},
+ {'prefix': 'KB', 'divider': 1000**1},
+ {'prefix': 'MB', 'divider': 1000**2},
+ {'prefix': 'GB', 'divider': 1000**3},
+ {'prefix': 'TB', 'divider': 1000**4},
+ {'prefix': 'PB', 'divider': 1000**5},
]
@@ -712,6 +735,9 @@ def is_url(url):
True
"""
+ if not url:
+ return False
+
return url.partition('://')[0] in ('http', 'https', 'ftp', 'udp')
@@ -726,6 +752,9 @@ def is_infohash(infohash):
bool: True if valid infohash, False otherwise.
"""
+ if not infohash:
+ return False
+
return len(infohash) == 40 and infohash.isalnum()
@@ -733,6 +762,8 @@ MAGNET_SCHEME = 'magnet:?'
XT_BTIH_PARAM = 'xt=urn:btih:'
DN_PARAM = 'dn='
TR_PARAM = 'tr='
+TR_TIER_PARAM = 'tr.'
+TR_TIER_REGEX = re.compile(r'^tr.(\d+)=(\S+)')
def is_magnet(uri):
@@ -775,8 +806,6 @@ def get_magnet_info(uri):
"""
- tr0_param = 'tr.'
- tr0_param_regex = re.compile(r'^tr.(\d+)=(\S+)')
if not uri.startswith(MAGNET_SCHEME):
return {}
@@ -804,12 +833,14 @@ def get_magnet_info(uri):
tracker = unquote_plus(param[len(TR_PARAM) :])
trackers[tracker] = tier
tier += 1
- elif param.startswith(tr0_param):
- try:
- tier, tracker = re.match(tr0_param_regex, param).groups()
- trackers[tracker] = tier
- except AttributeError:
- pass
+ elif param.startswith(TR_TIER_PARAM):
+ tracker_match = re.match(TR_TIER_REGEX, param)
+ if not tracker_match:
+ continue
+
+ tier, tracker = tracker_match.groups()
+ tracker = unquote_plus(tracker)
+ trackers[tracker] = int(tier)
if info_hash:
if not name:
@@ -830,7 +861,7 @@ def create_magnet_uri(infohash, name=None, trackers=None):
Args:
infohash (str): The info-hash of the torrent.
name (str, optional): The name of the torrent.
- trackers (list or dict, optional): A list of trackers or dict or {tracker: tier} pairs.
+ trackers (list or dict, optional): A list of trackers or a dict or some {tracker: tier} pairs.
Returns:
str: A magnet URI string.
@@ -872,7 +903,7 @@ def get_path_size(path):
return os.path.getsize(path)
dir_size = 0
- for (p, dummy_dirs, files) in os.walk(path):
+ for p, dummy_dirs, files in os.walk(path):
for _file in files:
filename = os.path.join(p, _file)
dir_size += os.path.getsize(filename)
@@ -904,6 +935,29 @@ def free_space(path):
return disk_data.f_bavail * block_size
+def is_interface(interface):
+ """Check if interface is a valid IP or network adapter.
+
+ Args:
+ interface (str): The IP or interface name to test.
+
+ Returns:
+ bool: Whether interface is valid is not.
+
+ Examples:
+ Windows:
+ >>> is_interface('{7A30AE62-23ZA-3744-Z844-A5B042524871}')
+ >>> is_interface('127.0.0.1')
+ True
+ Linux:
+ >>> is_interface('lo')
+ >>> is_interface('127.0.0.1')
+ True
+
+ """
+ return is_ip(interface) or is_interface_name(interface)
+
+
def is_ip(ip):
"""A test to see if 'ip' is a valid IPv4 or IPv6 address.
@@ -939,15 +993,12 @@ def is_ipv4(ip):
"""
- import socket
-
try:
- if windows_check():
- return socket.inet_aton(ip)
- else:
- return socket.inet_pton(socket.AF_INET, ip)
- except socket.error:
+ socket.inet_pton(socket.AF_INET, ip)
+ except OSError:
return False
+ else:
+ return True
def is_ipv6(ip):
@@ -966,23 +1017,51 @@ def is_ipv6(ip):
"""
try:
- import ipaddress
- except ImportError:
- import socket
-
- try:
- return socket.inet_pton(socket.AF_INET6, ip)
- except (socket.error, AttributeError):
- if windows_check():
- log.warning('Unable to verify IPv6 Address on Windows.')
- return True
+ socket.inet_pton(socket.AF_INET6, ip)
+ except OSError:
+ return False
else:
+ return True
+
+
+def is_interface_name(name):
+ """Returns True if an interface name exists.
+
+ Args:
+ name (str): The Interface to test. eg. eth0 linux. GUID on Windows.
+
+ Returns:
+ bool: Whether name is valid or not.
+
+ Examples:
+ >>> is_interface_name("eth0")
+ True
+ >>> is_interface_name("{7A30AE62-23ZA-3744-Z844-A5B042524871}")
+ True
+
+ """
+
+ if not windows_check():
try:
- return ipaddress.IPv6Address(decode_bytes(ip))
- except ipaddress.AddressValueError:
+ socket.if_nametoindex(name)
+ except OSError:
pass
+ else:
+ return True
- return False
+ if ifaddr:
+ try:
+ adapters = ifaddr.get_adapters()
+ except OSError:
+ return True
+ else:
+ return any([name == a.name for a in adapters])
+
+ if windows_check():
+ regex = '^{[0-9A-Z]{8}-([0-9A-Z]{4}-){3}[0-9A-Z]{12}}$'
+ return bool(re.search(regex, str(name)))
+
+ return True
def decode_bytes(byte_str, encoding='utf8'):
@@ -1060,7 +1139,7 @@ def utf8_encode_structure(data):
@functools.total_ordering
-class VersionSplit(object):
+class VersionSplit:
"""
Used for comparing version numbers.
@@ -1239,11 +1318,7 @@ 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 cdll, windll
@@ -1262,56 +1337,13 @@ def set_env_variable(name, value):
)
# Update the copy maintained by msvcrt (used by gtk+ runtime)
- result = cdll.msvcrt._wputenv('%s=%s' % (name, value))
+ result = cdll.msvcrt._wputenv(f'{name}={value}')
if result != 0:
log.info("Failed to set Env Var '%s' (msvcrt._putenv)", name)
else:
log.debug("Set Env Var '%s' to '%s' (msvcrt._putenv)", 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, c_int, cdll, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- get_cmd_linew = cdll.kernel32.GetCommandLineW
- get_cmd_linew.argtypes = []
- get_cmd_linew.restype = LPCWSTR
-
- 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
-
-
def run_profiled(func, *args, **kwargs):
"""
Profile a function with cProfile
diff --git a/deluge/component.py b/deluge/component.py
index 28e663344..421f49a7b 100644
--- a/deluge/component.py
+++ b/deluge/component.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2010 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,13 +6,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
@@ -27,13 +23,13 @@ class ComponentAlreadyRegistered(Exception):
class ComponentException(Exception):
def __init__(self, message, tb):
- super(ComponentException, self).__init__(message)
+ super().__init__(message)
self.message = message
self.tb = tb
def __str__(self):
- s = super(ComponentException, self).__str__()
- return '%s\n%s' % (s, ''.join(self.tb))
+ s = super().__str__()
+ return '{}\n{}'.format(s, ''.join(self.tb))
def __eq__(self, other):
if isinstance(other, self.__class__):
@@ -45,7 +41,7 @@ class ComponentException(Exception):
return not self.__eq__(other)
-class Component(object):
+class Component:
"""Component objects are singletons managed by the :class:`ComponentRegistry`.
When a new Component object is instantiated, it will be automatically
@@ -63,11 +59,16 @@ class Component(object):
Deluge core.
**update()** - This method is called every 1 second by default while the
- Componented is in a *Started* state. The interval can be
+ Component is in a *Started* state. The interval can be
specified during instantiation. The update() timer can be
paused by instructing the :class:`ComponentRegistry` to pause
this Component.
+ **pause()** - This method is called when the component is being paused.
+
+ **resume()** - This method is called when the component resumes from a Paused
+ state.
+
**shutdown()** - This method is called when the client is exiting. If the
Component is in a "Started" state when this is called, a
call to stop() will be issued prior to shutdown().
@@ -84,10 +85,10 @@ class Component(object):
**Stopped** - The Component has either been stopped or has yet to be started.
- **Stopping** - The Component has had it's stop method called, but it hasn't
+ **Stopping** - The Component has had its stop method called, but it hasn't
fully stopped yet.
- **Paused** - The Component has had it's update timer stopped, but will
+ **Paused** - The Component has had its update timer stopped, but will
still be considered in a Started state.
"""
@@ -115,9 +116,8 @@ class Component(object):
_ComponentRegistry.deregister(self)
def _component_start_timer(self):
- if hasattr(self, 'update'):
- self._component_timer = LoopingCall(self.update)
- self._component_timer.start(self._component_interval)
+ self._component_timer = LoopingCall(self.update)
+ self._component_timer.start(self._component_interval)
def _component_start(self):
def on_start(result):
@@ -133,13 +133,10 @@ class Component(object):
return fail(result)
if self._component_state == 'Stopped':
- if hasattr(self, 'start'):
- self._component_state = 'Starting'
- d = deferLater(reactor, 0, self.start)
- d.addCallbacks(on_start, on_start_fail)
- self._component_starting_deferred = d
- else:
- d = maybeDeferred(on_start, None)
+ self._component_state = 'Starting'
+ d = deferLater(reactor, 0, self.start)
+ d.addCallbacks(on_start, on_start_fail)
+ self._component_starting_deferred = d
elif self._component_state == 'Starting':
return self._component_starting_deferred
elif self._component_state == 'Started':
@@ -169,14 +166,11 @@ class Component(object):
return result
if self._component_state != 'Stopped' and self._component_state != 'Stopping':
- if hasattr(self, 'stop'):
- self._component_state = 'Stopping'
- d = maybeDeferred(self.stop)
- d.addCallback(on_stop)
- d.addErrback(on_stop_fail)
- self._component_stopping_deferred = d
- else:
- d = maybeDeferred(on_stop, None)
+ self._component_state = 'Stopping'
+ d = maybeDeferred(self.stop)
+ d.addCallback(on_stop)
+ d.addErrback(on_stop_fail)
+ self._component_stopping_deferred = d
if self._component_state == 'Stopping':
return self._component_stopping_deferred
@@ -186,13 +180,12 @@ class Component(object):
def _component_pause(self):
def on_pause(result):
self._component_state = 'Paused'
+ if self._component_timer and self._component_timer.running:
+ self._component_timer.stop()
if self._component_state == 'Started':
- if self._component_timer and self._component_timer.running:
- d = maybeDeferred(self._component_timer.stop)
- d.addCallback(on_pause)
- else:
- d = succeed(None)
+ d = maybeDeferred(self.pause)
+ d.addCallback(on_pause)
elif self._component_state == 'Paused':
d = succeed(None)
else:
@@ -209,9 +202,10 @@ class Component(object):
def _component_resume(self):
def on_resume(result):
self._component_state = 'Started'
+ self._component_start_timer()
if self._component_state == 'Paused':
- d = maybeDeferred(self._component_start_timer)
+ d = maybeDeferred(self.resume)
d.addCallback(on_resume)
else:
d = fail(
@@ -226,9 +220,7 @@ class Component(object):
def _component_shutdown(self):
def on_stop(result):
- if hasattr(self, 'shutdown'):
- return maybeDeferred(self.shutdown)
- return succeed(None)
+ return maybeDeferred(self.shutdown)
d = self._component_stop()
d.addCallback(on_stop)
@@ -249,8 +241,14 @@ class Component(object):
def shutdown(self):
pass
+ def pause(self):
+ pass
+
+ def resume(self):
+ pass
+
-class ComponentRegistry(object):
+class ComponentRegistry:
"""The ComponentRegistry holds a list of currently registered :class:`Component` objects.
It is used to manage the Components by starting, stopping, pausing and shutting them down.
@@ -293,7 +291,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 +323,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 +357,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 +397,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 +423,7 @@ class ComponentRegistry(object):
"""
if not names:
names = list(self.components)
- elif isinstance(names, string_types):
+ elif isinstance(names, str):
names = [names]
deferreds = []
diff --git a/deluge/config.py b/deluge/config.py
index 80b30faf5..c5cb3122b 100644
--- a/deluge/config.py
+++ b/deluge/config.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -39,39 +38,17 @@ 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__)
-callLater = None # noqa: N816 Necessary for the config tests
-
-
-def prop(func):
- """Function decorator for defining property attributes
-
- The decorated function is expected to return a dictionary
- containing one or more of the following pairs:
-
- fget - function for getting attribute value
- fset - function for setting attribute value
- fdel - function for deleting attribute
-
- This can be conveniently constructed by the locals() builtin
- function; see:
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
- """
- return property(doc=func.__doc__, **func())
def find_json_objects(text, decoder=json.JSONDecoder()):
@@ -105,7 +82,22 @@ def find_json_objects(text, decoder=json.JSONDecoder()):
return objects
-class Config(object):
+def cast_to_existing_type(value, old_value):
+ """Attempt to convert new value type to match old value type"""
+ types_match = isinstance(old_value, (type(None), type(value)))
+ if value is not None and not types_match:
+ old_type = type(old_value)
+ # Skip convert to bytes since requires knowledge of encoding and value should
+ # be unicode anyway.
+ if old_type is bytes:
+ return value
+
+ return old_type(value)
+
+ return value
+
+
+class Config:
"""This class is used to access/create/modify config files.
Args:
@@ -115,13 +107,23 @@ class Config(object):
file_version (int): The file format for the default config values when creating
a fresh config. This value should be increased whenever a new migration function is
setup to convert old config files. (default: 1)
+ log_mask_funcs (dict): A dict of key:function, used to mask sensitive
+ key values (e.g. passwords) when logging is enabled.
"""
- def __init__(self, filename, defaults=None, config_dir=None, file_version=1):
+ def __init__(
+ self,
+ filename,
+ defaults=None,
+ config_dir=None,
+ file_version=1,
+ log_mask_funcs=None,
+ ):
self.__config = {}
self.__set_functions = {}
self.__change_callbacks = []
+ self.__log_mask_funcs = log_mask_funcs if log_mask_funcs else {}
# These hold the version numbers and they will be set when loaded
self.__version = {'format': 1, 'file': file_version}
@@ -132,7 +134,7 @@ class Config(object):
if defaults:
for key, value in defaults.items():
- self.set_item(key, value)
+ self.set_item(key, value, default=True)
# Load the config from file in the config_dir
if config_dir:
@@ -142,6 +144,12 @@ class Config(object):
self.load()
+ def callLater(self, period, func, *args, **kwargs): # noqa: N802 ignore camelCase
+ """Wrapper around reactor.callLater for test purpose."""
+ from twisted.internet import reactor
+
+ return reactor.callLater(period, func, *args, **kwargs)
+
def __contains__(self, item):
return item in self.__config
@@ -150,7 +158,7 @@ class Config(object):
return self.set_item(key, value)
- def set_item(self, key, value):
+ def set_item(self, key, value, default=False):
"""Sets item 'key' to 'value' in the config dictionary.
Does not allow changing the item's type unless it is None.
@@ -162,6 +170,8 @@ class Config(object):
key (str): Item to change to change.
value (any): The value to change item to, must be same type as what is
currently in the config.
+ default (optional, bool): When setting a default value skip func or save
+ callbacks.
Raises:
ValueError: Raised when the type of value is not the same as what is
@@ -174,61 +184,54 @@ class Config(object):
5
"""
- if key not in self.__config:
- self.__config[key] = value
- log.debug('Setting key "%s" to: %s (of type: %s)', key, value, type(value))
- return
-
- if self.__config[key] == value:
- return
+ if isinstance(value, bytes):
+ value = value.decode()
- # Change the value type if it is not None and does not match.
- type_match = isinstance(self.__config[key], (type(None), type(value)))
- if value is not None and not type_match:
+ if key in self.__config:
try:
- oldtype = type(self.__config[key])
- # Don't convert to bytes as requires encoding and value will
- # be decoded anyway.
- if oldtype is not bytes:
- value = oldtype(value)
+ value = cast_to_existing_type(value, self.__config[key])
except ValueError:
log.warning('Value Type "%s" invalid for key: %s', type(value), key)
raise
+ else:
+ if self.__config[key] == value:
+ return
- if isinstance(value, bytes):
- value = value.decode('utf8')
-
- log.debug('Setting key "%s" to: %s (of type: %s)', key, value, type(value))
+ if log.isEnabledFor(logging.DEBUG):
+ if key in self.__log_mask_funcs:
+ value = self.__log_mask_funcs[key](value)
+ log.debug(
+ 'Setting key "%s" to: %s (of type: %s)',
+ key,
+ value,
+ type(value),
+ )
self.__config[key] = value
- global callLater
- if callLater is None:
- # Must import here and not at the top or it will throw ReactorAlreadyInstalledError
- from twisted.internet.reactor import ( # pylint: disable=redefined-outer-name
- callLater,
- )
+ # Skip save or func callbacks if setting default value for keys
+ if default:
+ return
+
# Run the set_function for this key if any
- try:
- for func in self.__set_functions[key]:
- callLater(0, func, key, value)
- except KeyError:
- pass
+ for func in self.__set_functions.get(key, []):
+ self.callLater(0, func, key, value)
+
try:
def do_change_callbacks(key, value):
for func in self.__change_callbacks:
func(key, value)
- callLater(0, do_change_callbacks, key, value)
+ self.callLater(0, do_change_callbacks, key, value)
except Exception:
pass
# We set the save_timer for 5 seconds if not already set
if not self._save_timer or not self._save_timer.active():
- self._save_timer = callLater(5, self.save)
+ self._save_timer = self.callLater(5, self.save)
def __getitem__(self, key):
- """See get_item """
+ """See get_item"""
return self.get_item(key)
def get_item(self, key):
@@ -301,16 +304,9 @@ class Config(object):
del self.__config[key]
- global callLater
- if callLater is None:
- # Must import here and not at the top or it will throw ReactorAlreadyInstalledError
- from twisted.internet.reactor import ( # pylint: disable=redefined-outer-name
- callLater,
- )
-
# We set the save_timer for 5 seconds if not already set
if not self._save_timer or not self._save_timer.active():
- self._save_timer = callLater(5, self.save)
+ self._save_timer = self.callLater(5, self.save)
def register_change_callback(self, callback):
"""Registers a callback function for any changed value.
@@ -356,7 +352,6 @@ class Config(object):
# Run the function now if apply_now is set
if apply_now:
function(key, self.__config[key])
- return
def apply_all(self):
"""Calls all set functions.
@@ -399,9 +394,9 @@ class Config(object):
filename = self.__config_file
try:
- with open(filename, 'r', encoding='utf8') as _file:
+ with open(filename, encoding='utf8') as _file:
data = _file.read()
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to open config file %s: %s', filename, ex)
return
@@ -431,12 +426,24 @@ class Config(object):
log.exception(ex)
log.warning('Unable to load config file: %s', filename)
+ if not log.isEnabledFor(logging.DEBUG):
+ return
+
+ config = self.__config
+ if self.__log_mask_funcs:
+ config = {
+ key: self.__log_mask_funcs[key](config[key])
+ if key in self.__log_mask_funcs
+ else config[key]
+ for key in config
+ }
+
log.debug(
'Config %s version: %s.%s loaded: %s',
filename,
self.__version['format'],
self.__version['file'],
- self.__config,
+ config,
)
def save(self, filename=None):
@@ -454,7 +461,7 @@ class Config(object):
# Check to see if the current config differs from the one on disk
# We will only write a new config file if there is a difference
try:
- with open(filename, 'r', encoding='utf8') as _file:
+ with open(filename, encoding='utf8') as _file:
data = _file.read()
objects = find_json_objects(data)
start, end = objects[0]
@@ -466,7 +473,7 @@ class Config(object):
if self._save_timer and self._save_timer.active():
self._save_timer.cancel()
return True
- except (IOError, IndexError) as ex:
+ except (OSError, IndexError) as ex:
log.warning('Unable to open config file: %s because: %s', filename, ex)
# Save the new config and make sure it's written to disk
@@ -480,7 +487,7 @@ class Config(object):
json.dump(self.__config, getwriter('utf8')(_file), **JSON_FORMAT)
_file.flush()
os.fsync(_file.fileno())
- except IOError as ex:
+ except OSError as ex:
log.error('Error writing new config file: %s', ex)
return False
@@ -491,7 +498,7 @@ class Config(object):
try:
log.debug('Backing up old config file to %s.bak', filename)
shutil.move(filename, filename + '.bak')
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to backup old config: %s', ex)
# The new config file has been written successfully, so let's move it over
@@ -499,7 +506,7 @@ class Config(object):
try:
log.debug('Moving new config file %s to %s', filename_tmp, filename)
shutil.move(filename_tmp, filename)
- except IOError as ex:
+ except OSError as ex:
log.error('Error moving new config file: %s', ex)
return False
else:
@@ -551,14 +558,11 @@ class Config(object):
def config_file(self):
return self.__config_file
- @prop
- def config(): # pylint: disable=no-method-argument
+ @property
+ def config(self):
"""The config dictionary"""
+ return self.__config
- def fget(self):
- return self.__config
-
- def fdel(self):
- return self.save()
-
- return locals()
+ @config.deleter
+ def config(self):
+ return self.save()
diff --git a/deluge/configmanager.py b/deluge/configmanager.py
index bbb0389a5..6e965b863 100644
--- a/deluge/configmanager.py
+++ b/deluge/configmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
@@ -19,7 +16,7 @@ from deluge.config import Config
log = logging.getLogger(__name__)
-class _ConfigManager(object):
+class _ConfigManager:
def __init__(self):
log.debug('ConfigManager started..')
self.config_files = {}
diff --git a/deluge/conftest.py b/deluge/conftest.py
new file mode 100644
index 000000000..c3070140d
--- /dev/null
+++ b/deluge/conftest.py
@@ -0,0 +1,214 @@
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+import asyncio
+import tempfile
+import warnings
+from unittest.mock import Mock, patch
+
+import pytest
+import pytest_twisted
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, maybeDeferred
+from twisted.internet.error import CannotListenError, ProcessTerminated
+from twisted.python.failure import Failure
+
+import deluge.component as _component
+import deluge.configmanager
+from deluge.common import get_localhost_auth
+from deluge.tests import common
+from deluge.ui.client import client as _client
+
+DEFAULT_LISTEN_PORT = 58900
+
+
+@pytest.fixture
+def listen_port(request):
+ if request and 'daemon' in request.fixturenames:
+ try:
+ return request.getfixturevalue('daemon').listen_port
+ except Exception:
+ pass
+ return DEFAULT_LISTEN_PORT
+
+
+@pytest.fixture
+def mock_callback():
+ """Returns a `Mock` object which can be registered as a callback to test against.
+
+ If callback was not called within `timeout` seconds, it will raise a TimeoutError.
+ The returned Mock instance will have a `deferred` attribute which will complete when the callback has been called.
+ """
+
+ def reset(timeout=0.5, *args, **kwargs):
+ if mock.called:
+ original_reset_mock(*args, **kwargs)
+ if mock.deferred:
+ mock.deferred.cancel()
+ deferred = Deferred(canceller=lambda x: deferred.callback(None))
+ deferred.addTimeout(timeout, reactor)
+ mock.side_effect = lambda *args, **kw: deferred.callback((args, kw))
+ mock.deferred = deferred
+
+ mock = Mock()
+ original_reset_mock = mock.reset_mock
+ mock.reset_mock = reset
+ mock.reset_mock()
+ return mock
+
+
+@pytest.fixture
+def config_dir(tmp_path):
+ config_dir = tmp_path / 'config'
+ deluge.configmanager.set_config_dir(config_dir)
+ yield config_dir
+
+
+@pytest_twisted.async_yield_fixture()
+async def client(request, config_dir, monkeypatch, listen_port):
+ # monkeypatch.setattr(
+ # _client, 'connect', functools.partial(_client.connect, port=listen_port)
+ # )
+ try:
+ username, password = get_localhost_auth()
+ except Exception:
+ username, password = '', ''
+ await _client.connect(
+ 'localhost',
+ port=listen_port,
+ username=username,
+ password=password,
+ )
+ yield _client
+ if _client.connected():
+ await _client.disconnect()
+
+
+@pytest_twisted.async_yield_fixture
+async def daemon(request, config_dir, tmp_path):
+ listen_port = DEFAULT_LISTEN_PORT
+ logfile = tmp_path / 'daemon.log'
+
+ if hasattr(request.cls, 'daemon_custom_script'):
+ custom_script = request.cls.daemon_custom_script
+ else:
+ custom_script = ''
+
+ for dummy in range(10):
+ try:
+ d, daemon = common.start_core(
+ listen_port=listen_port,
+ logfile=logfile,
+ timeout=5,
+ timeout_msg='Timeout!',
+ custom_script=custom_script,
+ print_stdout=True,
+ print_stderr=True,
+ config_directory=config_dir,
+ )
+ await d
+ except CannotListenError as ex:
+ exception_error = ex
+ listen_port += 1
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ else:
+ break
+ else:
+ raise exception_error
+ daemon.listen_port = listen_port
+ yield daemon
+ try:
+ await daemon.kill()
+ except ProcessTerminated:
+ pass
+
+
+@pytest.fixture(autouse=True)
+def common_fixture(config_dir, request, monkeypatch, listen_port):
+ """Adds some instance attributes to test classes for backwards compatibility with old testing."""
+
+ def fail(self, reason):
+ if isinstance(reason, Failure):
+ reason = reason.value
+ return pytest.fail(str(reason))
+
+ if request.instance:
+ request.instance.patch = monkeypatch.setattr
+ request.instance.config_dir = config_dir
+ request.instance.listen_port = listen_port
+ request.instance.id = lambda: request.node.name
+ request.cls.fail = fail
+
+
+@pytest_twisted.async_yield_fixture(scope='function')
+async def component():
+ """Verify component registry is clean, and clean up after test."""
+ if len(_component._ComponentRegistry.components) != 0:
+ warnings.warn(
+ 'The component._ComponentRegistry.components is not empty on test setup.\n'
+ 'This is probably caused by another test that did not clean up after finishing!: %s'
+ % _component._ComponentRegistry.components
+ )
+
+ yield _component
+
+ await _component.shutdown()
+ _component._ComponentRegistry.components.clear()
+ _component._ComponentRegistry.dependents.clear()
+
+
+@pytest_twisted.async_yield_fixture(scope='function')
+async def base_fixture(common_fixture, component, request):
+ """This fixture is autoused on all tests that subclass BaseTestCase"""
+ self = request.instance
+
+ if hasattr(self, 'set_up'):
+ try:
+ await maybeDeferred(self.set_up)
+ except Exception as exc:
+ warnings.warn('Error caught in test setup!\n%s' % exc)
+ pytest.fail('Error caught in test setup!\n%s' % exc)
+
+ yield
+
+ if hasattr(self, 'tear_down'):
+ try:
+ await maybeDeferred(self.tear_down)
+ except Exception as exc:
+ pytest.fail('Error caught in test teardown!\n%s' % exc)
+
+
+@pytest.mark.usefixtures('base_fixture')
+class BaseTestCase:
+ """This is the base class that should be used for all test classes
+ that create classes that inherit from deluge.component.Component. It
+ ensures that the component registry has been cleaned up when tests
+ have finished.
+
+ """
+
+
+@pytest.fixture
+def mock_mkstemp(tmp_path):
+ """Return known tempfile location to verify file deleted"""
+ tmp_file = tempfile.mkstemp(dir=tmp_path)
+ with patch('tempfile.mkstemp', return_value=tmp_file):
+ yield tmp_file
+
+
+def pytest_collection_modifyitems(session, config, items) -> None:
+ """
+ Automatically runs async tests with pytest_twisted.ensureDeferred
+ """
+ function_items = (item for item in items if isinstance(item, pytest.Function))
+ for function_item in function_items:
+ function = function_item.obj
+ if hasattr(function, '__func__'):
+ # methods need to be unwrapped.
+ function = function.__func__
+ if asyncio.iscoroutinefunction(function):
+ # This is how pytest_twisted marks ensureDeferred tests
+ setattr(function, '_pytest_twisted_mark', 'async_test')
diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py
index 2fe42224d..cf541f015 100644
--- a/deluge/core/alertmanager.py
+++ b/deluge/core/alertmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -15,12 +14,15 @@ This should typically only be used by the Core. Plugins should utilize the
`:mod:EventManager` for similar functionality.
"""
-from __future__ import unicode_literals
-
+import contextlib
import logging
-import types
+import threading
+import time
+from collections import defaultdict
+from functools import partial
+from typing import Any, Callable
-from twisted.internet import reactor
+from twisted.internet import reactor, task, threads
import deluge.component as component
from deluge._libtorrent import lt
@@ -28,21 +30,13 @@ 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"""
def __init__(self):
log.debug('AlertManager init...')
- component.Component.__init__(self, 'AlertManager', interval=0.3)
+ component.Component.__init__(self, 'AlertManager')
self.session = component.get('Core').session
# Increase the alert queue size so that alerts don't get lost.
@@ -57,53 +51,94 @@ class AlertManager(component.Component):
| lt.alert.category_t.status_notification
| lt.alert.category_t.ip_block_notification
| lt.alert.category_t.performance_warning
+ | lt.alert.category_t.file_progress_notification
)
self.session.apply_settings({'alert_mask': alert_mask})
# handlers is a dictionary of lists {"alert_type": [handler1,h2,..]}
- self.handlers = {}
+ self.handlers = defaultdict(list)
+ self.handlers_timeout_secs = 2
self.delayed_calls = []
+ self._event = threading.Event()
def update(self):
- self.delayed_calls = [dc for dc in self.delayed_calls if dc.active()]
- self.handle_alerts()
+ pass
+
+ def start(self):
+ thread = threading.Thread(
+ target=self.wait_for_alert_in_thread, name='alert-poller', daemon=True
+ )
+ thread.start()
+ self._event.set()
def stop(self):
+ self.cancel_delayed_calls()
+
+ def pause(self):
+ self._event.clear()
+
+ def resume(self):
+ self._event.set()
+
+ def wait_for_alert_in_thread(self):
+ while self._component_state not in ('Stopping', 'Stopped'):
+ if self.check_delayed_calls():
+ time.sleep(0.05)
+ continue
+
+ if self.session.wait_for_alert(1000) is None:
+ continue
+ if self._event.wait():
+ threads.blockingCallFromThread(reactor, self.maybe_handle_alerts)
+
+ def on_delayed_call_timeout(self, result, timeout, **kwargs):
+ log.warning('Alert handler was timed-out before being called %s', kwargs)
+
+ def cancel_delayed_calls(self):
+ """Cancel all delayed handlers."""
for delayed_call in self.delayed_calls:
- if delayed_call.active():
- delayed_call.cancel()
+ delayed_call.cancel()
self.delayed_calls = []
- def register_handler(self, alert_type, handler):
+ def check_delayed_calls(self) -> bool:
+ """Returns True if any handler calls are delayed."""
+ self.delayed_calls = [dc for dc in self.delayed_calls if not dc.called]
+ return len(self.delayed_calls) > 0
+
+ def maybe_handle_alerts(self) -> None:
+ if self._component_state != 'Started':
+ return
+
+ self.handle_alerts()
+
+ def register_handler(self, alert_type: str, handler: Callable[[Any], None]) -> None:
"""
Registers a function that will be called when 'alert_type' is pop'd
in handle_alerts. The handler function should look like: handler(alert)
Where 'alert' is the actual alert object from libtorrent.
- :param alert_type: str, this is string representation of the alert name
- :param handler: func(alert), the function to be called when the alert is raised
+ Args:
+ alert_type: String representation of the libtorrent alert name.
+ Can be supplied with or without `_alert` suffix.
+ handler: Callback function when the alert is raised.
"""
- if alert_type not in self.handlers:
- # There is no entry for this alert type yet, so lets make it with an
- # empty list.
- self.handlers[alert_type] = []
+ if alert_type and alert_type.endswith('_alert'):
+ alert_type = alert_type[: -len('_alert')]
- # Append the handler to the list in the handlers dictionary
self.handlers[alert_type].append(handler)
log.debug('Registered handler for alert %s', alert_type)
- def deregister_handler(self, handler):
+ def deregister_handler(self, handler: Callable[[Any], None]):
"""
- De-registers the `:param:handler` function from all alert types.
+ De-registers the `handler` function from all alert types.
- :param handler: func, the handler function to deregister
+ Args:
+ handler: The handler function to deregister.
"""
- # Iterate through all handlers and remove 'handler' where found
- for (dummy_key, value) in self.handlers.items():
- if handler in value:
- # Handler is in this alert type list
- value.remove(handler)
+ for alert_type_handlers in self.handlers.values():
+ with contextlib.suppress(ValueError):
+ alert_type_handlers.remove(handler)
def handle_alerts(self):
"""
@@ -122,26 +157,32 @@ class AlertManager(component.Component):
num_alerts,
)
- # Loop through all alerts in the queue
for alert in alerts:
- alert_type = type(alert).__name__
+ alert_type = alert.what()
+
# Display the alert message
if log.isEnabledFor(logging.DEBUG):
log.debug('%s: %s', alert_type, decode_bytes(alert.message()))
+
+ if alert_type not in self.handlers:
+ continue
+
# Call any handlers for this alert type
- if alert_type in self.handlers:
- for handler in self.handlers[alert_type]:
- if log.isEnabledFor(logging.DEBUG):
- log.debug('Handling alert: %s', alert_type)
- # Copy alert attributes
- alert_copy = SimpleNamespace(
- **{
- attr: getattr(alert, attr)
- for attr in dir(alert)
- if not attr.startswith('__')
- }
- )
- self.delayed_calls.append(reactor.callLater(0, handler, alert_copy))
+ for handler in self.handlers[alert_type]:
+ if log.isEnabledFor(logging.DEBUG):
+ log.debug('Handling alert: %s', alert_type)
+ d = task.deferLater(reactor, 0, handler, alert)
+ on_handler_timeout = partial(
+ self.on_delayed_call_timeout,
+ handler=handler.__qualname__,
+ alert_type=alert_type,
+ )
+ d.addTimeout(
+ self.handlers_timeout_secs,
+ reactor,
+ onTimeoutCancel=on_handler_timeout,
+ )
+ self.delayed_calls.append(d)
def set_alert_queue_size(self, queue_size):
"""Sets the maximum size of the libtorrent alert queue"""
diff --git a/deluge/core/authmanager.py b/deluge/core/authmanager.py
index 0d997c10c..3ff8a3ad9 100644
--- a/deluge/core/authmanager.py
+++ b/deluge/core/authmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -8,12 +7,9 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import shutil
-from io import open
import deluge.component as component
import deluge.configmanager as configmanager
@@ -32,14 +28,14 @@ log = logging.getLogger(__name__)
AUTH_LEVELS_MAPPING = {
'NONE': AUTH_LEVEL_NONE,
'READONLY': AUTH_LEVEL_READONLY,
- 'DEFAULT': AUTH_LEVEL_NORMAL,
- 'NORMAL': AUTH_LEVEL_DEFAULT,
+ 'DEFAULT': AUTH_LEVEL_DEFAULT,
+ 'NORMAL': AUTH_LEVEL_NORMAL,
'ADMIN': AUTH_LEVEL_ADMIN,
}
AUTH_LEVELS_MAPPING_REVERSE = {v: k for k, v in AUTH_LEVELS_MAPPING.items()}
-class Account(object):
+class Account:
__slots__ = ('username', 'password', 'authlevel')
def __init__(self, username, password, authlevel):
@@ -56,10 +52,10 @@ class Account(object):
}
def __repr__(self):
- return '<Account username="%(username)s" authlevel=%(authlevel)s>' % {
- 'username': self.username,
- 'authlevel': self.authlevel,
- }
+ return '<Account username="{username}" authlevel={authlevel}>'.format(
+ username=self.username,
+ authlevel=self.authlevel,
+ )
class AuthManager(component.Component):
@@ -101,7 +97,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.
"""
@@ -184,7 +180,7 @@ class AuthManager(component.Component):
if os.path.isfile(filepath):
log.debug('Creating backup of %s at: %s', filename, filepath_bak)
shutil.copy2(filepath, filepath_bak)
- except IOError as ex:
+ except OSError as ex:
log.error('Unable to backup %s to %s: %s', filepath, filepath_bak, ex)
else:
log.info('Saving the %s at: %s', filename, filepath)
@@ -198,7 +194,7 @@ class AuthManager(component.Component):
_file.flush()
os.fsync(_file.fileno())
shutil.move(filepath_tmp, filepath)
- except IOError as ex:
+ except OSError as ex:
log.error('Unable to save %s: %s', filename, ex)
if os.path.isfile(filepath_bak):
log.info('Restoring backup of %s from: %s', filename, filepath_bak)
@@ -227,9 +223,9 @@ class AuthManager(component.Component):
for _filepath in (auth_file, auth_file_bak):
log.info('Opening %s for load: %s', filename, _filepath)
try:
- with open(_filepath, 'r', encoding='utf8') as _file:
+ with open(_filepath, encoding='utf8') as _file:
file_data = _file.readlines()
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to load %s: %s', _filepath, ex)
file_data = []
else:
diff --git a/deluge/core/core.py b/deluge/core/core.py
index cdf1ec39b..e2130f595 100644
--- a/deluge/core/core.py
+++ b/deluge/core/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -8,23 +7,21 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import glob
import logging
import os
import shutil
import tempfile
-import threading
from base64 import b64decode, b64encode
+from typing import Any, Dict, List, Optional, Tuple, Union
+from urllib.request import URLError, urlopen
-from six import string_types
-from twisted.internet import defer, reactor, task
+from twisted.internet import defer, reactor, task, threads
from twisted.web.client import Agent, readBody
import deluge.common
import deluge.component as component
-from deluge import path_chooser_common
+from deluge import metafile, path_chooser_common
from deluge._libtorrent import LT_VERSION, lt
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.core.alertmanager import AlertManager
@@ -41,7 +38,7 @@ from deluge.core.pluginmanager import PluginManager
from deluge.core.preferencesmanager import PreferencesManager
from deluge.core.rpcserver import export
from deluge.core.torrentmanager import TorrentManager
-from deluge.decorators import deprecated
+from deluge.decorators import deprecated, maybe_coroutine
from deluge.error import (
AddTorrentError,
DelugeError,
@@ -56,12 +53,6 @@ from deluge.event import (
)
from deluge.httpdownloader import download_file
-try:
- from urllib.request import URLError, urlopen
-except ImportError:
- # PY2 fallback
- from urllib2 import URLError, urlopen
-
log = logging.getLogger(__name__)
DEPR_SESSION_STATUS_KEYS = {
@@ -120,7 +111,7 @@ class Core(component.Component):
component.Component.__init__(self, 'Core')
# Start the libtorrent session.
- user_agent = 'Deluge/{} libtorrent/{}'.format(DELUGE_VER, LT_VERSION)
+ user_agent = f'Deluge/{DELUGE_VER} libtorrent/{LT_VERSION}'
peer_id = self._create_peer_id(DELUGE_VER)
log.debug('Starting session (peer_id: %s, user_agent: %s)', peer_id, user_agent)
settings_pack = {
@@ -173,19 +164,25 @@ class Core(component.Component):
# store the one in the config so we can restore it on shutdown
self._old_listen_interface = None
if listen_interface:
- if deluge.common.is_ip(listen_interface):
+ if deluge.common.is_interface(listen_interface):
self._old_listen_interface = self.config['listen_interface']
self.config['listen_interface'] = listen_interface
else:
log.error(
- 'Invalid listen interface (must be IP Address): %s',
+ 'Invalid listen interface (must be IP Address or Interface Name): %s',
listen_interface,
)
self._old_outgoing_interface = None
if outgoing_interface:
- self._old_outgoing_interface = self.config['outgoing_interface']
- self.config['outgoing_interface'] = outgoing_interface
+ if deluge.common.is_interface(outgoing_interface):
+ self._old_outgoing_interface = self.config['outgoing_interface']
+ self.config['outgoing_interface'] = outgoing_interface
+ else:
+ log.error(
+ 'Invalid outgoing interface (must be IP Address or Interface Name): %s',
+ outgoing_interface,
+ )
# New release check information
self.__new_release = None
@@ -201,7 +198,7 @@ class Core(component.Component):
self.session_status_timer_interval = 0.5
self.session_status_timer = task.LoopingCall(self.session.post_session_stats)
self.alertmanager.register_handler(
- 'session_stats_alert', self._on_alert_session_stats
+ 'session_stats', self._on_alert_session_stats
)
self.session_rates_timer_interval = 2
self.session_rates_timer = task.LoopingCall(self._update_session_rates)
@@ -243,13 +240,12 @@ class Core(component.Component):
"""Apply libtorrent session settings.
Args:
- settings (dict): A dict of lt session settings to apply.
-
+ settings: A dict of lt session settings to apply.
"""
self.session.apply_settings(settings)
@staticmethod
- def _create_peer_id(version):
+ def _create_peer_id(version: str) -> str:
"""Create a peer_id fingerprint.
This creates the peer_id and modifies the release char to identify
@@ -264,11 +260,10 @@ class Core(component.Component):
``--DE201b--`` (beta pre-release of v2.0.1)
Args:
- version (str): The version string in PEP440 dotted notation.
+ version: The version string in PEP440 dotted notation.
Returns:
- str: The formattted peer_id with Deluge prefix e.g. '--DE200s--'
-
+ The formatted peer_id with Deluge prefix e.g. '--DE200s--'
"""
split = deluge.common.VersionSplit(version)
# Fill list with zeros to length of 4 and use lt to create fingerprint.
@@ -301,7 +296,7 @@ class Core(component.Component):
if os.path.isfile(filepath):
log.debug('Creating backup of %s at: %s', filename, filepath_bak)
shutil.copy2(filepath, filepath_bak)
- except IOError as ex:
+ except OSError as ex:
log.error('Unable to backup %s to %s: %s', filepath, filepath_bak, ex)
else:
log.info('Saving the %s at: %s', filename, filepath)
@@ -311,18 +306,17 @@ class Core(component.Component):
_file.flush()
os.fsync(_file.fileno())
shutil.move(filepath_tmp, filepath)
- except (IOError, EOFError) as ex:
+ except (OSError, EOFError) as ex:
log.error('Unable to save %s: %s', filename, ex)
if os.path.isfile(filepath_bak):
log.info('Restoring backup of %s from: %s', filename, filepath_bak)
shutil.move(filepath_bak, filepath)
- def _load_session_state(self):
+ def _load_session_state(self) -> dict:
"""Loads the libtorrent session state
Returns:
- dict: A libtorrent sesion state, empty dict if unable to load it.
-
+ A libtorrent sesion state, empty dict if unable to load it.
"""
filename = 'session.state'
filepath = get_config_dir(filename)
@@ -333,7 +327,7 @@ class Core(component.Component):
try:
with open(_filepath, 'rb') as _file:
state = lt.bdecode(_file.read())
- except (IOError, EOFError, RuntimeError) as ex:
+ except (OSError, EOFError, RuntimeError) as ex:
log.warning('Unable to load %s: %s', _filepath, ex)
else:
log.info('Successfully loaded %s: %s', filename, _filepath)
@@ -358,8 +352,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
@@ -404,18 +398,19 @@ class Core(component.Component):
# Exported Methods
@export
- def add_torrent_file_async(self, filename, filedump, options, save_state=True):
+ def add_torrent_file_async(
+ self, filename: str, filedump: str, options: dict, save_state: bool = True
+ ) -> 'defer.Deferred[Optional[str]]':
"""Adds a torrent file to the session asynchronously.
Args:
- filename (str): The filename of the torrent.
- filedump (str): A base64 encoded string of torrent file contents.
- options (dict): The options to apply to the torrent upon adding.
- save_state (bool): If the state should be saved after adding the file.
+ filename: The filename of the torrent.
+ filedump: A base64 encoded string of torrent file contents.
+ options: The options to apply to the torrent upon adding.
+ save_state: If the state should be saved after adding the file.
Returns:
- Deferred: The torrent ID or None.
-
+ The torrent ID or None.
"""
try:
filedump = b64decode(filedump)
@@ -436,42 +431,39 @@ class Core(component.Component):
return d
@export
- def prefetch_magnet_metadata(self, magnet, timeout=30):
+ @maybe_coroutine
+ async def prefetch_magnet_metadata(
+ self, magnet: str, timeout: int = 30
+ ) -> Tuple[str, bytes]:
"""Download magnet metadata without adding to Deluge session.
Used by UIs to get magnet files for selection before adding to session.
+ The metadata is bencoded and for transfer base64 encoded.
+
Args:
- magnet (str): The magnet URI.
- timeout (int): Number of seconds to wait before canceling request.
+ magnet: The magnet URI.
+ timeout: Number of seconds to wait before canceling request.
Returns:
- Deferred: A tuple of (torrent_id (str), metadata (dict)) for the magnet.
+ A tuple of (torrent_id, metadata) for the magnet.
"""
-
- def on_metadata(result, result_d):
- """Return result of torrent_id and metadata"""
- result_d.callback(result)
- return result
-
- d = self.torrentmanager.prefetch_metadata(magnet, timeout)
- # Use a seperate callback chain to handle existing prefetching magnet.
- result_d = defer.Deferred()
- d.addBoth(on_metadata, result_d)
- return result_d
+ return await self.torrentmanager.prefetch_metadata(magnet, timeout)
@export
- def add_torrent_file(self, filename, filedump, options):
+ def add_torrent_file(
+ self, filename: str, filedump: Union[str, bytes], options: dict
+ ) -> Optional[str]:
"""Adds a torrent file to the session.
Args:
- filename (str): The filename of the torrent.
- filedump (str): A base64 encoded string of the torrent file contents.
- options (dict): The options to apply to the torrent upon adding.
+ filename: The filename of the torrent.
+ filedump: A base64 encoded string of the torrent file contents.
+ options: The options to apply to the torrent upon adding.
Returns:
- str: The torrent_id or None.
+ The torrent_id or None.
"""
try:
filedump = b64decode(filedump)
@@ -487,25 +479,26 @@ class Core(component.Component):
raise
@export
- def add_torrent_files(self, torrent_files):
+ def add_torrent_files(
+ self, torrent_files: List[Tuple[str, Union[str, bytes], dict]]
+ ) -> 'defer.Deferred[List[AddTorrentError]]':
"""Adds multiple torrent files to the session asynchronously.
Args:
- torrent_files (list of tuples): Torrent files as tuple of
- ``(filename, filedump, options)``.
+ torrent_files: Torrent files as tuple of
+ ``(filename, filedump, options)``.
Returns:
- Deferred
-
+ A list of errors (if there were any)
"""
- @defer.inlineCallbacks
- def add_torrents():
+ @maybe_coroutine
+ async def add_torrents():
errors = []
last_index = len(torrent_files) - 1
for idx, torrent in enumerate(torrent_files):
try:
- yield self.add_torrent_file_async(
+ await self.add_torrent_file_async(
torrent[0], torrent[1], torrent[2], save_state=idx == last_index
)
except AddTorrentError as ex:
@@ -516,93 +509,89 @@ class Core(component.Component):
return task.deferLater(reactor, 0, add_torrents)
@export
- def add_torrent_url(self, url, options, headers=None):
- """
- Adds a torrent from a URL. Deluge will attempt to fetch the torrent
+ @maybe_coroutine
+ async def add_torrent_url(
+ self, url: str, options: dict, headers: dict = None
+ ) -> 'defer.Deferred[Optional[str]]':
+ """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
- :type url: string
- :param options: the options to apply to the torrent on add
- :type options: dict
- :param headers: any optional headers to send
- :type headers: dict
+ Args:
+ url: the URL pointing to the torrent file
+ options: the options to apply to the torrent on add
+ headers: any optional headers to send
- :returns: a Deferred which returns the torrent_id as a str or None
+ Returns:
+ a Deferred which returns the torrent_id as a str or None
"""
log.info('Attempting to add URL %s', url)
- def on_download_success(filename):
- # We got the file, so add it to the session
+ tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent')
+ try:
+ filename = await download_file(
+ url, tmp_file, headers=headers, force_filename=True
+ )
+ except Exception:
+ log.error('Failed to add torrent from URL %s', url)
+ raise
+ else:
with open(filename, 'rb') as _file:
data = _file.read()
+ return self.add_torrent_file(filename, b64encode(data), options)
+ finally:
try:
- os.remove(filename)
+ os.close(tmp_fd)
+ os.remove(tmp_file)
except OSError as ex:
- log.warning('Could not remove temp file: %s', ex)
- return self.add_torrent_file(filename, b64encode(data), options)
-
- 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)
- return failure
-
- tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent')
- os.close(tmp_fd)
- d = download_file(url, tmp_file, headers=headers, force_filename=True)
- d.addCallbacks(on_download_success, on_download_fail)
- return d
+ log.warning(f'Unable to delete temp file {tmp_file}: , {ex}')
@export
- def add_torrent_magnet(self, uri, options):
- """
- Adds a torrent from a magnet link.
-
- :param uri: the magnet link
- :type uri: string
- :param options: the options to apply to the torrent on add
- :type options: dict
+ def add_torrent_magnet(self, uri: str, options: dict) -> str:
+ """Adds a torrent from a magnet link.
- :returns: the torrent_id
- :rtype: string
+ Args:
+ uri: the magnet link
+ options: the options to apply to the torrent on add
+ Returns:
+ the torrent_id
"""
log.debug('Attempting to add by magnet URI: %s', uri)
return self.torrentmanager.add(magnet=uri, options=options)
@export
- def remove_torrent(self, torrent_id, remove_data):
+ def remove_torrent(self, torrent_id: str, remove_data: bool) -> bool:
"""Removes a single torrent from the session.
Args:
- torrent_id (str): The torrent ID to remove.
- remove_data (bool): If True, also remove the downloaded data.
+ torrent_id: The torrent ID to remove.
+ remove_data: If True, also remove the downloaded data.
Returns:
- bool: True if removed successfully.
+ True if removed successfully.
Raises:
InvalidTorrentError: If the torrent ID does not exist in the session.
-
"""
log.debug('Removing torrent %s from the core.', torrent_id)
return self.torrentmanager.remove(torrent_id, remove_data)
@export
- def remove_torrents(self, torrent_ids, remove_data):
+ def remove_torrents(
+ self, torrent_ids: List[str], remove_data: bool
+ ) -> 'defer.Deferred[List[Tuple[str, str]]]':
"""Remove multiple torrents from the session.
Args:
- torrent_ids (list): The torrent IDs to remove.
- remove_data (bool): If True, also remove the downloaded data.
+ torrent_ids: The torrent IDs to remove.
+ remove_data: If True, also remove the downloaded data.
Returns:
- list: An empty list if no errors occurred otherwise the list contains
- tuples of strings, a torrent ID and an error message. For example:
-
- [('<torrent_id>', 'Error removing torrent')]
+ An empty list if no errors occurred otherwise the list contains
+ tuples of strings, a torrent ID and an error message. For example:
+ [('<torrent_id>', 'Error removing torrent')]
"""
log.info('Removing %d torrents from core.', len(torrent_ids))
@@ -626,17 +615,17 @@ class Core(component.Component):
return task.deferLater(reactor, 0, do_remove_torrents)
@export
- def get_session_status(self, keys):
+ def get_session_status(self, keys: List[str]) -> Dict[str, Union[int, float]]:
"""Gets the session status values for 'keys', these keys are taking
from libtorrent's session status.
See: http://www.rasterbar.com/products/libtorrent/manual.html#status
- :param keys: the keys for which we want values
- :type keys: list
- :returns: a dictionary of {key: value, ...}
- :rtype: dict
+ Args:
+ keys: the keys for which we want values
+ Returns:
+ a dictionary of {key: value, ...}
"""
if not keys:
return self.session_status
@@ -657,22 +646,22 @@ class Core(component.Component):
return status
@export
- def force_reannounce(self, torrent_ids):
+ def force_reannounce(self, torrent_ids: List[str]) -> None:
log.debug('Forcing reannouncment to: %s', torrent_ids)
for torrent_id in torrent_ids:
self.torrentmanager[torrent_id].force_reannounce()
@export
- def pause_torrent(self, torrent_id):
+ def pause_torrent(self, torrent_id: str) -> None:
"""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()
@export
- def pause_torrents(self, torrent_ids=None):
+ def pause_torrents(self, torrent_ids: List[str] = None) -> None:
"""Pauses a list of torrents"""
if not torrent_ids:
torrent_ids = self.torrentmanager.get_torrent_list()
@@ -680,27 +669,27 @@ class Core(component.Component):
self.pause_torrent(torrent_id)
@export
- def connect_peer(self, torrent_id, ip, port):
+ def connect_peer(self, torrent_id: str, ip: str, port: int):
log.debug('adding peer %s to %s', ip, torrent_id)
if not self.torrentmanager[torrent_id].connect_peer(ip, port):
log.warning('Error adding peer %s:%s to %s', ip, port, torrent_id)
@export
- def move_storage(self, torrent_ids, dest):
+ def move_storage(self, torrent_ids: List[str], dest: str):
log.debug('Moving storage %s to %s', torrent_ids, dest)
for torrent_id in torrent_ids:
if not self.torrentmanager[torrent_id].move_storage(dest):
log.warning('Error moving torrent %s to %s', torrent_id, dest)
@export
- def pause_session(self):
+ def pause_session(self) -> None:
"""Pause the entire session"""
if not self.session.is_paused():
self.session.pause()
component.get('EventManager').emit(SessionPausedEvent())
@export
- def resume_session(self):
+ def resume_session(self) -> None:
"""Resume the entire session"""
if self.session.is_paused():
self.session.resume()
@@ -709,21 +698,21 @@ class Core(component.Component):
component.get('EventManager').emit(SessionResumedEvent())
@export
- def is_session_paused(self):
+ def is_session_paused(self) -> bool:
"""Returns the activity of the session"""
return self.session.is_paused()
@export
- def resume_torrent(self, torrent_id):
+ def resume_torrent(self, torrent_id: str) -> None:
"""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()
@export
- def resume_torrents(self, torrent_ids=None):
+ def resume_torrents(self, torrent_ids: List[str] = None) -> None:
"""Resumes a list of torrents"""
if not torrent_ids:
torrent_ids = self.torrentmanager.get_torrent_list()
@@ -747,7 +736,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
@@ -756,7 +745,9 @@ class Core(component.Component):
return status
@export
- def get_torrent_status(self, torrent_id, keys, diff=False):
+ def get_torrent_status(
+ self, torrent_id: str, keys: List[str], diff: bool = False
+ ) -> dict:
torrent_keys, plugin_keys = self.torrentmanager.separate_keys(
keys, [torrent_id]
)
@@ -770,57 +761,54 @@ class Core(component.Component):
)
@export
- def get_torrents_status(self, filter_dict, keys, diff=False):
- """
- returns all torrents , optionally filtered by filter_dict.
- """
+ @maybe_coroutine
+ async def get_torrents_status(
+ self, filter_dict: dict, keys: List[str], diff: bool = False
+ ) -> dict:
+ """returns all torrents , optionally filtered by filter_dict."""
+ all_keys = not keys
torrent_ids = self.filtermanager.filter_torrent_ids(filter_dict)
- d = self.torrentmanager.torrents_status_update(torrent_ids, keys, diff=diff)
-
- def add_plugin_fields(args):
- status_dict, plugin_keys = args
- # Ask the plugin manager to fill in the plugin keys
- if len(plugin_keys) > 0:
- for key in status_dict:
- status_dict[key].update(
- self.pluginmanager.get_status(key, plugin_keys)
- )
- return status_dict
-
- d.addCallback(add_plugin_fields)
- return d
+ status_dict, plugin_keys = await self.torrentmanager.torrents_status_update(
+ torrent_ids, keys, diff=diff
+ )
+ # Ask the plugin manager to fill in the plugin keys
+ if len(plugin_keys) > 0 or all_keys:
+ for key in status_dict:
+ status_dict[key].update(self.pluginmanager.get_status(key, plugin_keys))
+ return status_dict
@export
- def get_filter_tree(self, show_zero_hits=True, hide_cat=None):
- """
- returns {field: [(value,count)] }
+ def get_filter_tree(
+ self, show_zero_hits: bool = True, hide_cat: List[str] = None
+ ) -> Dict:
+ """returns {field: [(value,count)] }
for use in sidebar(s)
"""
return self.filtermanager.get_filter_tree(show_zero_hits, hide_cat)
@export
- def get_session_state(self):
+ def get_session_state(self) -> List[str]:
"""Returns a list of torrent_ids in the session."""
# Get the torrent list from the TorrentManager
return self.torrentmanager.get_torrent_list()
@export
- def get_config(self):
+ def get_config(self) -> dict:
"""Get all the preferences as a dictionary"""
return self.config.config
@export
- def get_config_value(self, key):
+ def get_config_value(self, key: str) -> Any:
"""Get the config value for key"""
return self.config.get(key)
@export
- def get_config_values(self, keys):
+ def get_config_values(self, keys: List[str]) -> Dict[str, Any]:
"""Get the config values for the entered keys"""
return {key: self.config.get(key) for key in keys}
@export
- def set_config(self, config):
+ def set_config(self, config: Dict[str, Any]):
"""Set the config with values from dictionary"""
# Load all the values into the configuration
for key in config:
@@ -829,21 +817,20 @@ class Core(component.Component):
self.config[key] = config[key]
@export
- def get_listen_port(self):
+ def get_listen_port(self) -> int:
"""Returns the active listen port"""
return self.session.listen_port()
@export
- def get_proxy(self):
+ def get_proxy(self) -> Dict[str, Any]:
"""Returns the proxy settings
Returns:
- dict: Contains proxy settings.
+ Proxy settings.
Notes:
Proxy type names:
0: None, 1: Socks4, 2: Socks5, 3: Socks5 w Auth, 4: HTTP, 5: HTTP w Auth, 6: I2P
-
"""
settings = self.session.get_settings()
@@ -866,54 +853,58 @@ class Core(component.Component):
return proxy_dict
@export
- def get_available_plugins(self):
+ def get_available_plugins(self) -> List[str]:
"""Returns a list of plugins available in the core"""
return self.pluginmanager.get_available_plugins()
@export
- def get_enabled_plugins(self):
+ def get_enabled_plugins(self) -> List[str]:
"""Returns a list of enabled plugins in the core"""
return self.pluginmanager.get_enabled_plugins()
@export
- def enable_plugin(self, plugin):
+ def enable_plugin(self, plugin: str) -> 'defer.Deferred[bool]':
return self.pluginmanager.enable_plugin(plugin)
@export
- def disable_plugin(self, plugin):
+ def disable_plugin(self, plugin: str) -> 'defer.Deferred[bool]':
return self.pluginmanager.disable_plugin(plugin)
@export
- def force_recheck(self, torrent_ids):
+ def force_recheck(self, torrent_ids: List[str]) -> None:
"""Forces a data recheck on torrent_ids"""
for torrent_id in torrent_ids:
self.torrentmanager[torrent_id].force_recheck()
@export
- def set_torrent_options(self, torrent_ids, options):
+ def set_torrent_options(
+ self, torrent_ids: List[str], options: Dict[str, Any]
+ ) -> None:
"""Sets the torrent options for torrent_ids
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_ids: A list of torrent_ids to set the options for.
+ options: 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:
self.torrentmanager[torrent_id].set_options(options)
@export
- def set_torrent_trackers(self, torrent_id, trackers):
+ def set_torrent_trackers(
+ self, torrent_id: str, trackers: List[Dict[str, Any]]
+ ) -> None:
"""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):
+ def get_magnet_uri(self, torrent_id: str) -> str:
return self.torrentmanager[torrent_id].get_magnet_uri()
@deprecated
@@ -1000,31 +991,33 @@ class Core(component.Component):
path,
tracker,
piece_length,
- comment,
- target,
- webseeds,
- private,
- created_by,
- trackers,
- add_to_session,
+ comment=None,
+ target=None,
+ webseeds=None,
+ private=False,
+ created_by=None,
+ trackers=None,
+ add_to_session=False,
+ torrent_format=metafile.TorrentFormat.V1,
):
+ if isinstance(torrent_format, str):
+ torrent_format = metafile.TorrentFormat(torrent_format)
log.debug('creating torrent..')
- threading.Thread(
- target=self._create_torrent_thread,
- args=(
- path,
- tracker,
- piece_length,
- comment,
- target,
- webseeds,
- private,
- created_by,
- trackers,
- add_to_session,
- ),
- ).start()
+ return threads.deferToThread(
+ self._create_torrent_thread,
+ path,
+ tracker,
+ piece_length,
+ comment=comment,
+ target=target,
+ webseeds=webseeds,
+ private=private,
+ created_by=created_by,
+ trackers=trackers,
+ add_to_session=add_to_session,
+ torrent_format=torrent_format,
+ )
def _create_torrent_thread(
self,
@@ -1038,30 +1031,44 @@ class Core(component.Component):
created_by,
trackers,
add_to_session,
+ torrent_format,
):
from deluge import metafile
- metafile.make_meta_file(
+ filecontent = metafile.make_meta_file_content(
path,
tracker,
piece_length,
comment=comment,
- target=target,
webseeds=webseeds,
private=private,
created_by=created_by,
trackers=trackers,
+ torrent_format=torrent_format,
)
+
+ write_file = False
+ if target or not add_to_session:
+ write_file = True
+
+ if not target:
+ target = metafile.default_meta_file_path(path)
+ filename = os.path.split(target)[-1]
+
+ if write_file:
+ with open(target, 'wb') as _file:
+ _file.write(filecontent)
+
+ filedump = b64encode(filecontent)
log.debug('torrent created!')
if add_to_session:
options = {}
options['download_location'] = os.path.split(path)[0]
- with open(target, 'rb') as _file:
- filedump = b64encode(_file.read())
- self.add_torrent_file(os.path.split(target)[1], filedump, options)
+ self.add_torrent_file(filename, filedump, options)
+ return filename, filedump
@export
- def upload_plugin(self, filename, filedump):
+ def upload_plugin(self, filename: str, filedump: Union[str, bytes]) -> None:
"""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,
@@ -1079,26 +1086,24 @@ class Core(component.Component):
component.get('CorePluginManager').scan_for_plugins()
@export
- def rescan_plugins(self):
- """
- Re-scans the plugin folders for new plugins
- """
+ def rescan_plugins(self) -> None:
+ """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
+ def rename_files(
+ self, torrent_id: str, filenames: List[Tuple[int, str]]
+ ) -> defer.Deferred:
+ """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.
- :param torrent_id: the torrent_id to rename files
- :type torrent_id: string
- :param filenames: a list of index, filename pairs
- :type filenames: ((index, filename), ...)
-
- :raises InvalidTorrentError: if torrent_id is invalid
+ Args:
+ torrent_id: the torrent_id to rename files
+ filenames: a list of index, filename pairs
+ Raises:
+ InvalidTorrentError: if torrent_id is invalid
"""
if torrent_id not in self.torrentmanager.torrents:
raise InvalidTorrentError('torrent_id is not in session')
@@ -1109,21 +1114,20 @@ class Core(component.Component):
return task.deferLater(reactor, 0, rename)
@export
- def rename_folder(self, torrent_id, folder, new_folder):
- """
- Renames the 'folder' to 'new_folder' in 'torrent_id'. Watch for the
+ def rename_folder(
+ self, torrent_id: str, folder: str, new_folder: str
+ ) -> defer.Deferred:
+ """Renames the 'folder' to 'new_folder' in 'torrent_id'. Watch for the
TorrentFolderRenamedEvent which is emitted when the folder has been
renamed successfully.
- :param torrent_id: the torrent to rename folder in
- :type torrent_id: string
- :param folder: the folder to rename
- :type folder: string
- :param new_folder: the new folder name
- :type new_folder: string
-
- :raises InvalidTorrentError: if the torrent_id is invalid
+ Args:
+ torrent_id: the torrent to rename folder in
+ folder: the folder to rename
+ new_folder: the new folder name
+ Raises:
+ InvalidTorrentError: if the torrent_id is invalid
"""
if torrent_id not in self.torrentmanager.torrents:
raise InvalidTorrentError('torrent_id is not in session')
@@ -1131,7 +1135,7 @@ class Core(component.Component):
return self.torrentmanager[torrent_id].rename_folder(folder, new_folder)
@export
- def queue_top(self, torrent_ids):
+ def queue_top(self, torrent_ids: List[str]) -> None:
log.debug('Attempting to queue %s to top', torrent_ids)
# torrent_ids must be sorted in reverse before moving to preserve order
for torrent_id in sorted(
@@ -1145,7 +1149,7 @@ class Core(component.Component):
log.warning('torrent_id: %s does not exist in the queue', torrent_id)
@export
- def queue_up(self, torrent_ids):
+ def queue_up(self, torrent_ids: List[str]) -> None:
log.debug('Attempting to queue %s to up', torrent_ids)
torrents = (
(self.torrentmanager.get_queue_position(torrent_id), torrent_id)
@@ -1170,7 +1174,7 @@ class Core(component.Component):
prev_queue_position = queue_position
@export
- def queue_down(self, torrent_ids):
+ def queue_down(self, torrent_ids: List[str]) -> None:
log.debug('Attempting to queue %s to down', torrent_ids)
torrents = (
(self.torrentmanager.get_queue_position(torrent_id), torrent_id)
@@ -1195,7 +1199,7 @@ class Core(component.Component):
prev_queue_position = queue_position
@export
- def queue_bottom(self, torrent_ids):
+ def queue_bottom(self, torrent_ids: List[str]) -> None:
log.debug('Attempting to queue %s to bottom', torrent_ids)
# torrent_ids must be sorted before moving to preserve order
for torrent_id in sorted(
@@ -1209,17 +1213,15 @@ class Core(component.Component):
log.warning('torrent_id: %s does not exist in the queue', torrent_id)
@export
- def glob(self, path):
+ def glob(self, path: str) -> List[str]:
return glob.glob(path)
@export
- def test_listen_port(self):
- """
- Checks if the active port is open
-
- :returns: True if the port is open, False if not
- :rtype: bool
+ def test_listen_port(self) -> 'defer.Deferred[Optional[bool]]':
+ """Checks if the active port is open
+ Returns:
+ True if the port is open, False if not
"""
port = self.get_listen_port()
url = 'https://deluge-torrent.org/test_port.php?port=%s' % port
@@ -1238,18 +1240,17 @@ class Core(component.Component):
return d
@export
- def get_free_space(self, path=None):
- """
- Returns the number of free bytes at path
+ def get_free_space(self, path: str = None) -> int:
+ """Returns the number of free bytes at path
- :param path: the path to check free space at, if None, use the default download location
- :type path: string
-
- :returns: the number of free bytes at path
- :rtype: int
+ Args:
+ path: the path to check free space at, if None, use the default download location
- :raises InvalidPathError: if the path is invalid
+ Returns:
+ the number of free bytes at path
+ Raises:
+ InvalidPathError: if the path is invalid
"""
if not path:
path = self.config['download_location']
@@ -1262,46 +1263,40 @@ class Core(component.Component):
self.external_ip = external_ip
@export
- def get_external_ip(self):
- """
- Returns the external IP address received from libtorrent.
- """
+ def get_external_ip(self) -> str:
+ """Returns the external IP address received from libtorrent."""
return self.external_ip
@export
- def get_libtorrent_version(self):
- """
- Returns the libtorrent version.
-
- :returns: the version
- :rtype: string
+ def get_libtorrent_version(self) -> str:
+ """Returns the libtorrent version.
+ Returns:
+ the version
"""
return LT_VERSION
@export
- def get_completion_paths(self, args):
- """
- Returns the available path completions for the input value.
- """
+ def get_completion_paths(self, args: Dict[str, Any]) -> Dict[str, Any]:
+ """Returns the available path completions for the input value."""
return path_chooser_common.get_completion_paths(args)
@export(AUTH_LEVEL_ADMIN)
- def get_known_accounts(self):
+ def get_known_accounts(self) -> List[Dict[str, Any]]:
return self.authmanager.get_known_accounts()
@export(AUTH_LEVEL_NONE)
- def get_auth_levels_mappings(self):
+ def get_auth_levels_mappings(self) -> Tuple[Dict[str, int], Dict[int, str]]:
return (AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE)
@export(AUTH_LEVEL_ADMIN)
- def create_account(self, username, password, authlevel):
+ def create_account(self, username: str, password: str, authlevel: str) -> bool:
return self.authmanager.create_account(username, password, authlevel)
@export(AUTH_LEVEL_ADMIN)
- def update_account(self, username, password, authlevel):
+ def update_account(self, username: str, password: str, authlevel: str) -> bool:
return self.authmanager.update_account(username, password, authlevel)
@export(AUTH_LEVEL_ADMIN)
- def remove_account(self, username):
+ def remove_account(self, username: str) -> bool:
return self.authmanager.remove_account(username)
diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py
index 3c2ac643b..0185dd8cb 100644
--- a/deluge/core/daemon.py
+++ b/deluge/core/daemon.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,8 +7,6 @@
#
"""The Deluge daemon"""
-from __future__ import unicode_literals
-
import logging
import os
import socket
@@ -44,8 +41,8 @@ def is_daemon_running(pid_file):
try:
with open(pid_file) as _file:
- pid, port = [int(x) for x in _file.readline().strip().split(';')]
- except (EnvironmentError, ValueError):
+ pid, port = (int(x) for x in _file.readline().strip().split(';'))
+ except (OSError, ValueError):
return False
if is_process_running(pid):
@@ -53,7 +50,7 @@ def is_daemon_running(pid_file):
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
_socket.connect(('127.0.0.1', port))
- except socket.error:
+ except OSError:
# Can't connect, so pid is not a deluged process.
return False
else:
@@ -62,7 +59,7 @@ def is_daemon_running(pid_file):
return True
-class Daemon(object):
+class Daemon:
"""The Deluge Daemon class"""
def __init__(
@@ -156,7 +153,7 @@ class Daemon(object):
pid = os.getpid()
log.debug('Storing pid %s & port %s in: %s', pid, self.port, self.pid_file)
with open(self.pid_file, 'w') as _file:
- _file.write('%s;%s\n' % (pid, self.port))
+ _file.write(f'{pid};{self.port}\n')
component.start()
diff --git a/deluge/core/daemon_entry.py b/deluge/core/daemon_entry.py
index 8b3746c43..c49fd2a35 100644
--- a/deluge/core/daemon_entry.py
+++ b/deluge/core/daemon_entry.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2010 Pedro Algarvio <pedro@algarvio.me>
@@ -7,8 +6,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
diff --git a/deluge/core/eventmanager.py b/deluge/core/eventmanager.py
index 5ba2989f9..d43847a77 100644
--- a/deluge/core/eventmanager.py
+++ b/deluge/core/eventmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
diff --git a/deluge/core/filtermanager.py b/deluge/core/filtermanager.py
index 90589ef1f..a60cc5bc4 100644
--- a/deluge/core/filtermanager.py
+++ b/deluge/core/filtermanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -7,12 +6,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
@@ -136,7 +131,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
diff --git a/deluge/core/pluginmanager.py b/deluge/core/pluginmanager.py
index 7d2f3a1e8..0482b16e7 100644
--- a/deluge/core/pluginmanager.py
+++ b/deluge/core/pluginmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -9,8 +8,6 @@
"""PluginManager for Core"""
-from __future__ import unicode_literals
-
import logging
from twisted.internet import defer
diff --git a/deluge/core/preferencesmanager.py b/deluge/core/preferencesmanager.py
index c250130c0..7e5c207a1 100644
--- a/deluge/core/preferencesmanager.py
+++ b/deluge/core/preferencesmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2010 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,13 +7,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
@@ -24,17 +23,14 @@ import deluge.configmanager
from deluge._libtorrent import lt
from deluge.event import ConfigValueChangedEvent
+GeoIP = None
try:
- import GeoIP
-except ImportError:
- GeoIP = None
-
-try:
- from urllib.parse import quote_plus
- from urllib.request import urlopen
+ from GeoIP import GeoIP
except ImportError:
- from urllib import quote_plus
- from urllib2 import urlopen
+ try:
+ from pygeoip import GeoIP
+ except ImportError:
+ pass
log = logging.getLogger(__name__)
@@ -202,7 +198,7 @@ class PreferencesManager(component.Component):
self.__set_listen_on()
def __set_listen_on(self):
- """ Set the ports and interface address to listen for incoming connections on."""
+ """Set the ports and interface address to listen for incoming connections on."""
if self.config['random_port']:
if not self.config['listen_random_port']:
self.config['listen_random_port'] = random.randrange(49152, 65525)
@@ -225,7 +221,7 @@ class PreferencesManager(component.Component):
self.config['listen_use_sys_port'],
)
interfaces = [
- '%s:%s' % (interface, port)
+ f'{interface}:{port}'
for port in range(listen_ports[0], listen_ports[1] + 1)
]
self.core.apply_session_settings(
@@ -400,7 +396,7 @@ class PreferencesManager(component.Component):
+ quote_plus(':'.join(self.config['enabled_plugins']))
)
urlopen(url)
- except IOError as ex:
+ except OSError as ex:
log.debug('Network error while trying to send info: %s', ex)
else:
self.config['info_sent'] = now
@@ -464,11 +460,9 @@ class PreferencesManager(component.Component):
# Load the GeoIP DB for country look-ups if available
if os.path.exists(geoipdb_path):
try:
- self.core.geoip_instance = GeoIP.open(
- geoipdb_path, GeoIP.GEOIP_STANDARD
- )
- except AttributeError:
- log.warning('GeoIP Unavailable')
+ self.core.geoip_instance = GeoIP(geoipdb_path, 0)
+ except Exception as ex:
+ log.warning('GeoIP Unavailable: %s', ex)
else:
log.warning('Unable to find GeoIP database file: %s', geoipdb_path)
diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py
index adb521901..81ab2e0a5 100644
--- a/deluge/core/rpcserver.py
+++ b/deluge/core/rpcserver.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008,2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,17 +7,14 @@
#
"""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 typing import Callable, TypeVar, overload
-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,
@@ -46,6 +42,18 @@ RPC_EVENT = 3
log = logging.getLogger(__name__)
+TCallable = TypeVar('TCallable', bound=Callable)
+
+
+@overload
+def export(func: TCallable) -> TCallable:
+ ...
+
+
+@overload
+def export(auth_level: int) -> Callable[[TCallable], TCallable]:
+ ...
+
def export(auth_level=AUTH_LEVEL_DEFAULT):
"""
@@ -69,7 +77,7 @@ def export(auth_level=AUTH_LEVEL_DEFAULT):
if func.__doc__:
if func.__doc__.endswith(' '):
indent = func.__doc__.split('\n')[-1]
- func.__doc__ += '\n{}'.format(indent)
+ func.__doc__ += f'\n{indent}'
else:
func.__doc__ += '\n\n'
func.__doc__ += rpc_text
@@ -114,7 +122,7 @@ def format_request(call):
class DelugeRPCProtocol(DelugeTransferProtocol):
def __init__(self):
- super(DelugeRPCProtocol, self).__init__()
+ super().__init__()
# namedtuple subclass with auth_level, username for the connected session.
self.AuthLevel = namedtuple('SessionAuthlevel', 'auth_level, username')
@@ -537,8 +545,8 @@ class RPCServer(component.Component):
:type event: :class:`deluge.event.DelugeEvent`
"""
log.debug('intevents: %s', self.factory.interested_events)
- # Find sessions interested in this event
- for session_id, interest in self.factory.interested_events.items():
+ # Use copy of `interested_events` since it can mutate while iterating.
+ for session_id, interest in self.factory.interested_events.copy().items():
if event.name in interest:
log.debug('Emit Event: %s %s', event.name, event.args)
# This session is interested so send a RPC_EVENT
@@ -588,59 +596,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)
diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py
index 9dc2d4163..57ec26f37 100644
--- a/deluge/core/torrent.py
+++ b/deluge/core/torrent.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -14,11 +13,12 @@ Attributes:
"""
-from __future__ import division, unicode_literals
-
import logging
import os
import socket
+import time
+from typing import Optional
+from urllib.parse import urlparse
from twisted.internet.defer import Deferred, DeferredList
@@ -34,18 +34,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 = {
@@ -94,7 +82,7 @@ def convert_lt_files(files):
"""Indexes and decodes files from libtorrent get_files().
Args:
- files (list): The libtorrent torrent files.
+ files (file_storage): The libtorrent torrent files.
Returns:
list of dict: The files.
@@ -109,18 +97,18 @@ def convert_lt_files(files):
}
"""
filelist = []
- for index, _file in enumerate(files):
+ for index in range(files.num_files()):
try:
- file_path = _file.path.decode('utf8')
+ file_path = files.file_path(index).decode('utf8')
except AttributeError:
- file_path = _file.path
+ file_path = files.file_path(index)
filelist.append(
{
'index': index,
'path': file_path.replace('\\', '/'),
- 'size': _file.size,
- 'offset': _file.offset,
+ 'size': files.file_size(index),
+ 'offset': files.file_offset(index),
}
)
@@ -161,7 +149,7 @@ class TorrentOptions(dict):
"""
def __init__(self):
- super(TorrentOptions, self).__init__()
+ super().__init__()
config = ConfigManager('core.conf').config
options_conf_map = {
'add_paused': 'add_paused',
@@ -191,14 +179,14 @@ class TorrentOptions(dict):
self['seed_mode'] = False
-class TorrentError(object):
+class TorrentError:
def __init__(self, error_message, was_paused=False, restart_to_resume=False):
self.error_message = error_message
self.was_paused = was_paused
self.restart_to_resume = restart_to_resume
-class Torrent(object):
+class Torrent:
"""Torrent holds information about torrents added to the libtorrent session.
Args:
@@ -248,9 +236,10 @@ class Torrent(object):
self.handle = handle
self.magnet = magnet
- self.status = self.handle.status()
+ self._status: Optional['lt.torrent_status'] = None
+ self._status_last_update: float = 0.0
- self.torrent_info = self.handle.get_torrent_info()
+ self.torrent_info = self.handle.torrent_file()
self.has_metadata = self.status.has_metadata
self.options = TorrentOptions()
@@ -281,7 +270,6 @@ class Torrent(object):
self.prev_status = {}
self.waiting_on_folder_rename = []
- self.update_status(self.handle.status())
self._create_status_funcs()
self.set_options(self.options)
self.update_state()
@@ -289,6 +277,18 @@ class Torrent(object):
if log.isEnabledFor(logging.DEBUG):
log.debug('Torrent object created.')
+ def _set_handle_flags(self, flag: lt.torrent_flags, set_flag: bool):
+ """set or unset a flag to the lt handle
+
+ Args:
+ flag (lt.torrent_flags): the flag to set/unset
+ set_flag (bool): True for setting the flag, False for unsetting it
+ """
+ if set_flag:
+ self.handle.set_flags(flag)
+ else:
+ self.handle.unset_flags(flag)
+
def on_metadata_received(self):
"""Process the metadata received alert for this torrent"""
self.has_metadata = True
@@ -373,7 +373,7 @@ class Torrent(object):
"""Sets maximum download speed for this torrent.
Args:
- m_up_speed (float): Maximum download speed in KiB/s.
+ m_down_speed (float): Maximum download speed in KiB/s.
"""
self.options['max_download_speed'] = m_down_speed
if m_down_speed < 0:
@@ -405,7 +405,7 @@ class Torrent(object):
return
# A list of priorities for each piece in the torrent
- priorities = self.handle.piece_priorities()
+ priorities = self.handle.get_piece_priorities()
def get_file_piece(idx, byte_offset):
return self.torrent_info.map_file(idx, byte_offset, 0).piece
@@ -431,14 +431,17 @@ 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._set_handle_flags(
+ flag=lt.torrent_flags.sequential_download,
+ set_flag=sequential,
+ )
def set_auto_managed(self, auto_managed):
"""Set auto managed mode, i.e. will be started or queued automatically.
@@ -448,7 +451,10 @@ class Torrent(object):
"""
self.options['auto_managed'] = auto_managed
if not (self.status.paused and not self.status.auto_managed):
- self.handle.auto_managed(auto_managed)
+ self._set_handle_flags(
+ flag=lt.torrent_flags.auto_managed,
+ set_flag=auto_managed,
+ )
self.update_state()
def set_super_seeding(self, super_seeding):
@@ -458,7 +464,10 @@ class Torrent(object):
super_seeding (bool): Enable super seeding.
"""
self.options['super_seeding'] = super_seeding
- self.handle.super_seeding(super_seeding)
+ self._set_handle_flags(
+ flag=lt.torrent_flags.super_seeding,
+ set_flag=super_seeding,
+ )
def set_stop_ratio(self, stop_ratio):
"""The seeding ratio to stop (or remove) the torrent at.
@@ -519,7 +528,7 @@ class Torrent(object):
self.handle.prioritize_files(file_priorities)
else:
log.debug('Unable to set new file priorities.')
- file_priorities = self.handle.file_priorities()
+ file_priorities = self.handle.get_file_priorities()
if 0 in self.options['file_priorities']:
# Previously marked a file 'skip' so check for any 0's now >0.
@@ -569,7 +578,7 @@ class Torrent(object):
trackers (list of dicts): A list of trackers.
"""
if trackers is None:
- self.trackers = [tracker for tracker in self.handle.trackers()]
+ self.trackers = list(self.handle.trackers())
self.tracker_host = None
return
@@ -634,7 +643,7 @@ class Torrent(object):
def update_state(self):
"""Updates the state, based on libtorrent's torrent state"""
- status = self.handle.status()
+ status = self.get_lt_status()
session_paused = component.get('Core').session.is_paused()
old_state = self.state
self.set_status_message()
@@ -646,7 +655,10 @@ class Torrent(object):
elif status_error:
self.state = 'Error'
# auto-manage status will be reverted upon resuming.
- self.handle.auto_managed(False)
+ self._set_handle_flags(
+ flag=lt.torrent_flags.auto_managed,
+ set_flag=False,
+ )
self.set_status_message(decode_bytes(status_error))
elif status.moving_storage:
self.state = 'Moving'
@@ -699,8 +711,11 @@ class Torrent(object):
restart_to_resume (bool, optional): Prevent resuming clearing the error, only restarting
session can resume.
"""
- status = self.handle.status()
- self.handle.auto_managed(False)
+ status = self.get_lt_status()
+ self._set_handle_flags(
+ flag=lt.torrent_flags.auto_managed,
+ set_flag=False,
+ )
self.forced_error = TorrentError(message, status.paused, restart_to_resume)
if not status.paused:
self.handle.pause()
@@ -714,7 +729,10 @@ class Torrent(object):
log.error('Restart deluge to clear this torrent error')
if not self.forced_error.was_paused and self.options['auto_managed']:
- self.handle.auto_managed(True)
+ self._set_handle_flags(
+ flag=lt.torrent_flags.auto_managed,
+ set_flag=True,
+ )
self.forced_error = None
self.set_status_message('OK')
if update_state:
@@ -838,7 +856,7 @@ class Torrent(object):
'client': client,
'country': country,
'down_speed': peer.payload_down_speed,
- 'ip': '%s:%s' % (peer.ip[0], peer.ip[1]),
+ 'ip': f'{peer.ip[0]}:{peer.ip[1]}',
'progress': peer.progress,
'seed': peer.flags & peer.seed,
'up_speed': peer.payload_up_speed,
@@ -857,7 +875,7 @@ class Torrent(object):
def get_file_priorities(self):
"""Return the file priorities"""
- if not self.handle.has_metadata():
+ if not self.handle.status().has_metadata:
return []
if not self.options['file_priorities']:
@@ -874,11 +892,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):
@@ -903,7 +928,7 @@ class Torrent(object):
# Check if hostname is an IP address and just return it if that's the case
try:
socket.inet_aton(host)
- except socket.error:
+ except OSError:
pass
else:
# This is an IP address because an exception wasn't raised
@@ -939,10 +964,10 @@ class Torrent(object):
if self.has_metadata:
# Use the top-level folder as torrent name.
- filename = decode_bytes(self.torrent_info.file_at(0).path)
+ filename = decode_bytes(self.torrent_info.files().file_path(0))
name = filename.replace('\\', '/', 1).split('/', 1)[0]
else:
- name = decode_bytes(self.handle.name())
+ name = decode_bytes(self.handle.status().name)
if not name:
name = self.torrent_id
@@ -1001,7 +1026,7 @@ class Torrent(object):
dict: a dictionary of the status keys and their values
"""
if update:
- self.update_status(self.handle.status())
+ self.get_lt_status()
if all_keys:
keys = list(self.status_funcs)
@@ -1031,13 +1056,35 @@ class Torrent(object):
return status_dict
- def update_status(self, status):
+ def get_lt_status(self) -> 'lt.torrent_status':
+ """Get the torrent status fresh, not from cache.
+
+ This should be used when a guaranteed fresh status is needed rather than
+ `torrent.handle.status()` because it will update the cache as well.
+ """
+ self.status = self.handle.status()
+ return self.status
+
+ @property
+ def status(self) -> 'lt.torrent_status':
+ """Cached copy of the libtorrent status for this torrent.
+
+ If it has not been updated within the last five seconds, it will be
+ automatically refreshed.
+ """
+ if self._status_last_update < (time.time() - 5):
+ self.status = self.handle.status()
+ return self._status
+
+ @status.setter
+ def status(self, status: 'lt.torrent_status') -> None:
"""Updates the cached status.
Args:
- status (libtorrent.torrent_status): a libtorrent torrent status
+ status: a libtorrent torrent status
"""
- self.status = status
+ self._status = status
+ self._status_last_update = time.time()
def _create_status_funcs(self):
"""Creates the functions for getting torrent status"""
@@ -1159,7 +1206,10 @@ class Torrent(object):
"""
# Turn off auto-management so the torrent will not be unpaused by lt queueing
- self.handle.auto_managed(False)
+ self._set_handle_flags(
+ flag=lt.torrent_flags.auto_managed,
+ set_flag=False,
+ )
if self.state == 'Error':
log.debug('Unable to pause torrent while in Error state')
elif self.status.paused:
@@ -1194,7 +1244,10 @@ class Torrent(object):
else:
# Check if torrent was originally being auto-managed.
if self.options['auto_managed']:
- self.handle.auto_managed(True)
+ self._set_handle_flags(
+ flag=lt.torrent_flags.auto_managed,
+ set_flag=True,
+ )
try:
self.handle.resume()
except RuntimeError as ex:
@@ -1298,7 +1351,7 @@ class Torrent(object):
try:
with open(filepath, 'wb') as save_file:
save_file.write(filedump)
- except IOError as ex:
+ except OSError as ex:
log.error('Unable to save torrent file to: %s', ex)
filepath = os.path.join(get_config_dir(), 'state', self.torrent_id + '.torrent')
@@ -1393,7 +1446,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:
diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py
index 174a92dae..c43a7a262 100644
--- a/deluge/core/torrentmanager.py
+++ b/deluge/core/torrentmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,25 +7,23 @@
#
"""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 base64 import b64encode
from tempfile import gettempdir
+from typing import Dict, List, NamedTuple, Tuple
-import six.moves.cPickle as pickle # noqa: N813
-from twisted.internet import defer, error, reactor, threads
+from twisted.internet import defer, 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_VERSION, lt
from deluge.common import (
- PY2,
VersionSplit,
archive_files,
decode_bytes,
@@ -36,6 +33,7 @@ from deluge.common import (
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
+from deluge.decorators import maybe_coroutine
from deluge.error import AddTorrentError, InvalidTorrentError
from deluge.event import (
ExternalIPEvent,
@@ -52,13 +50,18 @@ from deluge.event import (
log = logging.getLogger(__name__)
LT_DEFAULT_ADD_TORRENT_FLAGS = (
- lt.add_torrent_params_flags_t.flag_paused
- | lt.add_torrent_params_flags_t.flag_auto_managed
- | lt.add_torrent_params_flags_t.flag_update_subscribe
- | lt.add_torrent_params_flags_t.flag_apply_ip_filter
+ lt.torrent_flags.paused
+ | lt.torrent_flags.auto_managed
+ | lt.torrent_flags.update_subscribe
+ | lt.torrent_flags.apply_ip_filter
)
+class PrefetchQueueItem(NamedTuple):
+ alert_deferred: Deferred
+ result_queue: List[Deferred]
+
+
class TorrentState: # pylint: disable=old-style-class
"""Create a torrent state.
@@ -96,7 +99,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
@@ -136,7 +139,8 @@ class TorrentManager(component.Component):
"""
- callLater = reactor.callLater # noqa: N815
+ # This is used in the test to mock out timeouts
+ clock = reactor
def __init__(self):
component.Component.__init__(
@@ -165,7 +169,7 @@ class TorrentManager(component.Component):
self.is_saving_state = False
self.save_resume_data_file_lock = defer.DeferredLock()
self.torrents_loading = {}
- self.prefetching_metadata = {}
+ self.prefetching_metadata: Dict[str, PrefetchQueueItem] = {}
# This is a map of torrent_ids to Deferreds used to track needed resume data.
# The Deferreds will be completed when resume data has been saved.
@@ -198,34 +202,32 @@ class TorrentManager(component.Component):
# Register alert functions
alert_handles = [
- 'external_ip_alert',
- 'performance_alert',
- 'add_torrent_alert',
- 'metadata_received_alert',
- 'torrent_finished_alert',
- 'torrent_paused_alert',
- 'torrent_checked_alert',
- 'torrent_resumed_alert',
- 'tracker_reply_alert',
- 'tracker_announce_alert',
- 'tracker_warning_alert',
- 'tracker_error_alert',
- 'file_renamed_alert',
- 'file_error_alert',
- 'file_completed_alert',
- 'storage_moved_alert',
- 'storage_moved_failed_alert',
- 'state_update_alert',
- 'state_changed_alert',
- 'save_resume_data_alert',
- 'save_resume_data_failed_alert',
- 'fastresume_rejected_alert',
+ 'external_ip',
+ 'performance',
+ 'add_torrent',
+ 'metadata_received',
+ 'torrent_finished',
+ 'torrent_paused',
+ 'torrent_checked',
+ 'torrent_resumed',
+ 'tracker_reply',
+ 'tracker_announce',
+ 'tracker_warning',
+ 'tracker_error',
+ 'file_renamed',
+ 'file_error',
+ 'file_completed',
+ 'storage_moved',
+ 'storage_moved_failed',
+ 'state_update',
+ 'state_changed',
+ 'save_resume_data',
+ 'save_resume_data_failed',
+ 'fastresume_rejected',
]
for alert_handle in alert_handles:
- on_alert_func = getattr(
- self, ''.join(['on_alert_', alert_handle.replace('_alert', '')])
- )
+ on_alert_func = getattr(self, ''.join(['on_alert_', alert_handle]))
self.alerts.register_handler(alert_handle, on_alert_func)
# Define timers
@@ -250,8 +252,8 @@ class TorrentManager(component.Component):
self.save_resume_data_timer.start(190, False)
self.prev_status_cleanup_loop.start(10)
- @defer.inlineCallbacks
- def stop(self):
+ @maybe_coroutine
+ async def stop(self):
# Stop timers
if self.save_state_timer.running:
self.save_state_timer.stop()
@@ -263,11 +265,11 @@ class TorrentManager(component.Component):
self.prev_status_cleanup_loop.stop()
# Save state on shutdown
- yield self.save_state()
+ await self.save_state()
self.session.pause()
- result = yield self.save_resume_data(flush_disk_cache=True)
+ result = await self.save_resume_data(flush_disk_cache=True)
# Remove the temp_file to signify successfully saved state
if result and os.path.isfile(self.temp_file):
os.remove(self.temp_file)
@@ -281,11 +283,6 @@ class TorrentManager(component.Component):
'Paused',
'Queued',
):
- # If the global setting is set, but the per-torrent isn't...
- # Just skip to the next torrent.
- # This is so that a user can turn-off the stop at ratio option on a per-torrent basis
- if not torrent.options['stop_at_ratio']:
- continue
if (
torrent.get_ratio() >= torrent.options['stop_ratio']
and torrent.is_finished
@@ -293,8 +290,8 @@ class TorrentManager(component.Component):
if torrent.options['remove_at_ratio']:
self.remove(torrent_id)
break
- if not torrent.handle.status().paused:
- torrent.pause()
+
+ torrent.pause()
def __getitem__(self, torrent_id):
"""Return the Torrent with torrent_id.
@@ -346,66 +343,64 @@ class TorrentManager(component.Component):
else:
return torrent_info
- def prefetch_metadata(self, magnet, timeout):
+ @maybe_coroutine
+ async def prefetch_metadata(self, magnet: str, timeout: int) -> Tuple[str, bytes]:
"""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 canceling.
+ magnet: A magnet URI to download the metadata for.
+ timeout: Number of seconds to wait before canceling.
Returns:
- Deferred: A tuple of (torrent_id (str), metadata (dict))
+ A tuple of (torrent_id, metadata)
"""
torrent_id = get_magnet_info(magnet)['info_hash']
if torrent_id in self.prefetching_metadata:
- return self.prefetching_metadata[torrent_id].defer
+ d = Deferred()
+ self.prefetching_metadata[torrent_id].result_queue.append(d)
+ return await d
- add_torrent_params = {}
- add_torrent_params['save_path'] = gettempdir()
- add_torrent_params['url'] = magnet.strip().encode('utf8')
- add_torrent_params['flags'] = (
+ add_torrent_params = lt.parse_magnet_uri(magnet)
+ add_torrent_params.save_path = gettempdir()
+ add_torrent_params.flags = (
(
LT_DEFAULT_ADD_TORRENT_FLAGS
- | lt.add_torrent_params_flags_t.flag_duplicate_is_error
- | lt.add_torrent_params_flags_t.flag_upload_mode
+ | lt.torrent_flags.duplicate_is_error
+ | lt.torrent_flags.upload_mode
)
- ^ lt.add_torrent_params_flags_t.flag_auto_managed
- ^ lt.add_torrent_params_flags_t.flag_paused
+ ^ lt.torrent_flags.auto_managed
+ ^ lt.torrent_flags.paused
)
torrent_handle = self.session.add_torrent(add_torrent_params)
d = Deferred()
# Cancel the defer if timeout reached.
- defer_timeout = self.callLater(timeout, d.cancel)
- d.addBoth(self.on_prefetch_metadata, torrent_id, defer_timeout)
- Prefetch = namedtuple('Prefetch', 'defer handle')
- self.prefetching_metadata[torrent_id] = Prefetch(defer=d, handle=torrent_handle)
- return d
+ d.addTimeout(timeout, self.clock)
+ self.prefetching_metadata[torrent_id] = PrefetchQueueItem(d, [])
- def on_prefetch_metadata(self, torrent_info, torrent_id, defer_timeout):
- # Cancel reactor.callLater.
try:
- defer_timeout.cancel()
- except error.AlreadyCalled:
- pass
-
- log.debug('remove prefetch magnet from session')
- try:
- torrent_handle = self.prefetching_metadata.pop(torrent_id).handle
- except KeyError:
- pass
+ torrent_info = await d
+ except (defer.TimeoutError, defer.CancelledError):
+ log.debug(f'Prefetching metadata for {torrent_id} timed out or cancelled.')
+ metadata = b''
else:
- self.session.remove_torrent(torrent_handle, 1)
-
- metadata = None
- if isinstance(torrent_info, lt.torrent_info):
log.debug('prefetch metadata received')
- metadata = lt.bdecode(torrent_info.metadata())
+ if VersionSplit(LT_VERSION) < VersionSplit('2.0.0.0'):
+ metadata = torrent_info.metadata()
+ else:
+ metadata = torrent_info.info_section()
+
+ log.debug('remove prefetch magnet from session')
+ result_queue = self.prefetching_metadata.pop(torrent_id).result_queue
+ self.session.remove_torrent(torrent_handle, 1)
+ result = torrent_id, b64encode(metadata)
- return torrent_id, metadata
+ for d in result_queue:
+ d.callback(result)
+ return result
def _build_torrent_options(self, options):
"""Load default options and update if needed."""
@@ -438,14 +433,10 @@ class TorrentManager(component.Component):
elif magnet:
magnet_info = get_magnet_info(magnet)
if magnet_info:
- add_torrent_params['url'] = magnet.strip().encode('utf8')
add_torrent_params['name'] = magnet_info['name']
+ add_torrent_params['trackers'] = list(magnet_info['trackers'])
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)
- )
+ add_torrent_params['info_hash'] = bytes(bytearray.fromhex(torrent_id))
else:
raise AddTorrentError(
'Unable to add magnet, invalid magnet info: %s' % magnet
@@ -460,7 +451,7 @@ class TorrentManager(component.Component):
raise AddTorrentError('Torrent already being added (%s).' % torrent_id)
elif torrent_id in self.prefetching_metadata:
# Cancel and remove metadata fetching torrent.
- self.prefetching_metadata[torrent_id].defer.cancel()
+ self.prefetching_metadata[torrent_id].alert_deferred.cancel()
# Check for renamed files and if so, rename them in the torrent_info before adding.
if options['mapped_files'] and torrent_info:
@@ -488,16 +479,12 @@ class TorrentManager(component.Component):
# Set flags: enable duplicate_is_error & override_resume_data, disable auto_managed.
add_torrent_params['flags'] = (
- LT_DEFAULT_ADD_TORRENT_FLAGS
- | lt.add_torrent_params_flags_t.flag_duplicate_is_error
- | lt.add_torrent_params_flags_t.flag_override_resume_data
- ) ^ lt.add_torrent_params_flags_t.flag_auto_managed
+ LT_DEFAULT_ADD_TORRENT_FLAGS | lt.torrent_flags.duplicate_is_error
+ ) ^ lt.torrent_flags.auto_managed
if options['seed_mode']:
- add_torrent_params['flags'] |= lt.add_torrent_params_flags_t.flag_seed_mode
+ add_torrent_params['flags'] |= lt.torrent_flags.seed_mode
if options['super_seeding']:
- add_torrent_params[
- 'flags'
- ] |= lt.add_torrent_params_flags_t.flag_super_seeding
+ add_torrent_params['flags'] |= lt.torrent_flags.super_seeding
return torrent_id, add_torrent_params
@@ -654,7 +641,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
@@ -821,12 +808,9 @@ class TorrentManager(component.Component):
try:
with open(filepath, 'rb') as _file:
- if PY2:
- state = pickle.load(_file)
- else:
- state = pickle.load(_file, encoding='utf8')
- except (IOError, EOFError, pickle.UnpicklingError) as ex:
- message = 'Unable to load {}: {}'.format(filepath, ex)
+ state = pickle.load(_file, encoding='utf8')
+ except (OSError, EOFError, pickle.UnpicklingError) as ex:
+ message = f'Unable to load {filepath}: {ex}'
log.error(message)
if not filepath.endswith('.bak'):
self.archive_state(message)
@@ -1037,7 +1021,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 = []
@@ -1082,7 +1066,7 @@ class TorrentManager(component.Component):
try:
with open(_filepath, 'rb') as _file:
resume_data = lt.bdecode(_file.read())
- except (IOError, EOFError, RuntimeError) as ex:
+ except (OSError, EOFError, RuntimeError) as ex:
if self.torrents:
log.warning('Unable to load %s: %s', _filepath, ex)
resume_data = None
@@ -1366,10 +1350,8 @@ class TorrentManager(component.Component):
torrent.set_tracker_status('Announce OK')
# Check for peer information from the tracker, if none then send a scrape request.
- if (
- alert.handle.status().num_complete == -1
- or alert.handle.status().num_incomplete == -1
- ):
+ torrent.get_lt_status()
+ if torrent.status.num_complete == -1 or torrent.status.num_incomplete == -1:
torrent.scrape_tracker()
def on_alert_tracker_announce(self, alert):
@@ -1404,22 +1386,18 @@ class TorrentManager(component.Component):
log.debug(
'Tracker Error Alert: %s [%s]', decode_bytes(alert.message()), 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)
+ # 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
def on_alert_storage_moved(self, alert):
"""Alert handler for libtorrent storage_moved_alert"""
@@ -1493,7 +1471,9 @@ class TorrentManager(component.Component):
return
if torrent_id in self.torrents:
# libtorrent add_torrent expects bencoded resume_data.
- self.resume_data[torrent_id] = lt.bencode(alert.resume_data)
+ self.resume_data[torrent_id] = lt.bencode(
+ lt.write_resume_data(alert.params)
+ )
if torrent_id in self.waiting_on_resume_data:
self.waiting_on_resume_data[torrent_id].callback(None)
@@ -1575,7 +1555,7 @@ class TorrentManager(component.Component):
# Try callback to prefetch_metadata method.
try:
- d = self.prefetching_metadata[torrent_id].defer
+ d = self.prefetching_metadata[torrent_id].alert_deferred
except KeyError:
pass
else:
@@ -1621,23 +1601,14 @@ class TorrentManager(component.Component):
except RuntimeError:
continue
if torrent_id in self.torrents:
- self.torrents[torrent_id].update_status(t_status)
+ self.torrents[torrent_id].status = t_status
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"""
diff --git a/deluge/crypto_utils.py b/deluge/crypto_utils.py
index 7672efa71..d636c05e7 100644
--- a/deluge/crypto_utils.py
+++ b/deluge/crypto_utils.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007,2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,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 +19,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 +80,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)
diff --git a/deluge/decorators.py b/deluge/decorators.py
index b101712d4..92e3ecf59 100644
--- a/deluge/decorators.py
+++ b/deluge/decorators.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -7,12 +6,13 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import inspect
import re
import warnings
from functools import wraps
+from typing import Any, Callable, Coroutine, TypeVar
+
+from twisted.internet import defer
def proxy(proxy_func):
@@ -56,7 +56,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 +107,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
@@ -127,7 +127,7 @@ def _overrides(stack, method, explicit_base_classes=None):
% (
method.__name__,
cls,
- 'File: %s:%s' % (stack[1][1], stack[1][2]),
+ f'File: {stack[1][1]}:{stack[1][2]}',
)
)
@@ -137,7 +137,7 @@ def _overrides(stack, method, explicit_base_classes=None):
% (
method.__name__,
check_classes,
- 'File: %s:%s' % (stack[1][1], stack[1][2]),
+ f'File: {stack[1][1]}:{stack[1][2]}',
)
)
return method
@@ -146,7 +146,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.
"""
@@ -154,7 +154,7 @@ def deprecated(func):
def depr_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # Turn off filter
warnings.warn(
- 'Call to deprecated function {}.'.format(func.__name__),
+ f'Call to deprecated function {func.__name__}.',
category=DeprecationWarning,
stacklevel=2,
)
@@ -162,3 +162,58 @@ def deprecated(func):
return func(*args, **kwargs)
return depr_func
+
+
+class CoroutineDeferred(defer.Deferred):
+ """Wraps a coroutine in a Deferred.
+ It will dynamically pass through the underlying coroutine without wrapping where apporpriate.
+ """
+
+ def __init__(self, coro: Coroutine):
+ # Delay this import to make sure a reactor was installed first
+ from twisted.internet import reactor
+
+ super().__init__()
+ self.coro = coro
+ self.awaited = None
+ self.activate_deferred = reactor.callLater(0, self.activate)
+
+ def __await__(self):
+ if self.awaited in [None, True]:
+ self.awaited = True
+ return self.coro.__await__()
+ # Already in deferred mode
+ return super().__await__()
+
+ def activate(self):
+ """If the result wasn't awaited before the next context switch, we turn it into a deferred."""
+ if self.awaited is None:
+ self.awaited = False
+ try:
+ d = defer.Deferred.fromCoroutine(self.coro)
+ except AttributeError:
+ # Fallback for Twisted <= 21.2 without fromCoroutine
+ d = defer.ensureDeferred(self.coro)
+ d.chainDeferred(self)
+
+ def addCallbacks(self, *args, **kwargs): # noqa: N802
+ assert not self.awaited, 'Cannot add callbacks to an already awaited coroutine.'
+ self.activate()
+ return super().addCallbacks(*args, **kwargs)
+
+
+_RetT = TypeVar('_RetT')
+
+
+def maybe_coroutine(
+ f: Callable[..., Coroutine[Any, Any, _RetT]]
+) -> 'Callable[..., defer.Deferred[_RetT]]':
+ """Wraps a coroutine function to make it usable as a normal function that returns a Deferred."""
+
+ @wraps(f)
+ def wrapper(*args, **kwargs):
+ # Uncomment for quick testing to make sure CoroutineDeferred magic isn't at fault
+ # return defer.ensureDeferred(f(*args, **kwargs))
+ return CoroutineDeferred(f(*args, **kwargs))
+
+ return wrapper
diff --git a/deluge/error.py b/deluge/error.py
index 46e8e0cf1..d542dc2c0 100644
--- a/deluge/error.py
+++ b/deluge/error.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -9,18 +8,15 @@
#
-from __future__ import unicode_literals
-
-
class DelugeError(Exception):
def __new__(cls, *args, **kwargs):
- inst = super(DelugeError, cls).__new__(cls, *args, **kwargs)
+ inst = super().__new__(cls, *args, **kwargs)
inst._args = args
inst._kwargs = kwargs
return inst
def __init__(self, message=None):
- super(DelugeError, self).__init__(message)
+ super().__init__(message)
self.message = message
def __str__(self):
@@ -45,12 +41,12 @@ class InvalidPathError(DelugeError):
class WrappedException(DelugeError):
def __init__(self, message, exception_type, traceback):
- super(WrappedException, self).__init__(message)
+ super().__init__(message)
self.type = exception_type
self.traceback = traceback
def __str__(self):
- return '%s\n%s' % (self.message, self.traceback)
+ return f'{self.message}\n{self.traceback}'
class _ClientSideRecreateError(DelugeError):
@@ -64,7 +60,7 @@ class IncompatibleClient(_ClientSideRecreateError):
'Your deluge client is not compatible with the daemon. '
'Please upgrade your client to %(daemon_version)s'
) % {'daemon_version': self.daemon_version}
- super(IncompatibleClient, self).__init__(message=msg)
+ super().__init__(message=msg)
class NotAuthorizedError(_ClientSideRecreateError):
@@ -73,14 +69,14 @@ class NotAuthorizedError(_ClientSideRecreateError):
'current_level': current_level,
'required_level': required_level,
}
- super(NotAuthorizedError, self).__init__(message=msg)
+ super().__init__(message=msg)
self.current_level = current_level
self.required_level = required_level
class _UsernameBasedPasstroughError(_ClientSideRecreateError):
def __init__(self, message, username):
- super(_UsernameBasedPasstroughError, self).__init__(message)
+ super().__init__(message)
self.username = username
diff --git a/deluge/event.py b/deluge/event.py
index c5d5ff910..38fc32ff8 100644
--- a/deluge/event.py
+++ b/deluge/event.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -14,10 +13,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 = {}
@@ -27,12 +22,12 @@ class DelugeEventMetaClass(type):
"""
def __init__(cls, name, bases, dct): # pylint: disable=bad-mcs-method-argument
- super(DelugeEventMetaClass, cls).__init__(name, bases, dct)
+ super().__init__(name, bases, dct)
if name != 'DelugeEvent':
known_events[name] = cls
-class DelugeEvent(six.with_metaclass(DelugeEventMetaClass, object)):
+class DelugeEvent(metaclass=DelugeEventMetaClass):
"""
The base class for all events.
diff --git a/deluge/httpdownloader.py b/deluge/httpdownloader.py
index f0fe09ec0..700ade06b 100644
--- a/deluge/httpdownloader.py
+++ b/deluge/httpdownloader.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import cgi
import logging
import os.path
@@ -19,7 +16,7 @@ from twisted.internet.defer import Deferred
from twisted.python.failure import Failure
from twisted.web import client, http
from twisted.web._newclient import HTTPClientParser
-from twisted.web.error import PageRedirect
+from twisted.web.error import Error, PageRedirect
from twisted.web.http_headers import Headers
from twisted.web.iweb import IAgent
from zope.interface import implementer
@@ -40,11 +37,11 @@ class CompressionDecoderProtocol(client._GzipProtocol):
"""A compression decoder protocol for CompressionDecoder."""
def __init__(self, protocol, response):
- super(CompressionDecoderProtocol, self).__init__(protocol, response)
+ super().__init__(protocol, response)
self._zlibDecompress = zlib.decompressobj(32 + zlib.MAX_WBITS)
-class BodyHandler(HTTPClientParser, object):
+class BodyHandler(HTTPClientParser):
"""An HTTP parser that saves the response to a file."""
def __init__(self, request, finished, length, agent, encoding=None):
@@ -56,7 +53,7 @@ class BodyHandler(HTTPClientParser, object):
length (int): The length of the response.
agent (t.w.i.IAgent): The agent from which the request was sent.
"""
- super(BodyHandler, self).__init__(request, finished)
+ super().__init__(request, finished)
self.agent = agent
self.finished = finished
self.total_length = length
@@ -76,12 +73,12 @@ class BodyHandler(HTTPClientParser, object):
with open(self.agent.filename, 'wb') as _file:
_file.write(self.data)
self.finished.callback(self.agent.filename)
- self.state = u'DONE'
+ self.state = 'DONE'
HTTPClientParser.connectionLost(self, reason)
@implementer(IAgent)
-class HTTPDownloaderAgent(object):
+class HTTPDownloaderAgent:
"""A File Downloader Agent."""
def __init__(
@@ -125,6 +122,9 @@ class HTTPDownloaderAgent(object):
location = response.headers.getRawHeaders(b'location')[0]
error = PageRedirect(response.code, location=location)
finished.errback(Failure(error))
+ elif response.code >= 400:
+ error = Error(response.code)
+ finished.errback(Failure(error))
else:
headers = response.headers
body_length = int(headers.getRawHeaders(b'content-length', default=[0])[0])
@@ -146,7 +146,7 @@ class HTTPDownloaderAgent(object):
fileext = os.path.splitext(new_file_name)[1]
while os.path.isfile(new_file_name):
# Increment filename if already exists
- new_file_name = '%s-%s%s' % (fileroot, count, fileext)
+ new_file_name = f'{fileroot}-{count}{fileext}'
count += 1
self.filename = new_file_name
diff --git a/deluge/i18n/af.po b/deluge/i18n/af.po
index d9a5a3009..ad60b4946 100644
--- a/deluge/i18n/af.po
+++ b/deluge/i18n/af.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ar.po b/deluge/i18n/ar.po
index dfb0c3c53..bfc057c4d 100644
--- a/deluge/i18n/ar.po
+++ b/deluge/i18n/ar.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4440,16 +4440,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4458,7 +4458,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ast.po b/deluge/i18n/ast.po
index 1713b0a08..3e2e61fa0 100644
--- a/deluge/i18n/ast.po
+++ b/deluge/i18n/ast.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4434,16 +4434,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4452,7 +4452,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/be.po b/deluge/i18n/be.po
index 8ef0ed85f..cf4ed2586 100644
--- a/deluge/i18n/be.po
+++ b/deluge/i18n/be.po
@@ -8,50 +8,50 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-06-06 10:57+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2023-08-01 15:28+0000\n"
+"Last-Translator: Anton Hryb <Unknown>\n"
"Language-Team: Belarusian <be@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
-msgstr ""
+msgstr "Б"
#: deluge/common.py:412
msgid "KiB"
-msgstr ""
+msgstr "КіБ"
#: deluge/common.py:413
msgid "MiB"
-msgstr ""
+msgstr "МіБ"
#: deluge/common.py:414
msgid "GiB"
-msgstr ""
+msgstr "ГіБ"
#: deluge/common.py:415
msgid "TiB"
-msgstr ""
+msgstr "ТіБ"
#: deluge/common.py:416
msgid "K"
-msgstr ""
+msgstr "К"
#: deluge/common.py:417
msgid "M"
-msgstr ""
+msgstr "М"
#: deluge/common.py:418
msgid "G"
-msgstr ""
+msgstr "Г"
#: deluge/common.py:419
msgid "T"
-msgstr ""
+msgstr "Т"
#: deluge/common.py:515 deluge/ui/gtk3/statusbar.py:442
#: deluge/ui/gtk3/statusbar.py:455 deluge/ui/gtk3/statusbar.py:464
@@ -62,7 +62,7 @@ msgstr ""
#: deluge/ui/gtk3/systemtray.py:274 deluge/ui/gtk3/systemtray.py:442
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:40
msgid "K/s"
-msgstr ""
+msgstr "К/с"
#: deluge/common.py:515 deluge/ui/gtk3/menubar.py:449
#: deluge/ui/gtk3/menubar.py:455
@@ -76,55 +76,55 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:94
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:121
msgid "KiB/s"
-msgstr "КБ/с"
+msgstr "КіБ/с"
#: deluge/common.py:521
msgid "M/s"
-msgstr ""
+msgstr "М/с"
#: deluge/common.py:521
msgid "MiB/s"
-msgstr ""
+msgstr "МіБ/с"
#: deluge/common.py:527
msgid "G/s"
-msgstr ""
+msgstr "Г/с"
#: deluge/common.py:527
msgid "GiB/s"
-msgstr ""
+msgstr "ГіБ/с"
#: deluge/common.py:533
msgid "T/s"
-msgstr ""
+msgstr "Т/с"
#: deluge/common.py:533
msgid "TiB/s"
-msgstr ""
+msgstr "ТіБ/с"
#: deluge/argparserbase.py:172
msgid "Common Options"
-msgstr ""
+msgstr "Агульныя параметры"
#: deluge/argparserbase.py:175
msgid "Print this help message"
-msgstr ""
+msgstr "Друк дапаможнага паведамлення"
#: deluge/argparserbase.py:182
msgid "Print version information"
-msgstr ""
+msgstr "Друк інфармацыі пра версію"
#: deluge/argparserbase.py:194
msgid "Set the config directory path"
-msgstr ""
+msgstr "Наладзіць шлях да папкі з канфігурацыяй"
#: deluge/argparserbase.py:200
msgid "Output to specified logfile instead of stdout"
-msgstr ""
+msgstr "Вывад у вызначаны файл логаў замест stdout"
#: deluge/argparserbase.py:206
msgid "Set the log level (none, error, warning, info, debug)"
-msgstr ""
+msgstr "Узровень лога (none, error, warning, info, debug)"
#: deluge/argparserbase.py:215
#, python-format
@@ -132,10 +132,12 @@ msgid ""
"Enable logfile rotation, with optional maximum logfile size, default: "
"%(const)s (Logfile rotation count is 5)"
msgstr ""
+"Уключыць змену файла лога, з максімумам памера файла, па змаўчанні: "
+"%(const)s (Logfile rotation count is 5)"
#: deluge/argparserbase.py:223
msgid "Quieten logging output (Same as `--loglevel none`)"
-msgstr ""
+msgstr "Сцішаны вывад логаў (Тое ж што `--loglevel none`)"
#: deluge/argparserbase.py:231
#, python-format
@@ -143,91 +145,93 @@ msgid ""
"Profile %(prog)s with cProfile. Outputs to stdout unless a filename is "
"specified"
msgstr ""
+"Профіль %(prog)s з cProfile. Вывад у stdout пакуль не вызначана імя файла"
#: deluge/argparserbase.py:351
msgid "Process Control Options"
-msgstr ""
+msgstr "Опцыі кантролю працэса"
#: deluge/argparserbase.py:357
msgid "Pidfile to store the process id"
-msgstr ""
+msgstr "Файл для захавання id працэса"
#: deluge/argparserbase.py:365
msgid "Do not daemonize (fork) this process"
-msgstr ""
+msgstr "Не дэманізаваць (разгаліноўваць) гэты працэс"
#: deluge/argparserbase.py:379
msgid "Change to this user on startup (Requires root)"
-msgstr ""
+msgstr "Змяніць гэтага карыстальніка пры запуску (патрэбны правы root)"
#: deluge/argparserbase.py:386
msgid "Change to this group on startup (Requires root)"
-msgstr ""
+msgstr "Змяніць на гэту групу пры запуску (патрэбны правы root)"
#: deluge/core/daemon_entry.py:25
msgid "Daemon Options"
-msgstr ""
+msgstr "Опцыі дэмана"
#: deluge/core/daemon_entry.py:31
msgid "IP address to listen for UI connections"
-msgstr ""
+msgstr "IP адрасы каб слухаць падлучэнні"
#: deluge/core/daemon_entry.py:39
msgid "Port to listen for UI connections on"
-msgstr ""
+msgstr "Які порт слухаць пры падлучэнні"
#: deluge/core/daemon_entry.py:47
msgid "IP address to listen for BitTorrent connections"
-msgstr ""
+msgstr "IP адрас каб слухаць падлучэнне BitTorrent"
#: deluge/core/daemon_entry.py:56
msgid ""
"The network interface name or IP address for outgoing BitTorrent connections."
-msgstr ""
+msgstr "Назва сеткі ці IP адрас для сыходзячага падлучэння BitTorrent."
#: deluge/core/daemon_entry.py:63
msgid "Config keys to be unmodified by `set_config` RPC"
msgstr ""
+"Ключы канфігурацый для RPC, немадыфікаваных з дапамогай `set_config`"
#: deluge/ui/common.py:37 deluge/ui/gtk3/filtertreeview.py:130
#: deluge/ui/web/js/deluge-all/UI.js:18
msgid "All"
-msgstr ""
+msgstr "Усе"
#: deluge/ui/common.py:38 deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:490
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:534
#: deluge/ui/web/js/deluge-all/UI.js:19
msgid "Active"
-msgstr ""
+msgstr "Актыўны"
#: deluge/ui/common.py:39 deluge/ui/web/js/deluge-all/UI.js:20
msgid "Allocating"
-msgstr ""
+msgstr "Выдзяленне"
#: deluge/ui/common.py:40 deluge/ui/web/js/deluge-all/UI.js:21
#: deluge/ui/web/js/deluge-all/UI.js:25
msgid "Checking"
-msgstr ""
+msgstr "Праверка"
#: deluge/ui/common.py:41
#: deluge/ui/console/modes/preferences/preference_panes.py:568
#: deluge/ui/web/js/deluge-all/UI.js:22
msgid "Downloading"
-msgstr ""
+msgstr "Спампоўка"
#: deluge/ui/common.py:42
#: deluge/ui/console/modes/preferences/preference_panes.py:575
#: deluge/ui/web/js/deluge-all/UI.js:23
msgid "Seeding"
-msgstr ""
+msgstr "Раздача"
#: deluge/ui/common.py:43 deluge/ui/web/js/deluge-all/UI.js:24
msgid "Paused"
-msgstr ""
+msgstr "Прыпынена"
#: deluge/ui/common.py:44 deluge/ui/web/js/deluge-all/UI.js:26
msgid "Queued"
-msgstr ""
+msgstr "У чарзе"
#: deluge/ui/common.py:45 deluge/ui/common.py:122
#: deluge/ui/gtk3/statusbar.py:396 deluge/ui/gtk3/filtertreeview.py:131
@@ -258,7 +262,7 @@ msgstr "Назва"
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:80
#: deluge/ui/web/js/deluge-all/details/FilesTab.js:34
msgid "Progress"
-msgstr "Стан"
+msgstr "Прагрэс"
#: deluge/ui/common.py:52 deluge/ui/web/js/deluge-all/Sidebar.js:12
msgid "State"
@@ -278,17 +282,17 @@ msgstr "Памер"
#: deluge/ui/common.py:55 deluge/ui/gtk3/torrentview.py:289
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:244
msgid "Downloaded"
-msgstr ""
+msgstr "Спампавана"
#: deluge/ui/common.py:56 deluge/ui/gtk3/torrentview.py:296
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:252
msgid "Uploaded"
-msgstr ""
+msgstr "Запампавана"
#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
msgid "Remaining"
-msgstr ""
+msgstr "Застаецца"
#: deluge/ui/common.py:58 deluge/ui/gtk3/torrentview.py:373
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:165
@@ -300,32 +304,32 @@ msgstr "Рэйтынг"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:144
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:87
msgid "Down Speed"
-msgstr "Запампоўка"
+msgstr "Хуткасць спампоўкі"
#: deluge/ui/common.py:60 deluge/ui/gtk3/torrentview.py:346
#: deluge/ui/gtk3/peers_tab.py:146
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:151
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:94
msgid "Up Speed"
-msgstr "Раздача"
+msgstr "Хуткасць раздачы"
#: deluge/ui/common.py:61 deluge/ui/gtk3/torrentview.py:352
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:268
msgid "Down Limit"
-msgstr ""
+msgstr "Ліміт спампоўкі"
#: deluge/ui/common.py:62 deluge/ui/gtk3/torrentview.py:359
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:276
msgid "Up Limit"
-msgstr ""
+msgstr "Ліміт раздачы"
#: deluge/ui/common.py:63 deluge/ui/web/js/deluge-all/add/OptionsTab.js:101
msgid "Max Connections"
-msgstr ""
+msgstr "Макс злучэнняў"
#: deluge/ui/common.py:64 deluge/ui/web/js/deluge-all/add/OptionsTab.js:109
msgid "Max Upload Slots"
-msgstr ""
+msgstr "Макс слотаў раздачы"
#: deluge/ui/common.py:65 deluge/ui/gtk3/torrentview.py:325
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:136
@@ -336,7 +340,7 @@ msgstr "Піры"
#: deluge/ui/common.py:66 deluge/ui/gtk3/torrentview.py:317
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:128
msgid "Seeds"
-msgstr ""
+msgstr "Сіды"
#: deluge/ui/common.py:67 deluge/ui/gtk3/torrentview.py:380
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:173
@@ -346,13 +350,13 @@ msgstr "Даступна"
#: deluge/ui/common.py:68 deluge/ui/gtk3/torrentview.py:333
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:284
msgid "Seeds:Peers"
-msgstr ""
+msgstr "Сіды:Піры"
#: deluge/ui/common.py:69 deluge/ui/gtk3/listview.py:203
#: deluge/ui/gtk3/torrentview.py:387
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:181
msgid "Added"
-msgstr "Даданы"
+msgstr "Дададзена"
#: deluge/ui/common.py:70 deluge/ui/gtk3/createtorrentdialog.py:88
#: deluge/ui/gtk3/edittrackersdialog.py:127 deluge/ui/gtk3/torrentview.py:408
@@ -367,55 +371,55 @@ msgstr "Трэкер"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:213
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:31
msgid "Download Folder"
-msgstr ""
+msgstr "Папка спампоўкі"
#: deluge/ui/common.py:75
msgid "Seeding Time"
-msgstr ""
+msgstr "Час сідавання"
#: deluge/ui/common.py:76
msgid "Active Time"
-msgstr ""
+msgstr "Актыўны час"
#: deluge/ui/common.py:78
msgid "Last Activity"
-msgstr ""
+msgstr "Апошняя актыўнасць"
#: deluge/ui/common.py:81
msgid "Finished Time"
-msgstr ""
+msgstr "Час завяршэння"
#: deluge/ui/common.py:83 deluge/ui/gtk3/torrentview.py:401
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:189
msgid "Complete Seen"
-msgstr ""
+msgstr "Паказ завершанага"
#: deluge/ui/common.py:86 deluge/ui/gtk3/torrentview.py:394
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:197
msgid "Completed"
-msgstr ""
+msgstr "Завершана"
#: deluge/ui/common.py:87 deluge/ui/gtk3/torrentview.py:366
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:158
msgid "ETA"
-msgstr "Засталася"
+msgstr "Засталося"
#: deluge/ui/common.py:88 deluge/ui/gtk3/torrentview.py:418
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:236
msgid "Shared"
-msgstr ""
+msgstr "Супольнае"
#: deluge/ui/common.py:90 deluge/ui/gtk3/glade/main_window.tabs.ui.h:31
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:287
msgid "Prioritize First/Last"
-msgstr "Прыярытэт першай/апошняй"
+msgstr "Прыятытэт Першае/Апошняе"
#: deluge/ui/common.py:94 deluge/ui/gtk3/glade/main_window.tabs.ui.h:32
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:14
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:143
msgid "Sequential Download"
-msgstr ""
+msgstr "Паслядоўная спампоўка"
#: deluge/ui/common.py:97 deluge/ui/common.py:98
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:35
@@ -427,27 +431,27 @@ msgstr "Аўтаматычнае кіраванне"
#: deluge/ui/common.py:99
msgid "Stop At Ratio"
-msgstr ""
+msgstr "Спыніць пры суадносінах"
#: deluge/ui/common.py:100
msgid "Stop Ratio"
-msgstr ""
+msgstr "Суадносіны для спынення"
#: deluge/ui/common.py:101
msgid "Remove At Ratio"
-msgstr ""
+msgstr "Выдаліць пры суадносінах"
#: deluge/ui/common.py:102 deluge/ui/common.py:108
msgid "Move On Completed"
-msgstr ""
+msgstr "Перамясціць пры завяршэнні"
#: deluge/ui/common.py:104
msgid "Move Completed Path"
-msgstr ""
+msgstr "Шлях перамяшчэння завершанага"
#: deluge/ui/common.py:112
msgid "Move On Completed Path"
-msgstr ""
+msgstr "Перамясціць на шлях завершанага"
#: deluge/ui/common.py:115 deluge/ui/gtk3/filtertreeview.py:135
#: deluge/ui/gtk3/torrentview.py:416
@@ -455,25 +459,25 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/FilterPanel.js:32
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:221
msgid "Owner"
-msgstr ""
+msgstr "Уласнік"
#: deluge/ui/common.py:116
msgid "Pieces"
-msgstr ""
+msgstr "Часткі"
#: deluge/ui/common.py:117
msgid "Seed Rank"
-msgstr ""
+msgstr "Ранг сідаў"
#: deluge/ui/common.py:118 deluge/ui/gtk3/glade/main_window.tabs.ui.h:33
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:22
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:294
msgid "Super Seeding"
-msgstr ""
+msgstr "Супер сідаванне"
#: deluge/ui/common.py:123 deluge/ui/web/js/deluge-all/details/StatusTab.js:122
msgid "Warning"
-msgstr "Папярэджаньне"
+msgstr "Папярэджанне"
#: deluge/ui/common.py:124 deluge/ui/web/js/deluge-all/details/StatusTab.js:123
msgid "Announce OK"
@@ -496,7 +500,7 @@ msgstr "Інтэрфейс"
#: deluge/ui/console/modes/preferences/preferences.py:90
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:21
msgid "Downloads"
-msgstr "Запампоўкі"
+msgstr "Спампоўкі"
#: deluge/ui/common.py:131 deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:33
#: deluge/ui/console/modes/preferences/preference_panes.py:409
@@ -523,7 +527,7 @@ msgstr "Чарга"
#: deluge/ui/console/modes/preferences/preferences.py:91
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:28
msgid "Network"
-msgstr "Сеціва"
+msgstr "Сетка"
#: deluge/ui/common.py:134 deluge/ui/gtk3/glade/preferences_dialog.ui.h:146
#: deluge/ui/console/modes/preferences/preference_panes.py:640
@@ -562,33 +566,35 @@ msgstr "Дэман"
#: deluge/ui/common.py:138
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:18
msgid "Plugins"
-msgstr "Модулі"
+msgstr "Плагіны"
#: deluge/ui/common.py:150 deluge/ui/web/js/deluge-all/Deluge.js:154
#: deluge/ui/web/js/deluge-all/Menus.js:365
msgid "Skip"
-msgstr ""
+msgstr "Прапусціць"
#: deluge/ui/common.py:151 deluge/ui/web/js/deluge-all/Deluge.js:155
#: deluge/ui/web/js/deluge-all/Menus.js:371
msgid "Low"
-msgstr ""
+msgstr "Нізкі"
#: deluge/ui/common.py:152 deluge/ui/web/js/deluge-all/Deluge.js:156
#: deluge/ui/web/js/deluge-all/Menus.js:377
msgid "Normal"
-msgstr ""
+msgstr "Звычайны"
#: deluge/ui/common.py:153 deluge/ui/web/js/deluge-all/Deluge.js:157
#: deluge/ui/web/js/deluge-all/Menus.js:383
msgid "High"
-msgstr ""
+msgstr "Высокі"
#: deluge/ui/client.py:681
msgid ""
"Deluge cannot find the `deluged` executable, check that the deluged package "
"is installed, or added to your PATH."
msgstr ""
+"Deluge не знаходзіць выканальны `deluged`, праверце што пакет усталяваны, "
+"або дадайце шлях."
#: deluge/ui/countries.py:10
msgid "Afghanistan"
@@ -608,7 +614,7 @@ msgstr "Алжыр"
#: deluge/ui/countries.py:14
msgid "American Samoa"
-msgstr "Усходняе Самоа"
+msgstr "Амерыканскае Самоа"
#: deluge/ui/countries.py:15
msgid "Andorra"
@@ -680,7 +686,7 @@ msgstr "Бельгія"
#: deluge/ui/countries.py:32
msgid "Belize"
-msgstr "Бэліз"
+msgstr "Беліз"
#: deluge/ui/countries.py:33
msgid "Benin"
@@ -688,7 +694,7 @@ msgstr "Бенін"
#: deluge/ui/countries.py:34
msgid "Bermuda"
-msgstr "Бэрмудзкія астравы"
+msgstr "Бермудскія астравы"
#: deluge/ui/countries.py:35
msgid "Bhutan"
@@ -708,7 +714,7 @@ msgstr "Батсвана"
#: deluge/ui/countries.py:39
msgid "Bouvet Island"
-msgstr "Востраў Буве"
+msgstr "Востраў Бувэ"
#: deluge/ui/countries.py:40
msgid "Brazil"
@@ -728,7 +734,7 @@ msgstr "Балгарыя"
#: deluge/ui/countries.py:44
msgid "Burkina Faso"
-msgstr "Буркіна Фасо"
+msgstr "Буркіна-Фасо"
#: deluge/ui/countries.py:45
msgid "Burundi"
@@ -748,7 +754,7 @@ msgstr "Канада"
#: deluge/ui/countries.py:49
msgid "Cape Verde"
-msgstr "Каба Вэрдэ"
+msgstr "Каба-Вердэ"
#: deluge/ui/countries.py:50
msgid "Cayman Islands"
@@ -756,7 +762,7 @@ msgstr "Кайманавы астравы"
#: deluge/ui/countries.py:51
msgid "Central African Republic"
-msgstr "Цэнтральнаафрыканская Рэспубліка"
+msgstr "Цэнтральна-Афрыканская Рэспубліка"
#: deluge/ui/countries.py:52
msgid "Chad"
@@ -772,11 +778,11 @@ msgstr "Кітай"
#: deluge/ui/countries.py:55
msgid "Christmas Island"
-msgstr "Востраў Раства"
+msgstr "Востраў Каляд"
#: deluge/ui/countries.py:56
msgid "Cocos (Keeling) Islands"
-msgstr "Какосавыя астравы (Астравы Килинг)"
+msgstr "Какосавыя (Кілінг) астравы"
#: deluge/ui/countries.py:57
msgid "Colombia"
@@ -800,7 +806,7 @@ msgstr "Астравы Кука"
#: deluge/ui/countries.py:62
msgid "Costa Rica"
-msgstr "Коста Рыка"
+msgstr "Коста-Рыка"
#: deluge/ui/countries.py:63
msgid "Cote d'Ivoire"
@@ -820,7 +826,7 @@ msgstr "Кіпр"
#: deluge/ui/countries.py:67
msgid "Czech Republic"
-msgstr "Чэшская рэспубліка"
+msgstr "Чэшская Рэспубліка"
#: deluge/ui/countries.py:68
msgid "Denmark"
@@ -868,7 +874,7 @@ msgstr "Эфіопія"
#: deluge/ui/countries.py:79
msgid "Falkland Islands (Malvinas)"
-msgstr "Фальклендзкія (Мальвінскія) астравы"
+msgstr "Фалклендскія (Мальвінскія) астравы"
#: deluge/ui/countries.py:80
msgid "Faroe Islands"
@@ -956,7 +962,7 @@ msgstr "Гвінея"
#: deluge/ui/countries.py:101
msgid "Guinea-Bissau"
-msgstr "Гвінея-Бісаў"
+msgstr "Гвінея-Бісау"
#: deluge/ui/countries.py:102
msgid "Guyana"
@@ -996,7 +1002,7 @@ msgstr "Індыя"
#: deluge/ui/countries.py:111
msgid "Indonesia"
-msgstr "Інданэзія"
+msgstr "Інданезія"
#: deluge/ui/countries.py:112
msgid "Iran, Islamic Republic of"
@@ -1064,11 +1070,11 @@ msgstr "Кувейт"
#: deluge/ui/countries.py:128
msgid "Kyrgyzstan"
-msgstr "Кіргізія"
+msgstr "Кыргызстан"
#: deluge/ui/countries.py:129
msgid "Lao People's Democratic Republic"
-msgstr "Лаоская Народна-Дэмакратычная Рэспубліка"
+msgstr "Лаос"
#: deluge/ui/countries.py:130
msgid "Latvia"
@@ -1092,7 +1098,7 @@ msgstr "Лівійская Арабская Джамахірыя"
#: deluge/ui/countries.py:135
msgid "Liechtenstein"
-msgstr "Ліхтэнштайн"
+msgstr "Ліхтэнштэйн"
#: deluge/ui/countries.py:136
msgid "Lithuania"
@@ -1108,7 +1114,7 @@ msgstr "Макао"
#: deluge/ui/countries.py:139
msgid "Macedonia, The Former Yugoslav Republic of"
-msgstr "Македонія"
+msgstr "Паўночная Македонія"
#: deluge/ui/countries.py:140
msgid "Madagascar"
@@ -1124,7 +1130,7 @@ msgstr "Малайзія"
#: deluge/ui/countries.py:143
msgid "Maldives"
-msgstr "Мальдыўскія астравы"
+msgstr "Мальдывы"
#: deluge/ui/countries.py:144
msgid "Mali"
@@ -1160,7 +1166,7 @@ msgstr "Мексіка"
#: deluge/ui/countries.py:152
msgid "Micronesia, Federated States of"
-msgstr "Фэдэратыўныя Штаты Мікранэзіі"
+msgstr "Федэратыўныя Штаты Мікранезіі"
#: deluge/ui/countries.py:153
msgid "Moldova"
@@ -1180,7 +1186,7 @@ msgstr "Чарнагорыя"
#: deluge/ui/countries.py:157
msgid "Montserrat"
-msgstr "Мансэрат"
+msgstr "Мантсерат"
#: deluge/ui/countries.py:158
msgid "Morocco"
@@ -1204,7 +1210,7 @@ msgstr "Науру"
#: deluge/ui/countries.py:163
msgid "Nepal"
-msgstr "Нэпал"
+msgstr "Непал"
#: deluge/ui/countries.py:164
msgid "Netherlands"
@@ -1236,7 +1242,7 @@ msgstr "Нігерыя"
#: deluge/ui/countries.py:171
msgid "Niue"
-msgstr "Ніуе"
+msgstr "Ніуэ"
#: deluge/ui/countries.py:172
msgid "Norfolk Island"
@@ -1272,7 +1278,7 @@ msgstr "Панама"
#: deluge/ui/countries.py:180
msgid "Papua New Guinea"
-msgstr "Папуа - Новая Гвінея"
+msgstr "Папуа-Новая Гвінея"
#: deluge/ui/countries.py:181
msgid "Paraguay"
@@ -1288,7 +1294,7 @@ msgstr "Філіпіны"
#: deluge/ui/countries.py:184
msgid "Pitcairn"
-msgstr "Піткэрн"
+msgstr "Астравы Піткэрн"
#: deluge/ui/countries.py:185
msgid "Poland"
@@ -1300,7 +1306,7 @@ msgstr "Партугалія"
#: deluge/ui/countries.py:187
msgid "Puerto Rico"
-msgstr "Пуэрта Рыка"
+msgstr "Пуэрта-Рыка"
#: deluge/ui/countries.py:188
msgid "Qatar"
@@ -1324,31 +1330,31 @@ msgstr "Руанда"
#: deluge/ui/countries.py:193
msgid "Saint Barthelemy"
-msgstr "Сэнт-Бартелеми"
+msgstr "Сен-Бартэльмі"
#: deluge/ui/countries.py:194
msgid "Saint Helena"
-msgstr "Востраў Святой Елены"
+msgstr "Востраў Святой Алены"
#: deluge/ui/countries.py:195
msgid "Saint Kitts and Nevis"
-msgstr "Сэнт-Кітс і Нэвіс"
+msgstr "Сент-Кітс і Невіс"
#: deluge/ui/countries.py:196
msgid "Saint Lucia"
-msgstr "Сэнт-Люсія"
+msgstr "Сент-Люсія"
#: deluge/ui/countries.py:197
msgid "Saint Martin"
-msgstr "Сэнт-Марцін"
+msgstr "Сен-Мартэн"
#: deluge/ui/countries.py:198
msgid "Saint Pierre and Miquelon"
-msgstr "Сэнт-П'ер і Мікелон"
+msgstr "Сен-П'ер і Мікелон"
#: deluge/ui/countries.py:199
msgid "Saint Vincent and the Grenadines"
-msgstr "Сэнт-Вінсэнт і Грэнадзіны"
+msgstr "Сент-Вінсент і Грэнадзіны"
#: deluge/ui/countries.py:200
msgid "Samoa"
@@ -1360,7 +1366,7 @@ msgstr "Сан-Марына"
#: deluge/ui/countries.py:202
msgid "Sao Tome and Principe"
-msgstr "Сан-Томе і Прынсыпі"
+msgstr "Сан-Тамэ і Прынсіпі"
#: deluge/ui/countries.py:203
msgid "Saudi Arabia"
@@ -1396,7 +1402,7 @@ msgstr "Славенія"
#: deluge/ui/countries.py:211
msgid "Solomon Islands"
-msgstr "Саламонавы Астравы"
+msgstr "Саламонавы астравы"
#: deluge/ui/countries.py:212
msgid "Somalia"
@@ -1408,7 +1414,7 @@ msgstr "Паўднёва-Афрыканская Рэспубліка"
#: deluge/ui/countries.py:214
msgid "South Georgia and the South Sandwich Islands"
-msgstr "Паўднёвая Джорджыя і Паўднёвыя Сэндвічавыя выспы"
+msgstr "Паўднёвая Георгія і Паўднёвыя Сандвічавы астравы"
#: deluge/ui/countries.py:215
msgid "Spain"
@@ -1428,7 +1434,7 @@ msgstr "Сурынам"
#: deluge/ui/countries.py:219
msgid "Svalbard and Jan Mayen"
-msgstr "Шпіцбэрген і Ян-Майен"
+msgstr "Шпіцберген і Ян-Маен"
#: deluge/ui/countries.py:220
msgid "Swaziland"
@@ -1448,7 +1454,7 @@ msgstr "Сірыйская Арабская Рэспубліка"
#: deluge/ui/countries.py:224
msgid "Taiwan"
-msgstr ""
+msgstr "Тайвань"
#: deluge/ui/countries.py:225
msgid "Tajikistan"
@@ -1456,7 +1462,7 @@ msgstr "Таджыкістан"
#: deluge/ui/countries.py:226
msgid "Tanzania, United Republic of"
-msgstr "Злучаная Рэспубліка Танзанія"
+msgstr "Танзанія"
#: deluge/ui/countries.py:227
msgid "Thailand"
@@ -1464,7 +1470,7 @@ msgstr "Тайланд"
#: deluge/ui/countries.py:228
msgid "Timor-Leste"
-msgstr "Усходні Тымор"
+msgstr "Тымор-Лешці"
#: deluge/ui/countries.py:229
msgid "Togo"
@@ -1472,7 +1478,7 @@ msgstr "Тога"
#: deluge/ui/countries.py:230
msgid "Tokelau"
-msgstr "Такелаў"
+msgstr "Такелау"
#: deluge/ui/countries.py:231
msgid "Tonga"
@@ -1496,7 +1502,7 @@ msgstr "Туркменістан"
#: deluge/ui/countries.py:236
msgid "Turks and Caicos Islands"
-msgstr "Астравы Тэркс і Кайкас"
+msgstr "Астравы Цёркс і Кайкас"
#: deluge/ui/countries.py:237
msgid "Tuvalu"
@@ -1512,7 +1518,7 @@ msgstr "Украіна"
#: deluge/ui/countries.py:240
msgid "United Arab Emirates"
-msgstr "Злучаныя Арабскія Эміраты"
+msgstr "Аб'яднаныя Арабскія Эміраты"
#: deluge/ui/countries.py:241
msgid "United Kingdom"
@@ -1524,7 +1530,7 @@ msgstr "Злучаныя Штаты Амерыкі"
#: deluge/ui/countries.py:243
msgid "United States Minor Outlying Islands"
-msgstr "Знешнія Малыя Астравы (ЗША)"
+msgstr "Малыя Аддаленыя астравы ЗША"
#: deluge/ui/countries.py:244
msgid "Uruguay"
@@ -1556,7 +1562,7 @@ msgstr "Віргінскія астравы (ЗША)"
#: deluge/ui/countries.py:251
msgid "Wallis and Futuna"
-msgstr "Астравы Уоліс і Футуна"
+msgstr "Уоліс і Футуна"
#: deluge/ui/countries.py:252
msgid "Western Sahara"
@@ -1576,45 +1582,47 @@ msgstr "Зімбабвэ"
#: deluge/ui/ui_entry.py:51
msgid "UI Options"
-msgstr ""
+msgstr "Опцыі UI"
#: deluge/ui/ui_entry.py:57
msgid "Set the default UI to be run, when no UI is specified"
-msgstr ""
+msgstr "Вызначыць UI для запуску па змаўчанні, калі UI не вызначаны"
#: deluge/ui/ui_entry.py:91
msgid ""
"Alternative UI to launch, with optional ui args \n"
" (default UI: *)"
msgstr ""
+"Альтэрнатыўны UI для запуску, з апцыянальнымі аргументамі \n"
+" (UI па змаўчанні: *)"
#: deluge/ui/web/web.py:32
msgid "Web Server Options"
-msgstr ""
+msgstr "Опцыі вэб-сервера"
#: deluge/ui/web/web.py:38
msgid "IP address for web server to listen on"
-msgstr ""
+msgstr "IP адрас для праслухоўвання вэб-сервера"
#: deluge/ui/web/web.py:46
msgid "Port for web server to listen on"
-msgstr ""
+msgstr "Порт для праслухоўвання вэб-сервера"
#: deluge/ui/web/web.py:53
msgid "Set the base path that the ui is running on"
-msgstr ""
+msgstr "Наладзіць базавы шлях для запуску UI"
#: deluge/ui/web/web.py:56
msgid "Force the web server to use SSL"
-msgstr ""
+msgstr "Прымусіць вэб-сервер выкарыстоўваць SSL"
#: deluge/ui/web/web.py:61
msgid "Force the web server to disable SSL"
-msgstr ""
+msgstr "Прымусіць вэб-сервер адключыць SSL"
#: deluge/ui/web/json_api.py:868
msgid "Daemon does not exist"
-msgstr ""
+msgstr "Дэман не існуе"
#: deluge/ui/web/json_api.py:875
msgid "Daemon not running"
@@ -1660,18 +1668,18 @@ msgstr "Выберыце файл"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:2
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:2
msgid "_Cancel"
-msgstr ""
+msgstr "_Скасаваць"
#: deluge/ui/gtk3/createtorrentdialog.py:134
#: deluge/ui/gtk3/createtorrentdialog.py:171
#: deluge/ui/gtk3/addtorrentdialog.py:700 deluge/ui/gtk3/preferences.py:1160
msgid "_Open"
-msgstr ""
+msgstr "_Адкрыць"
#: deluge/ui/gtk3/createtorrentdialog.py:165
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:28
msgid "Choose a folder"
-msgstr "Выберыце каталог"
+msgstr "Выбраць папку"
#: deluge/ui/gtk3/createtorrentdialog.py:254
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:4
@@ -1682,12 +1690,12 @@ msgstr "Захаваць файл .torrent"
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:3
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:30
msgid "_Save"
-msgstr ""
+msgstr "_Захаваць"
#: deluge/ui/gtk3/createtorrentdialog.py:271
#: deluge/ui/gtk3/addtorrentdialog.py:712
msgid "Torrent files"
-msgstr "Торэнт файлы"
+msgstr "Торэнт-файлы"
#: deluge/ui/gtk3/createtorrentdialog.py:275
#: deluge/ui/gtk3/addtorrentdialog.py:716
@@ -1696,16 +1704,16 @@ msgstr "Усе файлы"
#: deluge/ui/gtk3/mainwindow.py:192
msgid "Enter your password to show Deluge..."
-msgstr ""
+msgstr "Увядзіце пароль, каб паказаць Deluge..."
#: deluge/ui/gtk3/mainwindow.py:251
msgid "Enter your password to Quit Deluge..."
-msgstr ""
+msgstr "Увядзіце пароль, каб выйсці з Deluge..."
#: deluge/ui/gtk3/mainwindow.py:343
#, python-brace-format
msgid "D: {download_rate} U: {upload_rate} - Deluge"
-msgstr ""
+msgstr "D: {download_rate} U: {upload_rate} - Deluge"
#: deluge/ui/gtk3/mainwindow.py:357 deluge/ui/gtk3/aboutdialog.py:26
#: deluge/ui/gtk3/aboutdialog.py:27 deluge/ui/gtk3/systemtray.py:96
@@ -1719,21 +1727,21 @@ msgstr "Deluge"
#: deluge/ui/gtk3/path_combo_chooser.py:393
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:20
msgid "Edit path"
-msgstr ""
+msgstr "Змяніць шлях"
#: deluge/ui/gtk3/path_combo_chooser.py:395
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:21
msgid "Remove path"
-msgstr ""
+msgstr "Выдаліць шлях"
#: deluge/ui/gtk3/options_tab.py:136
msgid "_Apply to selected"
-msgstr ""
+msgstr "_Прымяніць да выбранага"
#: deluge/ui/gtk3/aboutdialog.py:40
#, python-format
msgid "Copyright %(year_start)s-%(year_end)s Deluge Team"
-msgstr ""
+msgstr "Аўтарскае права %(year_start)s-%(year_end)s Каманда Deluge"
#: deluge/ui/gtk3/aboutdialog.py:44
#: deluge/ui/web/js/deluge-all/AboutWindow.js:52
@@ -1741,19 +1749,21 @@ msgid ""
"A peer-to-peer file sharing program\n"
"utilizing the BitTorrent protocol."
msgstr ""
+"Праграма перадачы файлаў\n"
+"выкарыстоўваючы пратакол BitTorrent."
#: deluge/ui/gtk3/aboutdialog.py:46
#: deluge/ui/web/js/deluge-all/AboutWindow.js:55
msgid "Client:"
-msgstr ""
+msgstr "Кліент:"
#: deluge/ui/gtk3/aboutdialog.py:52
msgid "Current Developers:"
-msgstr ""
+msgstr "Дзейныя распрацоўнікі:"
#: deluge/ui/gtk3/aboutdialog.py:61
msgid "Past Developers or Contributors:"
-msgstr ""
+msgstr "Ранейшыя распрацоўнікі і ўкладальнікі:"
#: deluge/ui/gtk3/aboutdialog.py:795
msgid ""
@@ -1781,16 +1791,38 @@ msgid ""
"delete this exception statement from all source files in the program, then "
"also delete it here."
msgstr ""
+"Гэта праграма свабодная; вы можаце змяняць яе паводле ліцэнзіі GNU General "
+"Public License, распаўсюджанай фондам Free Software Foundation; як трэцяй "
+"версіі, так і пазнейшымі. \n"
+"\n"
+"Гэта праграма распаўсюджваецца ў надзеі, што яна будзе карысна, але БЕЗ "
+"АНІЯКІХ ГАРАНТЫЙ; нават без гарантыі ПРЫДАТНАСЦІ ці АДПАВЕДНАСЦІ "
+"ПРЫЗНАЧЭННЮ. Для падрабязнасцей глядзіце тэкст ліцэнзіі GNU General Public "
+"License. \n"
+"\n"
+"Вы павінны былі атрымаць копію ліцэнзіі GNU General Public License разам з "
+"гэтай праграмай; калі ж не, глядзіце <http://www.gnu.org/licenses>. \n"
+"\n"
+"Дадаткова, як выключэнне, уладальнікі правоў даюць зазвол спасылацца на код "
+"праграмы праз свабодныя бібліятэкі OpenSSL. Вы павінны прытрымлівацца "
+"ліцэнзіі GNU General Public License з павагай да ўсяго кода, які "
+"выкарыстоўваецца акрамя OpenSSL. \n"
+"\n"
+"Пры змяненні файла(ў) у адпаведнасці з выключэннем, вы можаце пашырыць "
+"выключэнне да вашай версіі файла(ў), але вы не абавязаны рабіць гэта. Калі "
+"вы не жадаеце рабіць гэта, выдаліце гэта паведамленне з вашай версіі. Калі "
+"вы выдаліце паведамленне аб выключэнні з усіх файлаў праграмы, таксама "
+"выдаліце яго тут."
#: deluge/ui/gtk3/aboutdialog.py:829
#: deluge/ui/web/js/deluge-all/AboutWindow.js:65
msgid "Server:"
-msgstr "Сэрвер:"
+msgstr "Сервер:"
#: deluge/ui/gtk3/aboutdialog.py:833
#: deluge/ui/web/js/deluge-all/AboutWindow.js:41
msgid "libtorrent:"
-msgstr ""
+msgstr "libtorrent:"
#: deluge/ui/gtk3/addtorrentdialog.py:102 deluge/ui/gtk3/queuedtorrents.py:51
msgid "Torrent"
@@ -1799,17 +1831,17 @@ msgstr "Торэнт"
#: deluge/ui/gtk3/addtorrentdialog.py:232
#, python-format
msgid "Add Torrents (%d)"
-msgstr ""
+msgstr "Дадаць торэнты (%d)"
#: deluge/ui/gtk3/addtorrentdialog.py:238
msgid "Duplicate torrent(s)"
-msgstr ""
+msgstr "Дубліраваны(я) торрэнт(ы)"
#: deluge/ui/gtk3/addtorrentdialog.py:240
#, python-format
msgid ""
"You cannot add the same torrent twice. %d torrents were already added."
-msgstr ""
+msgstr "Нельга двойчы дадаць адзін торрэнт. Ужо дададзена торрэнтаў: %d."
#: deluge/ui/gtk3/addtorrentdialog.py:255
msgid "Invalid File"
@@ -1818,11 +1850,11 @@ msgstr "Няправільны файл"
#: deluge/ui/gtk3/addtorrentdialog.py:290
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:8
msgid "Please wait for files..."
-msgstr ""
+msgstr "Пачакайце файлаў..."
#: deluge/ui/gtk3/addtorrentdialog.py:296
msgid "Unable to download files for this magnet"
-msgstr ""
+msgstr "Немагчыма спампаваць файлы для гэтай спасылкі"
#: deluge/ui/gtk3/addtorrentdialog.py:694
msgid "Choose a .torrent file"
@@ -1834,49 +1866,49 @@ msgstr "Няправільны URL"
#: deluge/ui/gtk3/addtorrentdialog.py:778
msgid "is not a valid URL."
-msgstr ""
+msgstr "не карэктны URL."
#: deluge/ui/gtk3/addtorrentdialog.py:784
msgid "Downloading..."
-msgstr ""
+msgstr "Спампоўка..."
#: deluge/ui/gtk3/addtorrentdialog.py:819
msgid "Download Failed"
-msgstr "Збой запампоўкі"
+msgstr "Не ўдалося спампаваць"
#: deluge/ui/gtk3/addtorrentdialog.py:820
msgid "Failed to download:"
-msgstr ""
+msgstr "Не ўдалося спампаваць:"
#: deluge/ui/gtk3/dialogs.py:110
msgid "_No"
-msgstr ""
+msgstr "_Не"
#: deluge/ui/gtk3/dialogs.py:110
msgid "_Yes"
-msgstr ""
+msgstr "_Так"
#: deluge/ui/gtk3/dialogs.py:132 deluge/ui/gtk3/dialogs.py:156
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:2
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:2
#: deluge/ui/gtk3/glade/connection_manager.ui.h:2
msgid "_Close"
-msgstr ""
+msgstr "_Закрыць"
#: deluge/ui/gtk3/dialogs.py:179
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:195
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:38
msgid "Details:"
-msgstr "Дадатковыя звесткі:"
+msgstr "Падрабязнасці:"
#: deluge/ui/gtk3/dialogs.py:200
msgid "Authenticate"
-msgstr ""
+msgstr "Аўтэнтыфікацыя"
#: deluge/ui/gtk3/dialogs.py:203 deluge/ui/gtk3/connectionmanager.py:211
#: deluge/ui/gtk3/glade/connection_manager.ui.h:3
msgid "C_onnect"
-msgstr ""
+msgstr "Да_лучыцца"
#: deluge/ui/gtk3/dialogs.py:209 deluge/ui/gtk3/dialogs.py:281
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:139
@@ -1904,25 +1936,25 @@ msgstr "Пароль:"
#: deluge/ui/gtk3/dialogs.py:257
msgid "Edit Account"
-msgstr ""
+msgstr "Рэдагаваць акаўнт"
#: deluge/ui/gtk3/dialogs.py:258
msgid "Edit existing account"
-msgstr ""
+msgstr "Рэдагаваць існуючы акаўнт"
#: deluge/ui/gtk3/dialogs.py:263 deluge/ui/gtk3/dialogs.py:364
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:16
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:38
msgid "_Apply"
-msgstr ""
+msgstr "_Ужыць"
#: deluge/ui/gtk3/dialogs.py:270
msgid "New Account"
-msgstr ""
+msgstr "Новы акаўнт"
#: deluge/ui/gtk3/dialogs.py:271
msgid "Create a new account"
-msgstr ""
+msgstr "Стварыць новы акаўнт"
#: deluge/ui/gtk3/dialogs.py:273 deluge/ui/gtk3/glade/queuedtorrents.ui.h:3
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:191
@@ -1931,15 +1963,15 @@ msgstr ""
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:20
#: deluge/ui/gtk3/glade/connection_manager.ui.h:4
msgid "_Add"
-msgstr ""
+msgstr "_Дадаць"
#: deluge/ui/gtk3/dialogs.py:289
msgid "Authentication Level:"
-msgstr ""
+msgstr "Узровень аўтэнтыфікацыі:"
#: deluge/ui/gtk3/dialogs.py:423
msgid "Password Protected"
-msgstr ""
+msgstr "Абаронена паролем"
#: deluge/ui/gtk3/dialogs.py:429
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:17
@@ -1955,7 +1987,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:3
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:3
msgid "_OK"
-msgstr ""
+msgstr "_ОК"
#: deluge/ui/gtk3/common.py:155 deluge/ui/gtk3/menubar.py:83
msgid "Other..."
@@ -1967,19 +1999,19 @@ msgstr "Не падключаны"
#: deluge/ui/gtk3/statusbar.py:175
msgid "Connections (Limit)"
-msgstr ""
+msgstr "Падлучэнні (ліміт)"
#: deluge/ui/gtk3/statusbar.py:182
msgid "Download Speed (Limit)"
-msgstr ""
+msgstr "Хуткасць спампоўкі (ліміт)"
#: deluge/ui/gtk3/statusbar.py:189
msgid "Upload Speed (Limit)"
-msgstr ""
+msgstr "Хуткасць раздачы (ліміт)"
#: deluge/ui/gtk3/statusbar.py:196
msgid "Protocol Traffic (Down:Up)"
-msgstr ""
+msgstr "Пратакольны трафік (спампоўка:раздача)"
#: deluge/ui/gtk3/statusbar.py:201 deluge/ui/web/js/deluge-all/Statusbar.js:234
msgid "DHT Nodes"
@@ -1987,82 +2019,82 @@ msgstr "Вузлы DHT"
#: deluge/ui/gtk3/statusbar.py:207
msgid "Free Disk Space"
-msgstr ""
+msgstr "Свабоднае месца на дыску"
#: deluge/ui/gtk3/statusbar.py:212 deluge/ui/web/js/deluge-all/Statusbar.js:226
msgid "External IP Address"
-msgstr ""
+msgstr "Знешні IP адрас"
#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:409
#, python-format
msgid "<b>IP</b> <small>%s</small>"
-msgstr ""
+msgstr "<b>IP</b> <small>%s</small>"
#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:408
#: deluge/ui/console/widgets/statusbars.py:121
#: deluge/ui/web/js/deluge-all/Statusbar.js:358
msgid "n/a"
-msgstr ""
+msgstr "н/д"
#: deluge/ui/gtk3/statusbar.py:220
msgid "<b><small>Port Issue</small></b>"
-msgstr ""
+msgstr "<b><small>Праблема з портам</small></b>"
#: deluge/ui/gtk3/statusbar.py:222
msgid "No incoming connections, check port forwarding"
-msgstr ""
+msgstr "Няма ўваходзячых злучэнняў, праверце порт"
#: deluge/ui/gtk3/statusbar.py:475 deluge/ui/gtk3/systemtray.py:394
#: deluge/ui/gtk3/menubar.py:447
msgid "Download Speed Limit"
-msgstr ""
+msgstr "Ліміт хуткасці спампоўкі"
#: deluge/ui/gtk3/statusbar.py:476 deluge/ui/gtk3/systemtray.py:395
#: deluge/ui/gtk3/menubar.py:448
msgid "Set the maximum download speed"
-msgstr ""
+msgstr "Усталяваць максімум хуткасці спампоўкі"
#: deluge/ui/gtk3/statusbar.py:482 deluge/ui/gtk3/systemtray.py:409
#: deluge/ui/gtk3/menubar.py:453
msgid "Upload Speed Limit"
-msgstr ""
+msgstr "Ліміт хуткасці раздачы"
#: deluge/ui/gtk3/statusbar.py:483 deluge/ui/gtk3/systemtray.py:410
#: deluge/ui/gtk3/menubar.py:454
msgid "Set the maximum upload speed"
-msgstr ""
+msgstr "Усталяваць максімум хуткасці раздачы"
#: deluge/ui/gtk3/statusbar.py:489 deluge/ui/gtk3/menubar.py:459
msgid "Incoming Connections"
-msgstr ""
+msgstr "Уваходныя злучэнні"
#: deluge/ui/gtk3/statusbar.py:490 deluge/ui/gtk3/menubar.py:460
msgid "Set the maximum incoming connections"
-msgstr ""
+msgstr "Усталяваць максімум уваходных злучэнняў"
#: deluge/ui/gtk3/tab_data_funcs.py:28
#, python-brace-format
msgid "{state} {percent}%"
-msgstr ""
+msgstr "{state} {percent}%"
#: deluge/ui/gtk3/tab_data_funcs.py:30
#, python-brace-format
msgid "{state}: {err_msg}"
-msgstr ""
+msgstr "{state}: {err_msg}"
#: deluge/ui/gtk3/tab_data_funcs.py:42
#: deluge/ui/gtk3/torrentview_data_funcs.py:284
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:74
msgid "Never"
-msgstr ""
+msgstr "Ніколі"
#: deluge/ui/gtk3/tab_data_funcs.py:96
msgid "Yes"
-msgstr ""
+msgstr "Так"
#: deluge/ui/gtk3/tab_data_funcs.py:96
msgid "No"
-msgstr ""
+msgstr "Не"
#: deluge/ui/gtk3/files_tab.py:140
#: deluge/ui/web/js/deluge-all/details/FilesTab.js:48
@@ -2071,17 +2103,17 @@ msgstr "Прыярытэт"
#: deluge/ui/gtk3/torrentdetails.py:142
msgid "_All"
-msgstr ""
+msgstr "_Усе"
#: deluge/ui/gtk3/torrentdetails.py:143
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:15
msgid "_Status"
-msgstr "_Стан"
+msgstr "_Статус"
#: deluge/ui/gtk3/torrentdetails.py:144
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:26
msgid "_Details"
-msgstr "_Падрабязнасьці"
+msgstr "Паз_драбязнасці"
#: deluge/ui/gtk3/torrentdetails.py:145
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:27
@@ -2092,22 +2124,22 @@ msgstr "_Файлы"
#: deluge/ui/gtk3/torrentdetails.py:146
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:28
msgid "_Peers"
-msgstr "Піры"
+msgstr "_Піры"
#: deluge/ui/gtk3/torrentdetails.py:147
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:45
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:36
msgid "_Options"
-msgstr "_Налады"
+msgstr "_Опцыі"
#: deluge/ui/gtk3/torrentdetails.py:148
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:52
msgid "_Trackers"
-msgstr ""
+msgstr "_Трэкеры"
#: deluge/ui/gtk3/systemtray.py:184
msgid "Not Connected..."
-msgstr ""
+msgstr "Не злучана..."
#: deluge/ui/gtk3/systemtray.py:235 deluge/ui/gtk3/systemtray.py:239
#: deluge/ui/web/js/deluge-all/Statusbar.js:73
@@ -2118,7 +2150,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/Menus.js:199
#: deluge/ui/web/js/deluge-all/Menus.js:244
msgid "Unlimited"
-msgstr "Без абмежаванняў"
+msgstr "Неабмежавана"
#: deluge/ui/gtk3/systemtray.py:245
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:39
@@ -2127,7 +2159,7 @@ msgstr "Без абмежаванняў"
#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:85
#: deluge/ui/web/js/deluge-all/Menus.js:298
msgid "Down"
-msgstr "Запампоўка"
+msgstr "Спампоўка"
#: deluge/ui/gtk3/systemtray.py:248
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:37
@@ -2144,97 +2176,106 @@ msgid ""
"A Deluge daemon (deluged) is already running.\n"
"To use Standalone mode, stop local daemon and restart Deluge."
msgstr ""
+"Дэман Deluge (deluged) ужо запушчаны.\n"
+"Каб перайсці ў аўтаномны рэжым, спыніце лакальны дэман і перазапусціце "
+"Deluge."
#: 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 ""
+"Даступны толькі тонкі кліент, таму што libtorrent не ўсталяваны.\n"
+"Каб перайсці ў аўтаномны рэжым, усталюйце пакет libtorrent."
#: 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 ""
+"Даступны толькі тонкі кліент з-за невядомай памылкі імпарту.\n"
+"Каб перайсці ў аўтаномны рэжым, паглядзіце лог з памылкамі."
#: deluge/ui/gtk3/gtkui.py:349
msgid "Continue in Thin Client mode?"
-msgstr ""
+msgstr "Працягнуць у рэжыме тонкага кліента?"
#: deluge/ui/gtk3/gtkui.py:350
msgid "Change User Interface Mode"
-msgstr ""
+msgstr "Змяніць рэжым інтэрейса карыстальніка"
#: deluge/ui/gtk3/connectionmanager.py:52
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:56
msgid "Offline"
-msgstr "Ня ў сеціве"
+msgstr "Па-за сеткай"
#: deluge/ui/gtk3/connectionmanager.py:53
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:53
msgid "Online"
-msgstr "У сеціве"
+msgstr "У сетцы"
#: deluge/ui/gtk3/connectionmanager.py:54
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:59
msgid "Connected"
-msgstr "Падключаны"
+msgstr "Злучана"
#: deluge/ui/gtk3/connectionmanager.py:110
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:176
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:48
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:17
msgid "Status"
-msgstr ""
+msgstr "Статус"
#: deluge/ui/gtk3/connectionmanager.py:115
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:66
msgid "Host"
-msgstr ""
+msgstr "Хост"
#: deluge/ui/gtk3/connectionmanager.py:122
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:73
msgid "Version"
-msgstr ""
+msgstr "Версія"
#: deluge/ui/gtk3/connectionmanager.py:219
#: deluge/ui/gtk3/glade/connection_manager.ui.h:8
msgid "_Start Daemon"
-msgstr ""
+msgstr "_Запусціць дэман"
#: deluge/ui/gtk3/connectionmanager.py:250
msgid "_Stop Daemon"
-msgstr ""
+msgstr "_Спыніць дэман"
#: deluge/ui/gtk3/connectionmanager.py:255
msgid "_Disconnect"
-msgstr ""
+msgstr "_Адлучыць"
#: deluge/ui/gtk3/connectionmanager.py:280
msgid "Unable to start daemon!"
-msgstr ""
+msgstr "Немагчыма запусціць дэман!"
#: deluge/ui/gtk3/connectionmanager.py:281
msgid "Check deluged package is installed and logs for further details"
-msgstr ""
+msgstr "Праверце ўсталяванне пакета і логі для падрабязнасцей"
#: deluge/ui/gtk3/connectionmanager.py:332
msgid "Incompatible Client"
-msgstr ""
+msgstr "Несумяшчальны кліент"
#: deluge/ui/gtk3/connectionmanager.py:343
msgid ""
"Auto-starting the daemon locally is not enabled. See \"Options\" on the "
"\"Connection Manager\"."
msgstr ""
+"Лакальны аўтазапуск дэмана не ўключаны. Глядзіце \"Опцыі\" ў \"Кіраванні "
+"злучэннямі\"."
#: deluge/ui/gtk3/connectionmanager.py:346
msgid "Failed To Connect"
-msgstr ""
+msgstr "Памылка злучэння"
#: deluge/ui/gtk3/connectionmanager.py:403
msgid "Edit Host"
-msgstr ""
+msgstr "Рэдагаваць хост"
#: deluge/ui/gtk3/connectionmanager.py:428
msgid "Error Adding Host"
@@ -2242,18 +2283,18 @@ msgstr "Збой дадання хосту"
#: deluge/ui/gtk3/connectionmanager.py:464
msgid "Error Updating Host"
-msgstr ""
+msgstr "Памылка рэдагавання хосту"
#: deluge/ui/gtk3/preferences.py:131
#: deluge/ui/console/cmdline/commands/connect.py:33
#: deluge/ui/console/modes/preferences/preference_panes.py:651
msgid "Username"
-msgstr ""
+msgstr "Імя карыстальніка"
#: deluge/ui/gtk3/preferences.py:135
#: deluge/ui/console/modes/preferences/preference_panes.py:399
msgid "Level"
-msgstr ""
+msgstr "Узровень"
#: deluge/ui/gtk3/preferences.py:159
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:2
@@ -2264,106 +2305,106 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:67
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:71
msgid "Enabled"
-msgstr "Уключаны"
+msgstr "Уключана"
#: deluge/ui/gtk3/preferences.py:162
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:87
msgid "Plugin"
-msgstr "Модуль"
+msgstr "Плагін"
#: deluge/ui/gtk3/preferences.py:876 deluge/ui/gtk3/preferences.py:886
msgid "Attention"
-msgstr ""
+msgstr "Увага"
#: deluge/ui/gtk3/preferences.py:876
msgid "You must choose a language"
-msgstr ""
+msgstr "Вы павінны выбраць мову"
#: deluge/ui/gtk3/preferences.py:887
msgid "You must now restart the deluge UI for the changes to take effect."
-msgstr ""
+msgstr "Вы павінны перазапусціць UI праграмы каб прымяніць змены."
#: deluge/ui/gtk3/preferences.py:940
msgid "Thinclient"
-msgstr ""
+msgstr "Тонкі кліент"
#: deluge/ui/gtk3/preferences.py:940
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:18
msgid "Standalone"
-msgstr ""
+msgstr "Аўтаномны"
#: deluge/ui/gtk3/preferences.py:942
msgid "Switching Deluge Client Mode..."
-msgstr ""
+msgstr "Пераключэнне рэжыму кліента Deluge..."
#: deluge/ui/gtk3/preferences.py:943
#, python-format
msgid "Do you want to restart to use %s mode?"
-msgstr ""
+msgstr "Жадаеце перазапусціць у рэжыме %s?"
#: deluge/ui/gtk3/preferences.py:1154
msgid "Select the Plugin"
-msgstr "Выберыце модуль"
+msgstr "Выберыце плагін"
#: deluge/ui/gtk3/preferences.py:1170
msgid "Plugin Eggs"
-msgstr "Модулі Egg"
+msgstr "Плагін Egg"
#: deluge/ui/gtk3/preferences.py:1297
msgid "Server Side Error"
-msgstr ""
+msgstr "Памылка сервера"
#: deluge/ui/gtk3/preferences.py:1298
msgid "An error occurred on the server"
-msgstr ""
+msgstr "Памылка на серверы"
#: deluge/ui/gtk3/preferences.py:1368 deluge/ui/gtk3/preferences.py:1375
msgid "Error Adding Account"
-msgstr ""
+msgstr "Памылка дадання акаўнта"
#: deluge/ui/gtk3/preferences.py:1369
msgid "Authentication failed"
-msgstr ""
+msgstr "Памылка аўтэнтыфікацыі"
#: deluge/ui/gtk3/preferences.py:1376
msgid "An error occurred while adding account"
-msgstr ""
+msgstr "Памылка падчас дадання акаўнта"
#: deluge/ui/gtk3/preferences.py:1408
msgid "Error Updating Account"
-msgstr ""
+msgstr "Памылка абнаўлення акаўнта"
#: deluge/ui/gtk3/preferences.py:1409
msgid "An error occurred while updating account"
-msgstr ""
+msgstr "Памылка падчас абнаўлення акаўнта"
#: deluge/ui/gtk3/preferences.py:1427
msgid "Remove Account"
-msgstr ""
+msgstr "Выдаліць акаўнт"
#: deluge/ui/gtk3/preferences.py:1429
#, python-format
msgid ""
"Are you sure you want to remove the account with the username "
"\"%(username)s\"?"
-msgstr ""
+msgstr "Сапраўды жадаеце выдаліць акаўнт з іменем \"%(username)s\"?"
#: deluge/ui/gtk3/preferences.py:1441 deluge/ui/gtk3/preferences.py:1448
msgid "Error Removing Account"
-msgstr ""
+msgstr "Памылка выдаляння акаўнта"
#: deluge/ui/gtk3/preferences.py:1442
msgid "Auhentication failed"
-msgstr ""
+msgstr "Памылка аўтэнтыфікацыі"
#: deluge/ui/gtk3/preferences.py:1449
msgid "An error occurred while removing account"
-msgstr ""
+msgstr "Памылка падчас выдалення акаўнта"
#: deluge/ui/gtk3/filtertreeview.py:122
#: deluge/ui/web/js/deluge-all/FilterPanel.js:28
msgid "States"
-msgstr ""
+msgstr "Станы"
#: deluge/ui/gtk3/filtertreeview.py:128
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:23
@@ -2376,29 +2417,29 @@ msgstr "Трэкеры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:7
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:33
msgid "None"
-msgstr "Адсутнічае"
+msgstr "Няма"
#: deluge/ui/gtk3/filtertreeview.py:137
msgid "Admin"
-msgstr ""
+msgstr "Адмін"
#: deluge/ui/gtk3/filtertreeview.py:159
#: deluge/ui/web/js/deluge-all/FilterPanel.js:34
msgid "Labels"
-msgstr ""
+msgstr "Ярлыкі"
#: deluge/ui/gtk3/filtertreeview.py:204
#: deluge/plugins/Label/deluge_label/gtkui/submenu.py:28
msgid "No Label"
-msgstr ""
+msgstr "Без ярлыка"
#: deluge/ui/gtk3/filtertreeview.py:206
msgid "No Owner"
-msgstr ""
+msgstr "Няма ўласніка"
#: deluge/ui/gtk3/new_release_dialog.py:60
msgid "<i>Client Version</i>"
-msgstr ""
+msgstr "<i>Версія кліента</i>"
#: deluge/ui/gtk3/queuedtorrents.py:118
msgid " Torrents Queued"
@@ -2406,20 +2447,20 @@ msgstr " Торэнты пастаўлены ў чаргу"
#: deluge/ui/gtk3/queuedtorrents.py:120
msgid " Torrent Queued"
-msgstr " Тарэнт пастаўлены ў чаргу"
+msgstr " Торэнт пастаўлены ў чаргу"
#: deluge/ui/gtk3/torrentview.py:421
msgid "Torrent is shared between other Deluge users or not."
-msgstr ""
+msgstr "Торэнт агульны ці не з іншымі карыстальнікамі Deluge."
#: deluge/ui/gtk3/removetorrentdialog.py:67
msgid "Remove the selected torrents?"
-msgstr ""
+msgstr "Выдаліць вылучаныя торэнты?"
#: deluge/ui/gtk3/removetorrentdialog.py:68
#, python-format
msgid "Total of %s torrents selected"
-msgstr ""
+msgstr "Усяго вылучана торэнтаў: %s"
#: deluge/ui/gtk3/menubar.py:79
msgid "Set Unlimited"
@@ -2427,39 +2468,39 @@ msgstr "Неабмежавана"
#: deluge/ui/gtk3/menubar.py:91 deluge/ui/web/js/deluge-all/Menus.js:259
msgid "On"
-msgstr "Укл."
+msgstr "Уключана"
#: deluge/ui/gtk3/menubar.py:94 deluge/ui/web/js/deluge-all/Menus.js:265
msgid "Off"
-msgstr "Адкл."
+msgstr "Выключана"
#: deluge/ui/gtk3/menubar.py:101
msgid "Disable"
-msgstr ""
+msgstr "Выключыць"
#: deluge/ui/gtk3/menubar.py:104
msgid "Enable..."
-msgstr ""
+msgstr "Уключыць..."
#: deluge/ui/gtk3/menubar.py:465
msgid "Peer Upload Slots"
-msgstr ""
+msgstr "Слоты піраў раздачы"
#: deluge/ui/gtk3/menubar.py:466
msgid "Set the maximum upload slots"
-msgstr ""
+msgstr "Усталяваць максімум слотаў раздачы"
#: deluge/ui/gtk3/menubar.py:471
msgid "Stop Seed At Ratio"
-msgstr ""
+msgstr "Спыніць сіды па рэйтынгу"
#: deluge/ui/gtk3/menubar.py:606
msgid "Ownership Change Error"
-msgstr ""
+msgstr "Памылка змены ўласніка"
#: deluge/ui/gtk3/menubar.py:607
msgid "There was an error while trying changing ownership."
-msgstr ""
+msgstr "Памылка падчас спробы змяніць ўласніка."
#: deluge/ui/gtk3/peers_tab.py:91
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:66
@@ -2473,13 +2514,15 @@ msgstr "Кліент"
#: deluge/ui/gtk3/__init__.py:29
msgid "GTK Options"
-msgstr ""
+msgstr "Опцыі GTK"
#: deluge/ui/gtk3/__init__.py:36
msgid ""
"Add one or more torrent files, torrent URLs or magnet URIs to a currently "
"running Deluge GTK instance"
msgstr ""
+"Дадаць адзін ці больш торэнт-файлаў, URL-аў ці URI-спасылак у запушчаны "
+"цяпер Deluge GTK"
#: deluge/ui/gtk3/glade/create_torrent_dialog.progress.ui.h:1
msgid "Creating Torrent"
@@ -2491,7 +2534,7 @@ msgstr "Торэнты ў чарзе"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:4
msgid "Add Queued Torrents"
-msgstr ""
+msgstr "Дадаць торэнты ў чарзе"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:5
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:7
@@ -2503,7 +2546,7 @@ msgstr "_Выдаліць"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:6
msgid "_Clear"
-msgstr ""
+msgstr "А_чысціць"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:7
msgid "Automatically add torrents on connect"
@@ -2526,7 +2569,7 @@ msgstr "_Стварыць торэнт"
#: deluge/ui/gtk3/glade/main_window.ui.h:4
msgid "Quit & _Shutdown Daemon"
-msgstr ""
+msgstr "Выйсці і вы_ключыць дэман"
#: deluge/ui/gtk3/glade/main_window.ui.h:5
#: deluge/ui/gtk3/glade/tray_menu.ui.h:8
@@ -2538,11 +2581,11 @@ msgstr "В_ыхад"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:6
#: deluge/ui/gtk3/glade/connection_manager.ui.h:5
msgid "_Edit"
-msgstr "_Змяніць"
+msgstr "_Рэдагаваць"
#: deluge/ui/gtk3/glade/main_window.ui.h:7
msgid "_Preferences"
-msgstr ""
+msgstr "_Налады"
#: deluge/ui/gtk3/glade/main_window.ui.h:8
msgid "_Connection Manager"
@@ -2558,7 +2601,7 @@ msgstr "_Выгляд"
#: deluge/ui/gtk3/glade/main_window.ui.h:11
msgid "_Toolbar"
-msgstr "Панэль _прыладаў"
+msgstr "Панэль _прылад"
#: deluge/ui/gtk3/glade/main_window.ui.h:12
msgid "_Sidebar"
@@ -2578,7 +2621,7 @@ msgstr "_Калонкі"
#: deluge/ui/gtk3/glade/main_window.ui.h:16
msgid "_Find ..."
-msgstr ""
+msgstr "_Знайсці ..."
#: deluge/ui/gtk3/glade/main_window.ui.h:17
msgid "S_idebar"
@@ -2594,7 +2637,7 @@ msgstr "Паказваць _трэкеры"
#: deluge/ui/gtk3/glade/main_window.ui.h:20
msgid "Show _Owners"
-msgstr ""
+msgstr "Паказваць у_ласнікаў"
#: deluge/ui/gtk3/glade/main_window.ui.h:21
msgid "_Help"
@@ -2602,11 +2645,11 @@ msgstr "_Даведка"
#: deluge/ui/gtk3/glade/main_window.ui.h:22
msgid "_Homepage"
-msgstr ""
+msgstr "_Хатняя старонка"
#: deluge/ui/gtk3/glade/main_window.ui.h:23
msgid "_FAQ"
-msgstr ""
+msgstr "_Частыя пытанні"
#: deluge/ui/gtk3/glade/main_window.ui.h:24
msgid "Frequently Asked Questions"
@@ -2614,11 +2657,11 @@ msgstr "Частыя пытанні"
#: deluge/ui/gtk3/glade/main_window.ui.h:25
msgid "_Community"
-msgstr ""
+msgstr "_Супольнасць"
#: deluge/ui/gtk3/glade/main_window.ui.h:26
msgid "_About"
-msgstr ""
+msgstr "_Аб праграме"
#: deluge/ui/gtk3/glade/main_window.ui.h:27
msgid "Add torrent"
@@ -2631,7 +2674,7 @@ msgstr "Дадаць торэнт"
#: deluge/ui/gtk3/glade/main_window.ui.h:29
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:221
msgid "Remove torrent"
-msgstr ""
+msgstr "Выдаліць торэнт"
#: deluge/ui/gtk3/glade/main_window.ui.h:30
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:105
@@ -2647,10 +2690,12 @@ msgid ""
"Filter torrents by name.\n"
"This will filter torrents for the current selection on the sidebar."
msgstr ""
+"Фільтраваць торэнты па імені.\n"
+"Гэта адфільтруе торэнты паводле бягучага адбора на бакавой панэлі."
#: deluge/ui/gtk3/glade/main_window.ui.h:33
msgid "Filter"
-msgstr ""
+msgstr "Фільтр"
#: deluge/ui/gtk3/glade/main_window.ui.h:34
msgid "Pause the selected torrents"
@@ -2660,7 +2705,7 @@ msgstr "Прыпыніць вылучаныя торэнты"
#: deluge/ui/web/js/deluge-all/Toolbar.js:54
#: deluge/ui/web/js/deluge-all/Menus.js:52
msgid "Pause"
-msgstr "Паўза"
+msgstr "Прыпыніць"
#: deluge/ui/gtk3/glade/main_window.ui.h:36
msgid "Resume the selected torrents"
@@ -2674,7 +2719,7 @@ msgstr "Працягнуць"
#: deluge/ui/gtk3/glade/main_window.ui.h:38
msgid "Queue Torrent Up"
-msgstr "Перанесьці торэнт уверх"
+msgstr "Перанесці торэнт уверх"
#: deluge/ui/gtk3/glade/main_window.ui.h:39
msgid "Queue Up"
@@ -2682,7 +2727,7 @@ msgstr "Вышэй"
#: deluge/ui/gtk3/glade/main_window.ui.h:40
msgid "Queue Torrent Down"
-msgstr "Перанесьці торэнт уніз"
+msgstr "Перанесці торэнт уніз"
#: deluge/ui/gtk3/glade/main_window.ui.h:41
msgid "Queue Down"
@@ -2693,14 +2738,14 @@ msgstr "Ніжэй"
#: deluge/ui/web/js/deluge-all/Toolbar.js:84
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:24
msgid "Preferences"
-msgstr "Налады"
+msgstr "Параметры"
#: deluge/ui/gtk3/glade/main_window.ui.h:43
#: deluge/ui/gtk3/glade/connection_manager.ui.h:1
#: deluge/ui/web/js/deluge-all/Toolbar.js:91
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:21
msgid "Connection Manager"
-msgstr "Кіраванне злучэньнямі"
+msgstr "Кіраванне злучэннямі"
#: deluge/ui/gtk3/glade/main_window.ui.h:44
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:2
@@ -2711,19 +2756,19 @@ msgstr "Кіраванне злучэньнямі"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:211
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:86
msgid "Close"
-msgstr ""
+msgstr "Закрыць"
#: deluge/ui/gtk3/glade/main_window.ui.h:45
msgid "Filter:"
-msgstr ""
+msgstr "Фільтр:"
#: deluge/ui/gtk3/glade/main_window.ui.h:46
msgid "Clear the search"
-msgstr ""
+msgstr "Ачысціць пошук"
#: deluge/ui/gtk3/glade/main_window.ui.h:47
msgid "_Match Case"
-msgstr ""
+msgstr "_Улічваць рэгістр літар"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:1
#: deluge/ui/console/modes/preferences/preference_panes.py:383
@@ -2731,7 +2776,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:45
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:66
msgid "Forced"
-msgstr ""
+msgstr "Прымусова"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:3
#: deluge/ui/console/modes/preferences/preference_panes.py:383
@@ -2739,71 +2784,71 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:47
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:68
msgid "Disabled"
-msgstr ""
+msgstr "Адключана"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:4
#: deluge/ui/console/modes/preferences/preference_panes.py:400
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:87
msgid "Handshake"
-msgstr ""
+msgstr "Рукапацісканне"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:5
#: deluge/ui/console/modes/preferences/preference_panes.py:400
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:88
msgid "Full Stream"
-msgstr ""
+msgstr "Поўная плынь"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:6
#: deluge/ui/console/modes/preferences/preference_panes.py:400
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:89
msgid "Either"
-msgstr ""
+msgstr "Любы"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:8
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:34
msgid "Socks4"
-msgstr ""
+msgstr "Socks4"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:9
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:35
msgid "Socks5"
-msgstr ""
+msgstr "Socks5"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:10
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:36
msgid "Socks5 Auth"
-msgstr ""
+msgstr "Socks5 Auth"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:11
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:37
msgid "HTTP"
-msgstr ""
+msgstr "HTTP"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:12
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:38
msgid "HTTP Auth"
-msgstr ""
+msgstr "HTTP Auth"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:13
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:39
msgid "I2P"
-msgstr ""
+msgstr "I2P"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:19
msgid "The standalone self-contained application"
-msgstr ""
+msgstr "Аўтаномная самастойная праграма"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:20
msgid "Thin Client"
-msgstr ""
+msgstr "Тонкі кліент"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:21
msgid "Connect to a Deluge daemon (deluged)"
-msgstr ""
+msgstr "Злучыцца з дэманам Deluge (deluged)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:22
msgid "Application Mode"
-msgstr ""
+msgstr "Рэжым праграмы"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:46
@@ -2812,7 +2857,7 @@ msgstr "Паказваць хуткасць у загалоўку праграм
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:24
msgid "Focus window when adding torrent"
-msgstr ""
+msgstr "Факусіраваць на акне пры даданні торэнта"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:25
msgid ""
@@ -2820,45 +2865,48 @@ msgid ""
"will increase bandwidth use between client\n"
"and daemon (does not apply in Standalone mode)."
msgstr ""
+"Паласа паказу частак\n"
+"павялічыць прапускную здольнасць паміж кліентам\n"
+"і дэманам (не дзейнічае ў аўтаномным рэжыме)."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:28
msgid "Show a pieces bar in Status tab"
-msgstr ""
+msgstr "Паказваць паласу частак на панэлі стану"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:29
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:25
#: deluge/ui/web/render/tab_status.html:27
msgid "Completed:"
-msgstr ""
+msgstr "Завершана:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:30
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:72
msgid "Downloading:"
-msgstr ""
+msgstr "Спампоўка:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:31
msgid "Waiting:"
-msgstr ""
+msgstr "Чаканне:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:32
msgid "Missing:"
-msgstr ""
+msgstr "Згублена:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:33
msgid "_Revert"
-msgstr ""
+msgstr "_Вярнуць"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:34
msgid "Revert color to default"
-msgstr ""
+msgstr "Вярнуць зыходны колер"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:35
msgid "Piece Colors"
-msgstr ""
+msgstr "Колеры частак"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:36
msgid "Main Window"
-msgstr ""
+msgstr "Галоўнае акно"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:37
msgid "Enable system tray icon"
@@ -2866,11 +2914,11 @@ msgstr "Уключыць значок у сістэмным трэі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:38
msgid "App Indicator"
-msgstr ""
+msgstr "Індыкатар праграмы"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:39
msgid "Systray"
-msgstr ""
+msgstr "Сістэмны трэй"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:40
msgid "Minimize to tray on close"
@@ -2886,25 +2934,25 @@ msgstr "Абараніць паролем вобласць апавяшчэнн
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:44
msgid "System Tray"
-msgstr ""
+msgstr "Сістэмны трэй"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:45
msgid "Notify about new releases"
-msgstr ""
+msgstr "Апавяшчаць аб новых рэлізах"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:46
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:38
msgid "Updates"
-msgstr ""
+msgstr "Абнаўленні"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:47
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:235
msgid "System Default"
-msgstr ""
+msgstr "Сістэмныя налады па змаўчанні"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:48
msgid "<b>Language</b>"
-msgstr ""
+msgstr "<b>Мова</b>"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:49
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:16
@@ -2922,36 +2970,36 @@ msgstr "Капіяваць файлы .torrent ў:"
#: deluge/ui/console/modes/preferences/preference_panes.py:275
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:19
msgid "Delete copy of torrent file on remove"
-msgstr ""
+msgstr "Выдаляць копіі торэнт-файлаў"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:52
msgid ""
"Delete the copy of the torrent file created when the torrent is removed"
-msgstr ""
+msgstr "Выдаляць копію торэнт-файла пры выдаленні торэнта"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:53
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:53
msgid "Download to:"
-msgstr "Запампоўваць у:"
+msgstr "Спампоўваць у:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:54
msgid "Download Folders"
-msgstr ""
+msgstr "Папкі спампоўкі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:55
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:93
msgid "Prioritize first and last pieces of torrent"
-msgstr "Прыярытэт у першай і апошняй частак торэнта"
+msgstr "Прыярытэт першай і апошняй частак торэнта"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:56
msgid "Prioritize first and last pieces of files in torrent"
-msgstr "Прыярытэт у першай і апошняй частак файлаў торэнта"
+msgstr "Прыярытэт першай і апошняй частак файлаў у торэнце"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:57
#: deluge/ui/console/modes/preferences/preference_panes.py:287
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:102
msgid "Sequential download"
-msgstr ""
+msgstr "Паслядоўная спампоўка"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:58
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:15
@@ -2963,6 +3011,12 @@ msgid ""
"distribution negatively in the swarm. It should be\n"
"used sparingly."
msgstr ""
+"Калі ўключана, чаткі будуць падбірацца\n"
+"паслядоўна замест \"спачатку самы рэдкі\".\n"
+"\n"
+"Уключэнне паслядоўнай спампоўкі ў цэлым адмоўна\n"
+"на атрыманне частак падзейнічае. Трэба\n"
+"выкарыстоўваць ашчадна."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:64
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:111
@@ -2972,15 +3026,15 @@ msgstr "Дадаваць торэнты ў спыненым стане"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:65
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:120
msgid "Pre-allocate disk space"
-msgstr ""
+msgstr "Рэзерваваць месца на дыску"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:66
msgid "Pre-allocate the disk space for the torrent files"
-msgstr ""
+msgstr "Рэзерваваць месца на дыску для торэнт-файлаў"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:67
msgid "Add Torrent Options"
-msgstr ""
+msgstr "Опцыі дадання торэнта"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:68
msgid "Always show"
@@ -2992,15 +3046,15 @@ msgstr "Зрабіць дыялогавае акно актыўным"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:70
msgid "Add Torrents Dialog"
-msgstr ""
+msgstr "Дыялог дадання торэнта"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:71
msgid "Connection Attempts per Second:"
-msgstr ""
+msgstr "Спробы злучыцца ў секунду:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:72
msgid "Half-Open Connections:"
-msgstr ""
+msgstr "Паў-адкрытае злучэнне:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:73
msgid "The maximum number of connections allowed. Set -1 for unlimited."
@@ -3025,24 +3079,24 @@ msgstr ""
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:32
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:5
msgid "Upload Slots:"
-msgstr ""
+msgstr "Слоты раздачы:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:77
msgid "The maximum download speed for all torrents. Set -1 for unlimited."
msgstr ""
-"Максімальная хуткасць прыёму для ўсіх торэнтаў. Для неабмежаванай усталюйце -"
-"1."
+"Максімальная хуткасць спампоўкі для ўсіх торэнтаў. Для неабмежаванай "
+"усталюйце -1."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:78
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:41
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:7
msgid "Download Speed:"
-msgstr "Хуткасць запампоўкі:"
+msgstr "Хуткасць спампоўкі:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:79
msgid "The maximum upload speed for all torrents. Set -1 for unlimited."
msgstr ""
-"Максімальная хуткасць запампоўкі для ўсіх торэнтаў. Для неабмежаванай "
+"Максімальная хуткасць спампоўкі для ўсіх торэнтаў. Для неабмежаванай "
"усталюйце -1."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:81
@@ -3072,7 +3126,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:85
msgid "Global Bandwidth Limits"
-msgstr ""
+msgstr "Глабальныя ліміты прапускной здольнасці"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:86
msgid "The maximum upload slots per torrent. Set -1 for unlimited."
@@ -3088,62 +3142,66 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:88
msgid "The maximum number download speed per torrent. Set -1 for unlimited."
msgstr ""
+"Максімальная хуткасць спампоўкі на торэнт. Для неабмежаванай усталюйце -1."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:89
msgid "The maximum upload speed per torrent. Set -1 for unlimited."
msgstr ""
+"Максімальная хуткасць раздачы на торэнт. Для неабмежаванай усталюйце -1."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:90
msgid "Per-Torrent Bandwidth Limits"
-msgstr ""
+msgstr "Ліміты прапускной здольнасці на торэнт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:91
#: deluge/ui/console/modes/preferences/preference_panes.py:556
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:42
msgid "Queue to top"
-msgstr ""
+msgstr "Падняць у чарзе"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:92
#: deluge/ui/console/modes/preferences/preference_panes.py:554
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:30
msgid "New Torrents"
-msgstr ""
+msgstr "Новыя торэнты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:93
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:85
msgid "Seeding:"
-msgstr ""
+msgstr "Сідаванне:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:94
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:59
msgid "Total:"
-msgstr ""
+msgstr "Усяго:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:95
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:102
msgid "Ignore slow torrents"
-msgstr ""
+msgstr "Ігнараваць марудныя торэнты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:96
msgid ""
"Torrents not transfering any data do not count towards download/seeding "
"active count."
msgstr ""
+"Торэнты без перадачы даных не ўлічваюцца пры падліку актыўных "
+"спамповак/сідавання."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:97
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:111
msgid "Prefer seeding torrents"
-msgstr ""
+msgstr "Пераважнае сідаванне торэнтаў"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:98
msgid "Give preference to seeding torrents over downloading torrents."
-msgstr ""
+msgstr "Аддайваць перавагу сідаванню торэнтаў над спампоўкай."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:99
#: deluge/ui/console/modes/preferences/preference_panes.py:558
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:50
msgid "Active Torrents"
-msgstr ""
+msgstr "Актыўныя торэнты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:100
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:7
@@ -3151,52 +3209,54 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:187
#: deluge/ui/web/render/tab_status.html:4
msgid "Share Ratio:"
-msgstr ""
+msgstr "Суадносіны дзялення:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:101
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:142
msgid "Time Ratio:"
-msgstr ""
+msgstr "Суадносіны часу:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:102
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:157
msgid "Time (m):"
-msgstr ""
+msgstr "Час (хв):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:103
#: deluge/ui/console/modes/preferences/preference_panes.py:590
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:118
msgid "Seeding Rotation"
-msgstr ""
+msgstr "Змена сідавання"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:104
msgid "Pause Torrent"
-msgstr ""
+msgstr "Прыпыніць торэнт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:106
#: deluge/ui/console/modes/preferences/preference_panes.py:627
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:173
msgid "Share Ratio Reached"
-msgstr ""
+msgstr "Дасягнуты суадносіны дзялення"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:107
msgid ""
"The IP address of the interface to listen for incoming bittorrent "
"connections on. Leave this empty if you want to use the default."
msgstr ""
+"IP адрас інтэрфейса для праслухоўвання ўваходзячых злучэнняў bittorrent. "
+"Пакіньце пустым каб выкарыстоўваць значэнне па змаўчанні."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:108
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:38
msgid "Incoming Address"
-msgstr ""
+msgstr "Уваходзячы адрас"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:109
msgid "Random"
-msgstr ""
+msgstr "Выпадковы"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:110
msgid "Uses random ports in range 49152 to 65525"
-msgstr ""
+msgstr "Выкарыстоўвае выпадковы порт паміж 49152 і 65525"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:111
msgid "Active Port:"
@@ -3209,7 +3269,7 @@ msgstr "Праверыць актыўны порт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:113
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:58
msgid "Incoming Port"
-msgstr ""
+msgstr "Уваходзячы порт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:114
msgid ""
@@ -3218,12 +3278,16 @@ msgid ""
"connections. (Leave empty for default.)\n"
" "
msgstr ""
+"\n"
+"Сеткавы інтэрфейс ці IP адрас для зыходзячых злучэнняў BitTorrent. (Пакіньце "
+"пустым каб выкарыстоўваць перадвызначаны.)\n"
+" "
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:117
#: deluge/ui/console/modes/preferences/preference_panes.py:359
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:101
msgid "Outgoing Interface"
-msgstr ""
+msgstr "Зыходзячы інтэрфейс"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:118
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:11
@@ -3240,17 +3304,17 @@ msgstr "Да:"
#: deluge/ui/console/modes/preferences/preference_panes.py:328
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:120
msgid "Outgoing Ports"
-msgstr ""
+msgstr "Зыходзячыя парты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:121
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:59
msgid "Outgoing:"
-msgstr ""
+msgstr "Зыходзячы:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:122
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:38
msgid "Incoming:"
-msgstr ""
+msgstr "Уваходзячы:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:123
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:80
@@ -3261,7 +3325,7 @@ msgstr "Узровень:"
#: deluge/ui/console/modes/preferences/preference_panes.py:379
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:18
msgid "Encryption"
-msgstr ""
+msgstr "Шыфраванне"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:125
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:194
@@ -3288,7 +3352,7 @@ msgstr "Абмен пірамі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:130
msgid "Exchanges peers between clients. (Disabling requires restart)"
-msgstr ""
+msgstr "Абмен пірамі паміж кліентамі. (Адключэнне патрабуе перазапуску)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:131
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:223
@@ -3319,7 +3383,7 @@ msgstr "TOS-байт піра:"
#: deluge/ui/console/modes/preferences/preference_panes.py:372
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:181
msgid "Network Extras"
-msgstr ""
+msgstr "Сетка дадаткова"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:137
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:4
@@ -3343,68 +3407,72 @@ msgstr "Порт:"
#: deluge/ui/console/modes/preferences/preference_panes.py:658
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:94
msgid "Proxy Hostnames"
-msgstr ""
+msgstr "Назва хаста проксі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:141
msgid ""
"Hostnames should be attempted to be resolved through\n"
"the proxy instead of using the local DNS service"
msgstr ""
+"Назвы хаста трэба спрабаваць выправіць праз проксі\n"
+"замест таго, каб выкарыстоўваць лакальны сэрвіс DNS"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:143
#: deluge/ui/console/modes/preferences/preference_panes.py:661
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:103
msgid "Proxy Peers"
-msgstr ""
+msgstr "Проксі піры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:144
msgid "Proxy peer and web seed connections."
-msgstr ""
+msgstr "Злучэнне праз проксі піры і вэб сід."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:145
#: deluge/ui/console/modes/preferences/preference_panes.py:665
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:112
msgid "Proxy Trackers"
-msgstr ""
+msgstr "Проксі трэкеры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:147
msgid "Force Proxy Use"
-msgstr ""
+msgstr "Прымусовае выкарыстанне проксі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:148
#: deluge/ui/console/modes/preferences/preference_panes.py:671
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:141
msgid "Hide Client Identity"
-msgstr ""
+msgstr "Хаваць ідэнтычнасць кліента"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:149
msgid ""
"Attempt to hide client identity and only use proxy for incoming connections."
msgstr ""
+"Спрабаваць хаваць ідэнтычнасць кліента і выкарыстоўваць толькі проксі для "
+"ўваходзячых злучэнняў."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:150
#: deluge/ui/console/modes/preferences/preference_panes.py:668
#: deluge/ui/console/modes/preferences/preference_panes.py:669
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:120
msgid "Force Proxy"
-msgstr ""
+msgstr "Прымусовае проксі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:151
msgid "Cache Size (16 KiB blocks):"
-msgstr "Памер кэшу (у блоках па 16 КБ):"
+msgstr "Памер кэшу (у блоках па 16 КіБ):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:152
msgid ""
"The number of seconds from the last cached write to a piece in the write "
"cache, to when it's forcefully flushed to disk. Default is 60 seconds."
msgstr ""
-"Час (у секундах) ад апошняга кэшыраваннага запісу часткі ў кэше запісу да "
+"Час у секундах ад апошняга кэшыраваннага запісу часткі ў кэшы запісу да "
"таго, як прымусова скідаць кэш гэтай часткі на дыск. Па змаўчанні 60 секунд."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:153
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:53
msgid "Cache Expiry (seconds):"
-msgstr "Час жыцця кэшу (секунд):"
+msgstr "Час жыцця кэша (секунд):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:154
#: deluge/ui/console/modes/preferences/preference_panes.py:694
@@ -3412,7 +3480,7 @@ msgstr "Час жыцця кэшу (секунд):"
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:29
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:30
msgid "Settings"
-msgstr ""
+msgstr "Параметры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:155
msgid ""
@@ -3442,9 +3510,9 @@ msgid ""
"of saved write operations per total write operations, i.e. a kind of cache "
"hit ratio for the write cache."
msgstr ""
-"Суадноснасць (блокаў_запісана - аперацый_запісу) / блокаў_запісана "
-"прадстаўляе суадноснасць колькасці захаваных аперацый запісу да іх агульнай "
-"колькасці, г.зн. эфектыўнасць кэша запісу."
+"Суадносіны (блокаў_запісана - аперацый_запісу) / блокаў_запісана прадстаўляе "
+"суадноснасць колькасці захаваных аперацый запісу да іх агульнай колькасці, "
+"г.зн. эфектыўнасць кэша запісу."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:160
msgid "Write Cache Hit Ratio:"
@@ -3453,7 +3521,7 @@ msgstr "Працэнт траплення ў кэш:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:161
#: deluge/ui/console/modes/preferences/preference_panes.py:709
msgid "Write"
-msgstr ""
+msgstr "Запісаць"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:162
msgid ""
@@ -3461,7 +3529,7 @@ msgid ""
"peers), that were served from disk or cache."
msgstr ""
"Колькасць блокаў, запытаных рухавіком BitTorrent (ад піраў) і счытаных з "
-"дыска ці з кэшу."
+"дыска ці з кэша."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:163
msgid "Blocks Read:"
@@ -3469,42 +3537,42 @@ msgstr "Блокаў счытана:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:164
msgid "The number of blocks that were served from cache."
-msgstr "Колькасць блокаў, счытаных з кэшу."
+msgstr "Колькасць блокаў, счытаных з кэша."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:165
msgid "Blocks Read Hit:"
-msgstr "Счытана блокаў з кэшу:"
+msgstr "Счытана блокаў з кэша:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:166
msgid "The cache hit ratio for the read cache."
-msgstr "Каэфіцыент эфектыўнасці кэшу счытывання."
+msgstr "Каэфіцыент эфектыўнасці кэша счытвання."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:167
msgid "Read Cache Hit Ratio:"
-msgstr "Працэнт счытываньня з кэшу:"
+msgstr "Працэнт счытвання з кэша:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:168
msgid ""
"The total number of read operations performed since this session was started."
msgstr ""
-"Агульная колькасць аперацый счытываання, выкананых з пачатку гэтай сесіі."
+"Агульная колькасць аперацый счытвання, выкананых з пачатку гэтай сесіі."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:169
msgid "Reads:"
-msgstr "Аперацый счытывання:"
+msgstr "Аперацый счытвання:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:170
#: deluge/ui/console/modes/preferences/preference_panes.py:723
msgid "Read"
-msgstr ""
+msgstr "Чытаць"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:171
msgid ""
"The number of 16 KiB blocks currently in the disk cache. This includes both "
"read and write cache."
msgstr ""
-"Колькасць блокаў па 16 КБ, якія знаходзяцца зараз у дыскавым кэше. Уключае "
-"кэш счытывання і запісу."
+"Колькасць блокаў па 16 КБ, якія знаходзяцца зараз у дыскавым кэшы. Уключае "
+"кэш счытвання і запісу."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:172
msgid "Cache Size:"
@@ -3512,12 +3580,12 @@ msgstr "Памер кэша:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:173
msgid "Read Cache Size:"
-msgstr "Памер кэша счытывання:"
+msgstr "Памер кэша счытвання:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:175
#: deluge/ui/gtk3/glade/connection_manager.ui.h:7
msgid "_Refresh"
-msgstr ""
+msgstr "_Абнавіць"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:177
msgid ""
@@ -3536,11 +3604,11 @@ msgstr "Адсылаць ананімную статыстыку"
#: deluge/ui/console/modes/preferences/preference_panes.py:503
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:57
msgid "System Information"
-msgstr ""
+msgstr "Сістэмная інфармацыя"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:180
msgid "Location:"
-msgstr "Размяшчэнне:"
+msgstr "Месцазнаходжанне:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:181
msgid ""
@@ -3548,21 +3616,21 @@ msgid ""
"using DNS to resolve the peer's country."
msgstr ""
"Калі Deluge не зможа знайсці базу файлаў па паказаным шляху, то для "
-"вызначэння краіны пира будзе выкарыстоўвацца DNS."
+"вызначэння краіны піра будзе выкарыстоўвацца DNS."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:182
#: deluge/ui/console/modes/preferences/preference_panes.py:516
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:85
msgid "GeoIP Database"
-msgstr ""
+msgstr "База даных GeoIP"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:183
msgid "Associate with Deluge"
-msgstr ""
+msgstr "Звязаць з Deluge"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:184
msgid "Magnet Links"
-msgstr ""
+msgstr "Магнет-спасылкі"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:185
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:37
@@ -3573,7 +3641,7 @@ msgstr "Порт дэмана:"
#: deluge/ui/console/modes/preferences/preference_panes.py:655
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:30
msgid "Port"
-msgstr ""
+msgstr "Порт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:187
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:61
@@ -3591,15 +3659,15 @@ msgstr "Злучэнні"
#: deluge/ui/console/modes/preferences/preference_panes.py:543
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:80
msgid "Periodically check the website for new releases"
-msgstr "Перыядычна правяраць вэб-сайт на наяўнасць аднаўленьняў"
+msgstr "Перыядычна правяраць вэб-сайт на наяўнасць абнаўленняў"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:193
msgid "_Delete"
-msgstr ""
+msgstr "_Выдаліць"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:194
msgid "Accounts"
-msgstr ""
+msgstr "Акаўнты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:196
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:29
@@ -3629,27 +3697,27 @@ msgstr "Інфармацыя"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:201
msgid "_Install"
-msgstr ""
+msgstr "_Усталяваць"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:202
msgid "_Find More..."
-msgstr ""
+msgstr "_Знайсці болей..."
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:4
msgid "Remove the selected torrent(s)?"
-msgstr ""
+msgstr "Выдаліць вылучаныя торэнты?"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:5
msgid "Include downloaded files"
-msgstr ""
+msgstr "Уключыць спампаваныя файлы"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:6
msgid "(This is permanent!)"
-msgstr ""
+msgstr "(Гэта назаўсёды!)"
#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:1
msgid "Add Peer"
-msgstr "Дадаць піра"
+msgstr "Дадаць пір"
#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:4
msgid "hostname:port"
@@ -3657,11 +3725,11 @@ msgstr "назва_вузла:порт"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:1
msgid "Properties"
-msgstr ""
+msgstr "Уласцівасці"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:3
msgid "Max drop down rows"
-msgstr ""
+msgstr "Макс радкоў у спісе"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:4
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:2
@@ -3671,87 +3739,87 @@ msgstr "<b>Агульныя</b>"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:5
msgid "Show path entry"
-msgstr ""
+msgstr "Паказваць шлях"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:6
msgid "Show file chooser"
-msgstr ""
+msgstr "Паказваць выбар файлаў"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:7
msgid "Show folder name"
-msgstr ""
+msgstr "Паказваць назву папкі"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:8
msgid "Path Chooser Type"
-msgstr ""
+msgstr "Тып выбара шляху"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:9
msgid "Enable autocomplete"
-msgstr ""
+msgstr "Уключыць аўтазапаўненне"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:10
msgid "Show hidden files"
-msgstr ""
+msgstr "Паказваць схаваныя файлы"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:11
msgid "Set new key"
-msgstr ""
+msgstr "Усталяваць новую клавішу"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:12
msgid "Press this key to set new key accelerators to trigger auto-complete"
-msgstr ""
+msgstr "Націсніце гэту клавішу каб усталяваць яе як трыгер аўтазапаўнення"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:13
msgid "Autocomplete"
-msgstr ""
+msgstr "Аўтазапаўнення"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:14
msgid "Save path"
-msgstr ""
+msgstr "Захаваць шлях"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:15
msgid "Ctrl+S"
-msgstr ""
+msgstr "Ctrl+S"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:16
msgid "Ctrl+E"
-msgstr ""
+msgstr "Ctrl+E"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:17
msgid "Ctrl+R"
-msgstr ""
+msgstr "Ctrl+R"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:18
msgid "Ctrl+H"
-msgstr ""
+msgstr "Ctrl+H"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:19
msgid "Ctrl+D"
-msgstr ""
+msgstr "Ctrl+D"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:22
msgid "Toggle hidden files"
-msgstr ""
+msgstr "Пераключальнік схаваных файлаў"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:23
msgid "Default path"
-msgstr ""
+msgstr "Шлях па змаўчанні"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:24
msgid "Shortcuts"
-msgstr ""
+msgstr "Спалучэнні клавіш"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:25
msgid "Select a Directory"
-msgstr ""
+msgstr "Выберыце папку"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:26
msgid "Saved paths"
-msgstr ""
+msgstr "Захаваныя шляхі"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:27
msgid "column"
-msgstr ""
+msgstr "слупок"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:29
#: deluge/ui/console/modes/preferences/preferences.py:145
@@ -3763,11 +3831,11 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/OtherLimitWindow.js:51
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:64
msgid "Cancel"
-msgstr ""
+msgstr "Скасаваць"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:30
msgid "Open"
-msgstr ""
+msgstr "Адкрыць"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:31
#: deluge/ui/web/js/deluge-all/Toolbar.js:39
@@ -3778,22 +3846,22 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:27
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:65
msgid "Add"
-msgstr ""
+msgstr "Дадаць"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:32
msgid "Add the current entry value to the list"
-msgstr ""
+msgstr "Дадаць бягучае значэнне ў спіс"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:33
#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:98
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:33
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:102
msgid "Edit"
-msgstr ""
+msgstr "Рэдагаваць"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:34
msgid "Edit the selected entry"
-msgstr ""
+msgstr "Рэдагаваць выбранае значэнне"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:35
#: deluge/ui/web/js/deluge-all/Toolbar.js:46
@@ -3801,31 +3869,31 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:110
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:156
msgid "Remove"
-msgstr ""
+msgstr "Выдаліць"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:36
msgid "Remove the selected entry"
-msgstr ""
+msgstr "Выдаліць выбраны запіс"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:38
msgid "Move the selected entry up"
-msgstr ""
+msgstr "Падняць выбраны запіс"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:40
msgid "Move the selected entry down"
-msgstr ""
+msgstr "Апусціць выбраны запіс"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:41
msgid "Default"
-msgstr ""
+msgstr "Прадвызначана"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:42
msgid "No default path set"
-msgstr ""
+msgstr "Няма прадвызначанага шляху"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:43
msgid "Open properties dialog"
-msgstr ""
+msgstr "Адкрыць акенца ўласцівасцей"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:1
msgid "Add Infohash"
@@ -3833,11 +3901,11 @@ msgstr "Дадаць хэш файлаў"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
msgid "From Infohash"
-msgstr ""
+msgstr "З хэша файлаў"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
msgid "Infohash:"
-msgstr "Хэш файлаў"
+msgstr "Хэш файлаў:"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:6
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:5
@@ -3854,11 +3922,11 @@ msgstr "Дадаць хост"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:16
#: deluge/ui/web/js/deluge-all/Menus.js:346
msgid "Move Download Folder"
-msgstr ""
+msgstr "Перамясціць папку спампоўкі"
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:4
msgid "Move the torrent(s) download folder."
-msgstr ""
+msgstr "Перамясціць папку спампоўкі торэнта(ў)."
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:5
msgid "Destination:"
@@ -3874,95 +3942,95 @@ msgstr "_Перайсці на вэб-сайт"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:4
msgid "New Release Available!"
-msgstr ""
+msgstr "Даступна новая версія!"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:5
msgid "Available Version:"
-msgstr ""
+msgstr "Даступная версія:"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:6
msgid "Server Version"
-msgstr ""
+msgstr "Версія сервера"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:7
msgid "Current Version:"
-msgstr ""
+msgstr "Бягучая версія:"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:8
msgid "Do not show this dialog in the future"
-msgstr "Больш не паказваць гэтае акно"
+msgstr "Больш не паказваць гэта акно"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:1
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:26
#: deluge/ui/web/render/tab_status.html:9
msgid "Down Speed:"
-msgstr ""
+msgstr "Хуткасць спампоўкі:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:2
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:28
#: deluge/ui/web/render/tab_status.html:10
msgid "Up Speed:"
-msgstr ""
+msgstr "Хуткасць раздачы:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:3
#: deluge/ui/web/render/tab_status.html:2
msgid "Downloaded:"
-msgstr ""
+msgstr "Спампавана:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:4
#: deluge/ui/web/render/tab_status.html:3
msgid "Uploaded:"
-msgstr ""
+msgstr "Раздадзена:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:5
#: deluge/ui/web/render/tab_status.html:16
msgid "Seeds:"
-msgstr ""
+msgstr "Сіды:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:6
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:10
#: deluge/ui/web/render/tab_status.html:17
msgid "Peers:"
-msgstr ""
+msgstr "Піры:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:8
#: deluge/ui/web/render/tab_status.html:18
msgid "Availability:"
-msgstr ""
+msgstr "Даступнасць:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:9
#: deluge/ui/web/render/tab_status.html:25
msgid "Seed Rank:"
-msgstr ""
+msgstr "Ранг сіда:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:10
msgid "ETA Time:"
-msgstr ""
+msgstr "Час да завяршэння:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:11
#: deluge/ui/web/render/tab_status.html:13
msgid "Last Transfer:"
-msgstr ""
+msgstr "Апошняя перадача:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:12
#: deluge/ui/web/render/tab_status.html:23
msgid "Active Time:"
-msgstr ""
+msgstr "Час актыўнасці:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:13
#: deluge/ui/web/render/tab_status.html:20
msgid "Complete Seen:"
-msgstr ""
+msgstr "Цалкам прагледжана:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:14
#: deluge/ui/web/render/tab_status.html:24
msgid "Seeding Time:"
-msgstr ""
+msgstr "Час сідавання:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:16
#: deluge/ui/web/render/tab_status.html:12
msgid "Pieces:"
-msgstr ""
+msgstr "Частак:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:17
#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:3
@@ -3973,44 +4041,44 @@ msgstr "Назва:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:18
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:25
msgid "Download Folder:"
-msgstr ""
+msgstr "Папка спампоўкі:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:19
msgid "Added:"
-msgstr ""
+msgstr "Дададзена:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:20
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:26
msgid "Total Size:"
-msgstr ""
+msgstr "Усяго памер:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:21
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:27
msgid "Total Files:"
-msgstr ""
+msgstr "Усяго файлаў:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:22
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:24
msgid "Hash:"
-msgstr ""
+msgstr "Хэш:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:23
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:31
msgid "Created By:"
-msgstr ""
+msgstr "Створана:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:24
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:17
msgid "Comments:"
-msgstr "Каментарыі:"
+msgstr "Каментары:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:29
msgid "Owner:"
-msgstr ""
+msgstr "Уласнік:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:34
msgid "Move completed:"
-msgstr "Перамяшчаць завершаныя"
+msgstr "Перамяшчаць завершаныя:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:36
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:39
@@ -4028,46 +4096,46 @@ msgstr "Выдаліць на рэйтынгу"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:44
msgid "Bandwidth Limits"
-msgstr ""
+msgstr "Ліміты прапускной здольнасці"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:46
msgid "Current Tracker:"
-msgstr ""
+msgstr "Бягучы трэкер:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:47
msgid "Total Trackers:"
-msgstr ""
+msgstr "Усяго трэкераў:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:48
#: deluge/ui/web/render/tab_status.html:6
msgid "Tracker Status:"
-msgstr ""
+msgstr "Статус трэкера:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:49
#: deluge/ui/web/render/tab_status.html:5
msgid "Next Announce:"
-msgstr ""
+msgstr "Наступны анонс:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:50
msgid "Private Torrent:"
-msgstr ""
+msgstr "Прыватны торэнт:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:51
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:8
msgid "_Edit Trackers"
-msgstr "_Змяніць трэкеры"
+msgstr "_Рэдагаваць трэкеры"
#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:1
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:44
#: deluge/ui/web/js/deluge-all/Menus.js:284
msgid "Top"
-msgstr ""
+msgstr "Зверху"
#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:4
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:45
#: deluge/ui/web/js/deluge-all/Menus.js:305
msgid "Bottom"
-msgstr ""
+msgstr "Знізу"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:1
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:50
@@ -4076,7 +4144,7 @@ msgstr "Дадаць торэнты"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:5
msgid "_URL"
-msgstr "_Ссылка"
+msgstr "_URL"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:6
msgid "Info_hash"
@@ -4084,7 +4152,7 @@ msgstr "Хэш _файлаў"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:11
msgid "Move Complete Folder"
-msgstr ""
+msgstr "Перамясціць завершаную папку"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:12
msgid "Add In _Paused State"
@@ -4099,32 +4167,32 @@ msgstr "Прыярытэт у першай/апошняй частак"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:46
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:152
msgid "Skip File Hash Check"
-msgstr ""
+msgstr "Прапусціць праверку хэша"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:170
msgid "Preallocate Disk Space"
-msgstr ""
+msgstr "Зарэзерваваць месца на дыску"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:24
msgid "Preallocate the disk space for the torrent files"
-msgstr ""
+msgstr "Зарэзерваваць месца на дыску для файлаў"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:25
msgid "Maximum torrent download speed"
-msgstr ""
+msgstr "Макс хуткасць спампоўкі торэнта"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:27
msgid "Maximum torrent upload speed"
-msgstr ""
+msgstr "Макс хуткасць раздачы торэнта"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:29
msgid "Maximum torrent connections"
-msgstr ""
+msgstr "Макс злучэнняў для торэнта"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:31
msgid "Maximum torrent upload slots"
-msgstr ""
+msgstr "Макс слотаў раздачы торэнта"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:34
msgid "Apply To All"
@@ -4136,25 +4204,25 @@ msgstr "Вярнуцца да налад па змаўчанню"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:1
msgid "_Show Deluge"
-msgstr "Паказать Deluge"
+msgstr "Па_казать Deluge"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:3
msgid "_Pause Session"
-msgstr ""
+msgstr "_Прыпыніць сесію"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:4
msgid "_Resume Session"
-msgstr ""
+msgstr "Ад_навіць сесію"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:5
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:1
msgid "_Download Speed Limit"
-msgstr "Перанесьці _файлы"
+msgstr "Ліміт хуткасці с_пампоўкі"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:6
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:2
msgid "_Upload Speed Limit"
-msgstr "Абмежаванне хуткасці _запампоўкі"
+msgstr "Ліміт хуткасці _раздачы"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:7
msgid "Quit & Shutdown Daemon"
@@ -4165,21 +4233,21 @@ msgstr "Выйсці і спыніць дэман"
#: deluge/ui/web/js/deluge-all/Menus.js:323
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:318
msgid "Edit Trackers"
-msgstr "Змяніць трэкеры"
+msgstr "Рэдагаваць трэкеры"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:4
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:19
msgid "_Up"
-msgstr ""
+msgstr "_Вышэй"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:8
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:22
msgid "_Down"
-msgstr ""
+msgstr "_Ніжэй"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:1
msgid "_Add Peer"
-msgstr ""
+msgstr "_Дадаць пір"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:2
msgid "Add a peer by its IP"
@@ -4188,7 +4256,7 @@ msgstr "Дадаць піра па IP"
#: deluge/ui/gtk3/glade/edit_trackers.edit.ui.h:1
#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:17
msgid "Edit Tracker"
-msgstr "Змяніць трэкер"
+msgstr "Рэдагаваць трэкер"
#: deluge/ui/gtk3/glade/edit_trackers.edit.ui.h:4
#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:44
@@ -4198,11 +4266,11 @@ msgstr "Трэкер:"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:1
msgid "Enter Remote Path"
-msgstr "Калі ласка, увядзіце аддалены шлях"
+msgstr "Увядзіце аддалены шлях"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:4
msgid "Remote Path"
-msgstr ""
+msgstr "Аддалены шлях"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:5
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:5
@@ -4212,43 +4280,43 @@ msgstr "Шлях:"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:1
msgid "32 KiB"
-msgstr ""
+msgstr "32 КіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:2
msgid "64 KiB"
-msgstr ""
+msgstr "64 КіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:3
msgid "128 KiB"
-msgstr ""
+msgstr "128 КіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:4
msgid "256 KiB"
-msgstr ""
+msgstr "256 КіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:5
msgid "512 KiB"
-msgstr ""
+msgstr "512 КіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:6
msgid "1 MiB"
-msgstr ""
+msgstr "1 МіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:7
msgid "2 MiB"
-msgstr ""
+msgstr "2 МіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:8
msgid "4 MiB"
-msgstr ""
+msgstr "4 МіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:9
msgid "8 MiB"
-msgstr ""
+msgstr "8 МіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:10
msgid "16 MiB"
-msgstr ""
+msgstr "16 МіБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:11
msgid "Create Torrent"
@@ -4256,7 +4324,7 @@ msgstr "Стварыць торэнт"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:13
msgid "Fol_der"
-msgstr "_Каталог"
+msgstr "_Папка"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:14
msgid "_Remote Path"
@@ -4267,7 +4335,7 @@ msgstr "_Аддалены шлях"
#: deluge/ui/web/js/deluge-all/details/FilesTab.js:73
#: deluge/ui/web/js/deluge-all/add/FilesTab.js:18
msgid "Files"
-msgstr ""
+msgstr "Файлы"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:24
msgid "Webseeds"
@@ -4279,7 +4347,7 @@ msgstr "Памер часткі:"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:26
msgid "Set Private Flag"
-msgstr "Усталяваць сьцяг прыватнасьці"
+msgstr "Усталяваць сцяг прыватнасці"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:27
msgid "Add this torrent to the session"
@@ -4293,7 +4361,7 @@ msgstr "Дадаць гэты торэнт да сесіі"
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:80
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:17
msgid "Options"
-msgstr "Налады"
+msgstr "Параметры"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:1
msgid "Save .torrent as"
@@ -4301,15 +4369,15 @@ msgstr "Захаваць .torrent як"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:1
msgid "_Open Download Folder"
-msgstr ""
+msgstr "_Адкрыць папку спампоўкі"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:2
msgid "_Pause"
-msgstr ""
+msgstr "_Прыпыніць"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:3
msgid "Resu_me"
-msgstr "_Аднавіць"
+msgstr "Ад_навіць"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:4
#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:4
@@ -4318,7 +4386,7 @@ msgstr "Аднавіць вылучаныя торэнты."
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:5
msgid "Opt_ions"
-msgstr "_Налады"
+msgstr "_Параметры"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:6
msgid "_Queue"
@@ -4334,11 +4402,11 @@ msgstr "_Выдаліць торэнт"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:10
msgid "_Force Re-check"
-msgstr "Пераправерыць файлы"
+msgstr "П_ераправерыць файлы"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:11
msgid "_Move Download Folder"
-msgstr ""
+msgstr "Пера_мясціць папку спампоўкі"
#: deluge/ui/gtk3/glade/other_dialog.ui.h:3
msgid "label"
@@ -4350,23 +4418,23 @@ msgstr "_Вылучыць усе"
#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:2
msgid "_Pause All"
-msgstr "_Прыпыніць усё"
+msgstr "_Прыпыніць усе"
#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:3
msgid "Resu_me All"
-msgstr "Аднавіц_ь усё"
+msgstr "Ад_навіць усе"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:3
msgid "_Connection Limit"
-msgstr "_Абмежаванне злучэнняў"
+msgstr "Ліміт _злучэнняў"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:4
msgid "Upload _Slot Limit"
-msgstr "Абмежаванне слотаў раз_дачы"
+msgstr "Ліміт _слотаў раздачы"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:5
msgid "Stop seed at _ratio"
-msgstr ""
+msgstr "Спыніць сід на р_эйтынгу"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:6
msgid "_Auto Managed"
@@ -4374,11 +4442,11 @@ msgstr "_Аўтаматычнае кіраванне"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:7
msgid "_Super Seeding"
-msgstr ""
+msgstr "С_упер сідаванне"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:8
msgid "_Change Ownership"
-msgstr ""
+msgstr "Змяніць у_ладальніка"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:1
#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:26
@@ -4387,48 +4455,48 @@ msgstr "Дадаць трэкер"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:4
msgid "Add Trackers"
-msgstr ""
+msgstr "Дадаць трэкеры"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:1
msgid "Add URL"
-msgstr "Дадаць адрас"
+msgstr "Дадаць URL"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:4
msgid "From URL"
-msgstr ""
+msgstr "З URL"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:5
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:1
msgid "URL:"
-msgstr "URL-адрас:"
+msgstr "URL:"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:9
msgid "Deluge Daemons"
-msgstr ""
+msgstr "Дэманы Deluge"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:10
msgid "Auto-connect to selected Daemon"
-msgstr ""
+msgstr "Аўтазлучэнне з выбраным дэманам"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:11
msgid "Auto-start localhost daemon (if required)"
-msgstr ""
+msgstr "Аўтастарт лакальнага дэмана (калі патрэбна)"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:12
msgid "Hide this dialog"
-msgstr ""
+msgstr "Схаваць гэта акно"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:13
msgid "Startup Options"
-msgstr ""
+msgstr "Параметры запуску"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:1
msgid "_Open File"
-msgstr ""
+msgstr "_Адкрыць файл"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:2
msgid "_Show Folder"
-msgstr ""
+msgstr "_Паказаць папку"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:3
msgid "_Expand All"
@@ -4436,30 +4504,30 @@ msgstr "_Разгарнуць усё"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:4
msgid "_Skip"
-msgstr ""
+msgstr "Пр_апусціць"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:5
msgid "_Low"
-msgstr ""
+msgstr "_Нізкі"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:6
msgid "_Normal"
-msgstr ""
+msgstr "_Звычайны"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:7
msgid "_High"
-msgstr ""
+msgstr "_Высокі"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
-msgstr ""
+msgstr "Каманда Deluge"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
-msgstr ""
+msgstr "Deluge - легкавесны, свабодны, крос-платформенны кліент BitTorrent."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4467,334 +4535,353 @@ msgid ""
"Deluge heavily utilises the libtorrent library it has a comprehensive list "
"of the features provided."
msgstr ""
+"Deluge утрымлівае звычайныя функцыі кліентаў BitTorrent, напрыклад пратакол "
+"шыфравання, DHT, лакальнае адкрыццё піраў (LSD), абмен пірамі (PEX), UPnP, "
+"NAT-PMP, падтрымка проксі, вэб-сіды, ліміты хуткасці. Паколькі Deluge "
+"актыўна выкарыстоўвае бібліятэку libtorrent, яна мае комплексны спіс функцый."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
"handles all the BitTorrent activity and is able to run on headless machines "
"with the user-interfaces connecting remotely from any other platform."
msgstr ""
+"Deluge распрацавана каб працаваць і як звычайная аўтаномная праграма, і як "
+"кліент-серверная. У рэжыме тонкага кліента дэман Deluge кіруе ўсімі "
+"дзеяннямі з торэнтамі і здольны працаваць аддалена з іншых платформ праз "
+"карыстальніцкі інтэрфейс."
#: deluge/ui/data/share/applications/deluge.desktop.in.h:2
msgid "BitTorrent Client"
-msgstr ""
+msgstr "Кліент BitTorrent"
#: deluge/ui/data/share/applications/deluge.desktop.in.h:3
msgid "Deluge BitTorrent Client"
-msgstr ""
+msgstr "BitTorrent кліент Deluge"
#: deluge/ui/data/share/applications/deluge.desktop.in.h:4
msgid "Download and share files over BitTorrent"
-msgstr ""
+msgstr "Спампоўванне і раздача файлаў праз BitTorrent"
#: deluge/ui/console/console.py:76
msgid "Console Options"
-msgstr ""
+msgstr "Налады кансолі"
#: deluge/ui/console/console.py:78
msgid ""
"These daemon connect options will be used for commands, or if console ui "
"autoconnect is enabled."
msgstr ""
+"Гэтыя налады дэмана будуць выкарыстоўвацца для каманд ці пры ўключэнні "
+"аўтазлучэння ў кансолі."
#: deluge/ui/console/console.py:87
msgid "Deluge daemon IP address to connect to (default 127.0.0.1)"
-msgstr ""
+msgstr "IP адрас дэмана Deluge для злучэння (па змаўчанні 127.0.0.1)"
#: deluge/ui/console/console.py:96
msgid "Deluge daemon port to connect to (default 58846)"
-msgstr ""
+msgstr "Порт дэмана Deluge для злучэння (па змаўчанні 58846)"
#: deluge/ui/console/console.py:104
msgid "Deluge daemon username to use when connecting"
-msgstr ""
+msgstr "Імя карыстальніка дэмана Deluge пры злучэнні"
#: deluge/ui/console/console.py:111
msgid "Deluge daemon password to use when connecting"
-msgstr ""
+msgstr "Пароль дэмана Deluge пры злучэнні"
#: deluge/ui/console/console.py:131
msgid "Console Commands"
-msgstr ""
+msgstr "Кансольныя каманды"
#: deluge/ui/console/console.py:132
msgid "Description"
-msgstr ""
+msgstr "Апісанне"
#: deluge/ui/console/console.py:133
msgid "The following console commands are available:"
-msgstr ""
+msgstr "Даступны кансольныя каманды:"
#: deluge/ui/console/console.py:134
#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:2
msgid "Command"
-msgstr "Загад"
+msgstr "Каманда"
#: deluge/ui/console/cmdline/command.py:208
#, python-format
msgid "`%s` alias"
-msgstr ""
+msgstr "`%s` імя"
#: deluge/ui/console/cmdline/commands/manage.py:29
msgid "Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
msgstr ""
+"Выкарыстанне: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
#: deluge/ui/console/cmdline/commands/manage.py:35
msgid "an expression matched against torrent ids and torrent names"
-msgstr ""
+msgstr "выраз суадносіць id торэнтаў і імёны торэнтаў"
#: deluge/ui/console/cmdline/commands/manage.py:43
#: deluge/ui/console/cmdline/commands/config.py:88
msgid "set value for this key"
-msgstr ""
+msgstr "задаць значэнне ключа"
#: deluge/ui/console/cmdline/commands/manage.py:46
#: deluge/ui/console/cmdline/commands/config.py:91
msgid "Value to set"
-msgstr ""
+msgstr "Задаць значэнне"
#: deluge/ui/console/cmdline/commands/manage.py:53
#: deluge/ui/console/cmdline/commands/config.py:98
msgid "one or more keys separated by space"
-msgstr ""
+msgstr "адзін ці больш ключоў- праз прабел"
#: deluge/ui/console/cmdline/commands/rm.py:33
msgid "Also removes the torrent data"
-msgstr ""
+msgstr "Таксама выдаляе даныя торэнта"
#: deluge/ui/console/cmdline/commands/rm.py:40
msgid "List the matching torrents without removing."
-msgstr ""
+msgstr "Спіс суаднесеных торэнтаў без выдалення."
#: deluge/ui/console/cmdline/commands/rm.py:46
#: deluge/ui/console/cmdline/commands/recheck.py:28
#: deluge/ui/console/cmdline/commands/move.py:31
msgid "One or more torrent ids"
-msgstr ""
+msgstr "Адзін ці больш id торэнтаў"
#: deluge/ui/console/cmdline/commands/rm.py:66
#, python-format
msgid "Confirm with -c to remove the listed torrents (Count: %d)"
-msgstr ""
+msgstr "Пацвердзіць з -c каб выдаліць пералічаныя торэнты (Падлік: %d)"
#: deluge/ui/console/cmdline/commands/resume.py:22
msgid "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
-msgstr ""
+msgstr "Выкарыстанне: resume [ * | <torrent-id> [<torrent-id> ...] ]"
#: deluge/ui/console/cmdline/commands/resume.py:29
msgid "One or more torrent ids. Use \"*\" to resume all torrents"
msgstr ""
+"Адзін ці больш id торэнтаў. Выкарыстоўвайце \"*\" каб аднавіць усе торэнты"
#: deluge/ui/console/cmdline/commands/pause.py:29
msgid "One or more torrent ids. Use \"*\" to pause all torrents"
msgstr ""
+"Адзін ці больш id торэнтаў. Выкарыстоўвайце \"*\" каб прыпыніць усе торэнты"
#: deluge/ui/console/cmdline/commands/add.py:38
msgid "Download folder for torrent"
-msgstr ""
+msgstr "Папка спампоўкі для торэнта"
#: deluge/ui/console/cmdline/commands/add.py:44
msgid "Move the completed torrent to this folder"
-msgstr ""
+msgstr "Перамясціць завершаны торэнт у гэту папку"
#: deluge/ui/console/cmdline/commands/add.py:50
msgid "One or more torrent files, URLs or magnet URIs"
-msgstr ""
+msgstr "Адзін ці больш торэнт-файлаў, URL ці спасылак URI"
#: deluge/ui/console/cmdline/commands/plugin.py:29
msgid "Lists available plugins"
-msgstr ""
+msgstr "Спіс даступных плагінаў"
#: deluge/ui/console/cmdline/commands/plugin.py:37
msgid "Shows enabled plugins"
-msgstr ""
+msgstr "Паказвае ўключаныя плагіны"
#: deluge/ui/console/cmdline/commands/plugin.py:40
msgid "Enables a plugin"
-msgstr ""
+msgstr "Уключае плагін"
#: deluge/ui/console/cmdline/commands/plugin.py:43
msgid "Disables a plugin"
-msgstr ""
+msgstr "Выключае плагін"
#: deluge/ui/console/cmdline/commands/plugin.py:51
msgid "Reload list of available plugins"
-msgstr ""
+msgstr "Перазагрузка спіса даступных плагінаў"
#: deluge/ui/console/cmdline/commands/plugin.py:54
msgid "Install a plugin from an .egg file"
-msgstr ""
+msgstr "Усталяваць плагін з файла .egg"
#: deluge/ui/console/cmdline/commands/status.py:36
msgid ""
"Raw values for upload/download rates (without KiB/s suffix)(useful for "
"scripts that want to do their own parsing)"
msgstr ""
+"Значэнні для рэйтынга раздачы/спампоўкі (без КіБ/с)(карысна для скрыптоў, "
+"якія жадаюць рабіць уласны парсінг)"
#: deluge/ui/console/cmdline/commands/status.py:46
msgid "Do not show torrent status (Improves command speed)"
-msgstr ""
+msgstr "Не паказваць статус торэнта (Паляпшае хуткасць каманды)"
#: deluge/ui/console/cmdline/commands/connect.py:26
msgid "Usage: connect <host[:port]> [<username>] [<password>]"
-msgstr ""
+msgstr "Выкарыстанне: connect <host[:port]> [<username>] [<password>]"
#: deluge/ui/console/cmdline/commands/connect.py:30
msgid "Daemon host and port"
-msgstr ""
+msgstr "Хост і порт дэмана"
#: deluge/ui/console/cmdline/commands/connect.py:36
#: deluge/ui/console/modes/preferences/preference_panes.py:652
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:259
msgid "Password"
-msgstr ""
+msgstr "Пароль"
#: deluge/ui/console/cmdline/commands/move.py:34
msgid "The path to move the torrents to"
-msgstr ""
+msgstr "Шлях куды перамясціць торэнты"
#: deluge/ui/console/cmdline/commands/debug.py:26
msgid "The new state"
-msgstr ""
+msgstr "Новы стан"
#: deluge/ui/console/cmdline/commands/help.py:29
msgid "One or more commands"
-msgstr ""
+msgstr "Адна ці больш каманд"
#: deluge/ui/console/cmdline/commands/config.py:79
msgid "Usage: config [--set <key> <value>] [<key> [<key>...] ]"
-msgstr ""
+msgstr "Выкарыстанне: config [--set <key> <value>] [<key> [<key>...] ]"
#: deluge/ui/console/cmdline/commands/info.py:101
msgid "Show more information per torrent."
-msgstr ""
+msgstr "Паказаць больш інфармацыі аб торэнце."
#: deluge/ui/console/cmdline/commands/info.py:109
msgid "Show more detailed information including files and peers."
-msgstr ""
+msgstr "Паказаць больш падрабязную інфармацыю пра файлы і піры."
#: deluge/ui/console/cmdline/commands/info.py:116
#, python-format
msgid "Show torrents with state STATE: %s."
-msgstr ""
+msgstr "Паказаць торэнты са станам STATE: %s."
#: deluge/ui/console/cmdline/commands/info.py:132
msgid "Same as --sort but items are in reverse order."
-msgstr ""
+msgstr "Такое ж як --sort але адзінкі ў адваротным парадку."
#: deluge/ui/console/cmdline/commands/info.py:138
msgid "One or more torrent ids. If none is given, list all"
-msgstr ""
+msgstr "Адзін ці больш id торэнтаў. Калі нічога не выбрана, паказваюцца ўсе"
#: deluge/ui/console/modes/connectionmanager.py:44
msgid "Select Host"
-msgstr ""
+msgstr "Выбраць хост"
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Quit"
-msgstr ""
+msgstr "Выйсці"
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Delete Host"
-msgstr ""
+msgstr "Выдаліць хост"
#: deluge/ui/console/modes/connectionmanager.py:116
msgid "Add Host (Up & Down arrows to navigate, Esc to cancel)"
-msgstr ""
+msgstr "Дадаць хост (стрэлкі ўверх/уніз для перамяшчэння, Esc каб скасаваць)"
#: deluge/ui/console/modes/connectionmanager.py:133
msgid "Error adding host"
-msgstr ""
+msgstr "Памылка дадання хоста"
#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
msgid "Columns"
-msgstr ""
+msgstr "Слупкі"
#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
msgid "Width"
-msgstr ""
+msgstr "Шырыня"
#: deluge/ui/console/modes/preferences/preference_panes.py:178
msgid "General options"
-msgstr ""
+msgstr "Агульныя параметры"
#: deluge/ui/console/modes/preferences/preference_panes.py:182
msgid "Ring system bell when a download finishes"
-msgstr ""
+msgstr "Сістэмны званочак калі скончылася спампоўка"
#: deluge/ui/console/modes/preferences/preference_panes.py:188
msgid "List complete torrents after incomplete regardless of sorting order"
msgstr ""
+"Змяшчаць завершаныя торэнты пасля незавершаных па выбранай сарціроўцы"
#: deluge/ui/console/modes/preferences/preference_panes.py:193
msgid "Move selection when moving torrents in the queue"
-msgstr ""
+msgstr "Перамясціць выбар пры перамяшчэнні торэнта ў чарзе"
#: deluge/ui/console/modes/preferences/preference_panes.py:200
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:67
msgid "Language"
-msgstr ""
+msgstr "Мова"
#: deluge/ui/console/modes/preferences/preference_panes.py:202
msgid "Command Line Mode"
-msgstr ""
+msgstr "Рэжым каманднага радка"
#: deluge/ui/console/modes/preferences/preference_panes.py:205
msgid "Do not store duplicate input in history"
-msgstr ""
+msgstr "Не захоўваць копіі ўводу ў гісторыі"
#: deluge/ui/console/modes/preferences/preference_panes.py:210
msgid "Store and load command line history in command line mode"
msgstr ""
+"Захоўваць і загружаць гісторыю каманднага радка ў рэжыме каманднага радка"
#: deluge/ui/console/modes/preferences/preference_panes.py:216
msgid "Third tab lists all remaining torrents in command line mode"
msgstr ""
+"Трэцяя ўкладка ўтрымлівае ўсе торэнты, якія засталіся, у рэжыме каманднага "
+"радка"
#: deluge/ui/console/modes/preferences/preference_panes.py:221
msgid "Torrents per tab press"
-msgstr ""
+msgstr "Торэнты на націсканне ўкладкі"
#: deluge/ui/console/modes/preferences/preference_panes.py:234
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:18
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:39
msgid "Folders"
-msgstr ""
+msgstr "Папкі"
#: deluge/ui/console/modes/preferences/preference_panes.py:237
msgid "Download To"
-msgstr ""
+msgstr "Спампоўваць у"
#: deluge/ui/console/modes/preferences/preference_panes.py:254
msgid "Move completed to"
-msgstr ""
+msgstr "Перамяшчаць завершаны ў"
#: deluge/ui/console/modes/preferences/preference_panes.py:269
msgid "Copy of .torrent files to"
-msgstr ""
+msgstr "Капіяваць файлы .torrent у"
#: deluge/ui/console/modes/preferences/preference_panes.py:290
msgid "Add Paused"
-msgstr ""
+msgstr "Дадаць прыпыненыя"
#: deluge/ui/console/modes/preferences/preference_panes.py:293
msgid "Pre-Allocate disk space"
-msgstr ""
+msgstr "Рэзерваваць месца на дыску"
#: deluge/ui/console/modes/preferences/preference_panes.py:304
msgid "Incomming Ports"
-msgstr ""
+msgstr "Уваходзячыя парты"
#: deluge/ui/console/modes/preferences/preference_panes.py:313
#: deluge/ui/console/modes/preferences/preference_panes.py:337
msgid "From"
-msgstr ""
+msgstr "З"
#: deluge/ui/console/modes/preferences/preference_panes.py:321
#: deluge/ui/console/modes/preferences/preference_panes.py:345
msgid "To"
-msgstr ""
+msgstr "Да"
#: deluge/ui/console/modes/preferences/preference_panes.py:331
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:131
@@ -4803,189 +4890,192 @@ msgstr "Выкарыстоўваць выпадковыя парты"
#: deluge/ui/console/modes/preferences/preference_panes.py:352
msgid "Incoming Interface"
-msgstr ""
+msgstr "Уваходзячы інтэрфейс"
#: deluge/ui/console/modes/preferences/preference_panes.py:355
msgid "IP address of the interface to listen on (leave empty for default):"
msgstr ""
+"IP адрас інтэрфейса каб слухаць (перадвызначаны калі пакінуць пустым):"
#: deluge/ui/console/modes/preferences/preference_panes.py:363
msgid ""
"The network interface name or IP address for outgoing BitTorrent "
"connections. (Leave empty for default.):"
msgstr ""
+"Імя інтэрфейса сеткі ці IP адрас для сыходзячага злучэння BitTorrent. "
+"(Перадвызначаны калі пакінуць пустым.):"
#: deluge/ui/console/modes/preferences/preference_panes.py:382
msgid "Inbound"
-msgstr ""
+msgstr "Уваходзячы"
#: deluge/ui/console/modes/preferences/preference_panes.py:391
msgid "Outbound"
-msgstr ""
+msgstr "Сыходзячы"
#: deluge/ui/console/modes/preferences/preference_panes.py:413
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:38
msgid "Global Bandwidth Usage"
-msgstr ""
+msgstr "Агульнае выкарыстанне прапускной здольнасці"
#: deluge/ui/console/modes/preferences/preference_panes.py:416
#: deluge/ui/console/modes/preferences/preference_panes.py:469
msgid "Maximum Connections"
-msgstr ""
+msgstr "Максімум злучэнняў"
#: deluge/ui/console/modes/preferences/preference_panes.py:423
#: deluge/ui/console/modes/preferences/preference_panes.py:476
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:63
msgid "Maximum Upload Slots"
-msgstr ""
+msgstr "Максімум слотаў раздачы"
#: deluge/ui/console/modes/preferences/preference_panes.py:430
#: deluge/ui/console/modes/preferences/preference_panes.py:483
msgid "Maximum Download Speed (KiB/s)"
-msgstr ""
+msgstr "Максімум хуткасці спампоўкі (КіБ/с)"
#: deluge/ui/console/modes/preferences/preference_panes.py:437
#: deluge/ui/console/modes/preferences/preference_panes.py:490
msgid "Maximum Upload Speed (KiB/s)"
-msgstr ""
+msgstr "Максімум хуткасці раздачы (КіБ/с)"
#: deluge/ui/console/modes/preferences/preference_panes.py:444
msgid "Maximum Half-Open Connections"
-msgstr ""
+msgstr "Максімум паўадкрытых злучэнняў"
#: deluge/ui/console/modes/preferences/preference_panes.py:451
msgid "Maximum Connection Attempts per Second"
-msgstr ""
+msgstr "Максімум спроб злучэнняў у секунду"
#: deluge/ui/console/modes/preferences/preference_panes.py:463
msgid "Rate Limit IP Overhead"
-msgstr ""
+msgstr "Ацаніць верхні ліміт IP"
#: deluge/ui/console/modes/preferences/preference_panes.py:466
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:148
msgid "Per Torrent Bandwidth Usage"
-msgstr ""
+msgstr "Выкарыстанне прапускной здольнасці на торэнт"
#: deluge/ui/console/modes/preferences/preference_panes.py:513
msgid "Yes, please send anonymous statistics."
-msgstr ""
+msgstr "Так, адсылаць ананімную статыстыку."
#: deluge/ui/console/modes/preferences/preference_panes.py:531
msgid "Daemon Port"
-msgstr ""
+msgstr "Порт дэмана"
#: deluge/ui/console/modes/preferences/preference_panes.py:538
msgid "Allow remote connections"
-msgstr ""
+msgstr "Дазволіць аддаленыя злучэнні"
#: deluge/ui/console/modes/preferences/preference_panes.py:561
msgid "Total"
-msgstr ""
+msgstr "Усяго"
#: deluge/ui/console/modes/preferences/preference_panes.py:593
msgid "Share Ratio"
-msgstr ""
+msgstr "Рэйтынг раздачы"
#: deluge/ui/console/modes/preferences/preference_panes.py:601
msgid "Time Ratio"
-msgstr ""
+msgstr "Рэйтынг часу"
#: deluge/ui/console/modes/preferences/preference_panes.py:609
msgid "Time (m)"
-msgstr ""
+msgstr "Час (хв)"
#: deluge/ui/console/modes/preferences/preference_panes.py:633
msgid "Remove torrent (Unchecked pauses torrent)"
-msgstr ""
+msgstr "Выдаліць торэнт (Адключаны прыпыняе торэнт)"
#: deluge/ui/console/modes/preferences/preference_panes.py:646
msgid "Proxy Settings"
-msgstr ""
+msgstr "Налады проксі"
#: deluge/ui/console/modes/preferences/preference_panes.py:649
msgid "Type"
-msgstr ""
+msgstr "Тып"
#: deluge/ui/console/modes/preferences/preference_panes.py:653
msgid "Hostname"
-msgstr ""
+msgstr "Імя хоста"
#: deluge/ui/console/modes/preferences/preference_panes.py:673
msgid "Proxy Type Help"
-msgstr ""
+msgstr "Дапамога тыпа проксі"
#: deluge/ui/console/modes/preferences/preference_panes.py:697
msgid "Cache Size (16 KiB blocks)"
-msgstr ""
+msgstr "Памер кэша (блокі 16 КіБ)"
#: deluge/ui/console/modes/preferences/preference_panes.py:704
msgid "Cache Expiry (seconds)"
-msgstr ""
+msgstr "Заканчэння кэша (секунды)"
#: deluge/ui/console/modes/preferences/preference_panes.py:712
msgid "Blocks Written"
-msgstr ""
+msgstr "Блакіруе запісанае"
#: deluge/ui/console/modes/preferences/preference_panes.py:716
msgid "Writes"
-msgstr ""
+msgstr "Запісвае"
#: deluge/ui/console/modes/preferences/preference_panes.py:720
msgid "Write Cache Hit Ratio"
-msgstr ""
+msgstr "Рэйтынг запісу кэша"
#: deluge/ui/console/modes/preferences/preference_panes.py:725
msgid "Blocks Read"
-msgstr ""
+msgstr "Блакіруе чытанне"
#: deluge/ui/console/modes/preferences/preference_panes.py:729
msgid "Blocks Read hit"
-msgstr ""
+msgstr "Блакіруе чытанне"
#: deluge/ui/console/modes/preferences/preference_panes.py:732
msgid "Reads"
-msgstr ""
+msgstr "Чытае"
#: deluge/ui/console/modes/preferences/preference_panes.py:735
msgid "Read Cache Hit Ratio"
-msgstr ""
+msgstr "Рэйтынг чытання кэша"
#: deluge/ui/console/modes/preferences/preference_panes.py:741
msgid "Cache Size"
-msgstr ""
+msgstr "Памер кэша"
#: deluge/ui/console/modes/preferences/preference_panes.py:746
msgid "Read Cache Size"
-msgstr ""
+msgstr "Памер кэша чытання"
#: deluge/ui/console/modes/preferences/preferences.py:145
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:333
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:87
msgid "Apply"
-msgstr ""
+msgstr "Ужыць"
#: deluge/ui/console/modes/preferences/preferences.py:145
#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:35
#: deluge/ui/web/js/deluge-all/OtherLimitWindow.js:52
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:88
msgid "OK"
-msgstr ""
+msgstr "ОК"
#: deluge/ui/console/widgets/fields.py:1070
msgid "Select Language"
-msgstr ""
+msgstr "Выбраць мову"
#: deluge/ui/console/widgets/statusbars.py:120
#, python-format
msgid "IP {!white,blue!}%s{!status!}"
-msgstr ""
+msgstr "IP {!white,blue!}%s{!status!}"
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:114
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:116
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:118
#, python-format
msgid "The IP address \"%s\" is badly formed"
-msgstr ""
+msgstr "IP адрас \"%s\" дрэнна сфарміраваны"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:21
msgid "Emule IP list (GZip)"
@@ -4997,7 +5087,7 @@ msgstr "SafePeer тэкст (zip)"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:23
msgid "PeerGuardian Text (Uncompressed)"
-msgstr "PeerGuardian тэкст (без сціску)"
+msgstr "PeerGuardian тэкст (несціснуты)"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:24
msgid "PeerGuardian P2B (GZip)"
@@ -5005,7 +5095,7 @@ msgstr "PeerGuardian P2B (GZip)"
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:45
msgid "Blocked IP Ranges /Whitelisted IP Ranges"
-msgstr ""
+msgstr "Блакіраваныя дыяпазоны IP /Дазволеныя дыяпазоны IP"
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:56
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:156
@@ -5015,7 +5105,7 @@ msgstr "Чорны спіс"
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:233
msgid "Bad IP address"
-msgstr ""
+msgstr "Дрэнны IP адрас"
#: deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py:40
msgid "Invalid leader"
@@ -5052,11 +5142,11 @@ msgstr "Спампаваць файл чорнага спісу, калі неа
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:8
msgid "Check Download and Import"
-msgstr "Праверыць і запампанаваць"
+msgstr "Праверыць спампоўку і імпарт"
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:9
msgid "Download a new blocklist file and import it."
-msgstr "Спампаваць новы файл чорнага спісу і дабавіць яго"
+msgstr "Спампаваць новы файл чорнага спісу і дабавіць яго."
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:10
msgid "Force Download and Import"
@@ -5089,11 +5179,11 @@ msgstr "<b>Інфармацыя</b>"
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:17
msgid "<b>Whitelist</b>"
-msgstr ""
+msgstr "<b>Белы спіс</b>"
#: deluge/plugins/Execute/deluge_execute/gtkui.py:36
msgid "Torrent Complete"
-msgstr "Запампоўка торэнту скончана"
+msgstr "Торэнт завершаны"
#: deluge/plugins/Execute/deluge_execute/gtkui.py:37
msgid "Torrent Added"
@@ -5101,7 +5191,7 @@ msgstr "Торэнт дададзены"
#: deluge/plugins/Execute/deluge_execute/gtkui.py:38
msgid "Torrent Removed"
-msgstr ""
+msgstr "Торэнт выдалены"
#: deluge/plugins/Execute/deluge_execute/gtkui.py:64
#: deluge/plugins/Execute/deluge_execute/gtkui.py:79
@@ -5114,86 +5204,92 @@ msgstr "Падзея"
#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:3
msgid "<b>Add Command</b>"
-msgstr "Дадаць загад"
+msgstr "<b>Дадаць каманду</b>"
#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:4
msgid "<b>Commands</b>"
-msgstr "<b>Загады</b>"
+msgstr "<b>Каманды</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:327
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:342
msgid "Incompatible Option"
-msgstr ""
+msgstr "Несумяшчальная опцыя"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:418
msgid ""
"\"Watch Folder\" directory and \"Copy of .torrent files to\" directory "
"cannot be the same!"
msgstr ""
+"Папка для \"Глядзець папку\" і \"Капіяваць файл .torrent у\" не можа быць "
+"адной і той жа!"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:462
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:466
msgid "AutoAdd"
-msgstr ""
+msgstr "Аўтададанне"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:495
msgid "Double-click to toggle"
-msgstr ""
+msgstr "Двайное націсканне каб пераключыць"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:503
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:511
msgid "Double-click to edit"
-msgstr ""
+msgstr "Двайное націсканне каб рэдагаваць"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:507
msgid "Path"
-msgstr ""
+msgstr "Шлях"
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:125
msgid "Watch folder does not exist."
-msgstr ""
+msgstr "Папка прагляду не існуе."
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:128
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:443
msgid "Path does not exist."
-msgstr ""
+msgstr "Шлях не існуе."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:1
msgid "Watch Folder Properties"
-msgstr ""
+msgstr "Уласцівасці папкі прагляду"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:2
msgid ""
"If a .torrent file is added to this directory,\n"
"it will be added to the session."
msgstr ""
+"Калі файл .torrent дададзены ў папку,\n"
+"ён будзе дададзены да сесіі."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:4
#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:2
msgid "Select A Folder"
-msgstr "Выбраць каталог"
+msgstr "Вылучыць папку"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:5
msgid "Enable this watch folder"
-msgstr ""
+msgstr "Уключыць папку прагляду"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:6
msgid "<b>Watch Folder</b>"
-msgstr ""
+msgstr "<b>Папка прагляду</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:7
msgid "Delete .torrent after adding"
-msgstr ""
+msgstr "Выдаліць .torrent пасля дадання"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:8
msgid ""
"Once the torrent is added to the session,\n"
"the .torrent will be deleted."
msgstr ""
+"Як толькі торэнт дададзены да сесіі,\n"
+".torrent будзе выдалены."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:10
msgid "Append extension after adding:"
-msgstr ""
+msgstr "Дадаць пашырэнне пасля дадання:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:11
msgid ""
@@ -5201,10 +5297,13 @@ msgid ""
"an extension will be appended to the .torrent\n"
"and it will remain in the same directory."
msgstr ""
+"Як толькі торэнт дададзены да сесіі,\n"
+"пашырэнне будзе дададзена да .torrent\n"
+"і застанецца ў той жа папцы."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:14
msgid ".added"
-msgstr ""
+msgstr ".added"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:16
msgid ""
@@ -5212,56 +5311,61 @@ msgid ""
"the .torrent will copied to the chosen directory\n"
"and deleted from the watch folder."
msgstr ""
+"Як толькі торэнт дададзены да сесіі,\n"
+".torrent будзе скапіяваны ў вылучаную папку\n"
+"і выдалены з папкі прагляду."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:20
msgid ""
"Once the torrent is deleted from the session,\n"
"also delete the .torrent file used to add it."
msgstr ""
+"Як толькі торэнт выдалены з сесіі,\n"
+"таксама выдаліць і адпаведны файл .torrent."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:22
msgid "<b>Torrent File Action</b>"
-msgstr ""
+msgstr "<b>Дзеянні з файлам торэнта</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:23
msgid "Set download folder"
-msgstr ""
+msgstr "Выбраць папку спампоўкі"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:24
msgid "This folder will be where the torrent data is downloaded to."
-msgstr ""
+msgstr "У гэту папку будуць спампоўвацца даныя торэнтаў."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:25
msgid "<b>Download Folder</b>"
-msgstr ""
+msgstr "<b>Папка спампоўкі</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:26
msgid "Set move completed folder"
-msgstr ""
+msgstr "Выбраць папку для перамяшчэння завершаных"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:27
msgid "<b>Move Completed</b>"
-msgstr ""
+msgstr "<b>Перамясціць завершаныя</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:28
msgid "Label: "
-msgstr ""
+msgstr "Пазнака: "
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:29
msgid "<b>Label</b>"
-msgstr ""
+msgstr "<b>Пазнака</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:30
msgid "Main"
-msgstr ""
+msgstr "Асноўная"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:31
msgid "The user selected here will be the owner of the torrent."
-msgstr ""
+msgstr "Выбраны тут карыстальнік будзе ўласнікам торэнта."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:32
msgid "<b>Owner</b>"
-msgstr ""
+msgstr "<b>Уласнік</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:33
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:103
@@ -5281,7 +5385,7 @@ msgstr "Максімальная колькасць слотаў раздачы:
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:37
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:77
msgid "Max Download Speed:"
-msgstr "Максімальная хуткасць запампоўкі:"
+msgstr "Максімальная хуткасць спампоўкі:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:38
msgid "<b>Bandwidth</b>"
@@ -5290,15 +5394,15 @@ msgstr "<b>Абмежаванні</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:41
#: deluge/ui/web/render/tab_status.html:19
msgid "Auto Managed:"
-msgstr ""
+msgstr "Аўтаматычна:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:42
msgid "Add Paused:"
-msgstr ""
+msgstr "Дадаць прыпыненыя:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:43
msgid "Queue to:"
-msgstr ""
+msgstr "Чарга да:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:47
msgid "<b>Queue</b>"
@@ -5306,82 +5410,84 @@ msgstr "<b>Чарга</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/config.ui.h:1
msgid "<b>Watch Folders:</b>"
-msgstr ""
+msgstr "<b>Папкі назірання:</b>"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:60
msgid "minutes"
-msgstr ""
+msgstr "хвіліны"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:62
msgid "1 minute"
-msgstr ""
+msgstr "1 хвіліна"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:64
msgid "1 second"
-msgstr ""
+msgstr "1 секунда"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:66
msgid "seconds"
-msgstr ""
+msgstr "секунды"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:1
msgid "Stats"
-msgstr ""
+msgstr "Статыстыка"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:2
msgid "Resolution"
-msgstr ""
+msgstr "Раздзяляльнасць"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:5
msgid "Seeds/Peers"
-msgstr ""
+msgstr "Сіды/Піры"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:1
msgid "Download color:"
-msgstr ""
+msgstr "Колер спампоўкі:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:2
msgid "Upload color:"
-msgstr ""
+msgstr "Колер раздачы:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:3
msgid "<b>Connections Graph</b>"
-msgstr ""
+msgstr "<b>Граф злучэнняў</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:4
msgid "<b>Bandwidth Graph</b>"
-msgstr ""
+msgstr "<b>Граф прапускной здольнасці</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:5
msgid "DHT nodes:"
-msgstr ""
+msgstr "Вузлы DHT:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:6
msgid "Cached DHT nodes:"
-msgstr ""
+msgstr "Кэшаваныя вузлы DHT:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:7
msgid "DHT torrents:"
-msgstr ""
+msgstr "Торэнты DHT:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:9
msgid "<b>Seeds / Peers</b>"
-msgstr ""
+msgstr "<b>Сіды / Піры</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:11
msgid "<b>Graph Colors</b>"
-msgstr ""
+msgstr "<b>Колеры графаў</b>"
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:35
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:47
msgid "WebUi"
-msgstr ""
+msgstr "Вэб-інтэрфейс"
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:90
msgid ""
"The Deluge web interface is not installed, please install the\n"
"interface and try again"
msgstr ""
+"Вэб-інтэрфейс Deluge не ўсталяваны, калі ласка,\n"
+"усталюйце яго і паўтарыце спробу"
#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:1
msgid "Enable web interface"
@@ -5397,21 +5503,21 @@ msgstr "Праслухоўваць порт:"
#: deluge/plugins/Label/deluge_label/core.py:184
msgid "Invalid label, valid characters:[a-z0-9_-]"
-msgstr "Няверная метка. Дазволеныя сімвалы: [a-z0-9_-]"
+msgstr "Памылковая пазнака. Дазволеныя сімвалы: [a-z0-9_-]"
#: deluge/plugins/Label/deluge_label/core.py:186
msgid "Empty Label"
-msgstr "Пустая метка"
+msgstr "Пустая пазнака"
#: deluge/plugins/Label/deluge_label/core.py:187
msgid "Label already exists"
-msgstr "Метка ўжо існуе"
+msgstr "Пазнака ўжо існуе"
#: deluge/plugins/Label/deluge_label/core.py:195
#: deluge/plugins/Label/deluge_label/core.py:285
#: deluge/plugins/Label/deluge_label/core.py:320
msgid "Unknown Label"
-msgstr "Невядомая метка"
+msgstr "Невядомая пазнака"
#: deluge/plugins/Label/deluge_label/core.py:321
msgid "Unknown Torrent"
@@ -5419,20 +5525,20 @@ msgstr "Невядомы торэнт"
#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:46
msgid "Label _Options"
-msgstr "_Налады меткі"
+msgstr "_Налады пазнакі"
#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:47
msgid "_Remove Label"
-msgstr "_Выдаліць метку"
+msgstr "_Выдаліць пазнаку"
#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:48
msgid "_Add Label"
-msgstr "_Дадаць метку"
+msgstr "_Дадаць пазнаку"
#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:177
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:2
msgid "Label Options"
-msgstr "Налады меткі"
+msgstr "Налады пазнакі"
#: deluge/plugins/Label/deluge_label/gtkui/submenu.py:34
#: deluge/plugins/Label/deluge_label/gtkui/label_config.py:37
@@ -5440,19 +5546,19 @@ msgstr "Налады меткі"
#: deluge/plugins/Label/deluge_label/gtkui/__init__.py:49
#: deluge/plugins/Label/deluge_label/gtkui/__init__.py:77
msgid "Label"
-msgstr "Метка"
+msgstr "Пазнака"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:1
msgid "tracker1.org"
-msgstr ""
+msgstr "tracker1.org"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:3
msgid "<b>Label Options</b>"
-msgstr "<b>Налады меткі</b>"
+msgstr "<b>Налады пазнакі</b>"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:9
msgid "Apply per torrent max settings:"
-msgstr "Прымяніць максімальныя налады да кожнага торэнту"
+msgstr "Прымяніць макс налады да кожнага торэнта:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:10
msgid "Maximum"
@@ -5460,11 +5566,11 @@ msgstr "Максімум"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:14
msgid "Apply Queue settings:"
-msgstr "Прымяніць налады чаргі"
+msgstr "Прымяніць налады чаргі:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:17
msgid "Apply folder settings:"
-msgstr ""
+msgstr "Прымяніць налады папкі:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:19
msgid "<i>(1 line per tracker)</i>"
@@ -5472,107 +5578,107 @@ msgstr "<i>(кожны трэкер у асобным радку)</i>"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:20
msgid "Automatically apply label:"
-msgstr "Аўтаматычна ўжываць метку:"
+msgstr "Аўтаматычна ўжываць пазнаку:"
#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:1
msgid "Add Label"
-msgstr "Дадаць метку"
+msgstr "Дадаць пазнаку"
#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:2
msgid "<b>Add Label</b>"
-msgstr "<b>Дадаць метку</b>"
+msgstr "<b>Дадаць пазнаку</b>"
#: deluge/plugins/Label/deluge_label/data/label_pref.ui.h:1
msgid "<i>Use the sidebar to add,edit and remove labels. </i>\n"
msgstr ""
"<i>Выкарыстоўвайце бакавую панэль для дадання, выпраўлення і выдалення "
-"метак.</i>\n"
+"пазнак.</i>\n"
#: deluge/plugins/Label/deluge_label/data/label_pref.ui.h:3
msgid "<b>Labels</b>"
-msgstr "<b>Меткі</b>"
+msgstr "<b>Пазнакі</b>"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:171
msgid "Notification Blink shown"
-msgstr ""
+msgstr "Паказваць мігценне паведамлення"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:175
msgid "Popup notification is not enabled."
-msgstr ""
+msgstr "Усплываючыя вокны не ўключаны."
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:177
msgid "libnotify is not installed"
-msgstr ""
+msgstr "libnotify не ўсталявана"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:185
msgid "Failed to popup notification"
-msgstr ""
+msgstr "Памылка паведамленняў"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:188
msgid "Notification popup shown"
-msgstr ""
+msgstr "Паказваць усплываючае акно"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:192
msgid "Sound notification not enabled"
-msgstr ""
+msgstr "Гукавое апавяшчэнне не ўключана"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:194
msgid "pygame is not installed"
-msgstr ""
+msgstr "pygame не ўсталявана"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:206
#, python-format
msgid "Sound notification failed %s"
-msgstr ""
+msgstr "Гукавое апавяшчэнне няўдала %s"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:210
msgid "Sound notification Success"
-msgstr ""
+msgstr "Гукавое апавяшчэнне ўдалае"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:234
msgid "Finished Torrent"
-msgstr ""
+msgstr "Торэнт скончаны"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:238
#, python-format
msgid ""
"The torrent \"%(name)s\" including %(num_files)i file(s) has finished "
"downloading."
-msgstr ""
+msgstr "Скончылася спампоўка \"%(name)s\" з %(num_files)i файла(ў)."
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:287
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:317
msgid "Notifications"
-msgstr ""
+msgstr "Апавяшчэнні"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:663
msgid "Choose Sound File"
-msgstr ""
+msgstr "Выберыце гукавы файл"
#: deluge/plugins/Notifications/deluge_notifications/core.py:127
#: deluge/plugins/Notifications/deluge_notifications/core.py:158
#, python-format
msgid "There was an error sending the notification email: %s"
-msgstr ""
+msgstr "Памылка апавяшчэння па электроннай пошце: %s"
#: deluge/plugins/Notifications/deluge_notifications/core.py:145
#, python-format
msgid "Server did not reply properly to HELO greeting: %s"
-msgstr ""
+msgstr "Сервер няправільна адказаў на прывітанне: %s"
#: deluge/plugins/Notifications/deluge_notifications/core.py:149
#, python-format
msgid "Server refused username/password combination: %s"
-msgstr ""
+msgstr "Сервер адмовіўся прыняць імя карыстальніка або пароль: %s"
#: deluge/plugins/Notifications/deluge_notifications/core.py:174
msgid "Notification email sent."
-msgstr ""
+msgstr "Апавяшчэнне выслана па эл. пошце."
#: deluge/plugins/Notifications/deluge_notifications/core.py:181
#, python-format
msgid "Finished Torrent \"%(name)s\""
-msgstr ""
+msgstr "Завершаны торэнт \"%(name)s\""
#: deluge/plugins/Notifications/deluge_notifications/core.py:184
#, python-format
@@ -5585,53 +5691,62 @@ msgid ""
"Thank you,\n"
"Deluge."
msgstr ""
+"Гэты ліст інфармуе, што Deluge скончыў спампоўку «%(name)s», які складаецца "
+"з %(num_files)i файлаў.\n"
+"Каб перастаць атрымліваць гэтыя паведамленні — выключыце апавяшчэнні ў "
+"наладах Deluge.\n"
+"\n"
+"Дзякуем,\n"
+"Deluge."
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:1
msgid "Tray icon blinks enabled"
-msgstr ""
+msgstr "Мігценне значка ў трэі ўключана"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:2
msgid "Popups enabled"
-msgstr ""
+msgstr "Усплываючыя вокны ўключаны"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:3
msgid "Sound enabled"
-msgstr ""
+msgstr "Гукі ўключаны"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:4
msgid "<b>UI Notifications</b>"
-msgstr ""
+msgstr "<b>Апавяшчэнні UI</b>"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:9
msgid "<b>Recipients</b>"
-msgstr ""
+msgstr "<b>Атрымальнікі</b>"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:10
msgid "Server requires TLS/SSL"
-msgstr ""
+msgstr "Сервер патрабуе TLS/SSL"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:13
msgid "<b>Email Notifications</b>"
-msgstr ""
+msgstr "<b>Паштовыя апавяшчэнні</b>"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:15
msgid ""
"This configuration does not mean that you'll actually receive notifications "
"for all these events."
msgstr ""
+"Гэтая канфігурацыя не азначае, што вы атрымаеце ўсе апавяшчэнні пра гэтыя "
+"падзеі."
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:16
msgid "Subscriptions"
-msgstr ""
+msgstr "Падпіскі"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:17
msgid "Sound Customization"
-msgstr ""
+msgstr "Налады гуку"
#: deluge/plugins/Extractor/deluge_extractor/gtkui.py:42
#: deluge/plugins/Extractor/deluge_extractor/gtkui.py:53
msgid "Extractor"
-msgstr ""
+msgstr "Распакоўка"
#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:1
msgid "Extract to:"
@@ -5639,32 +5754,32 @@ msgstr "Распакаваць у:"
#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:3
msgid "Create torrent name sub-folder"
-msgstr "Стварыць падкаталог з назвай торэнту"
+msgstr "Стварыць пад-папку з назвай торэнта"
#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:4
msgid ""
"This option will create a sub-folder using the torrent's name within the "
"selected extract folder and put the extracted files there."
msgstr ""
-"Гэтая налада дазволіць стварыць падкаталог з назвай торэнту ўнутры выбранага "
-"каталога і перамясціць туды распакаваныя файлы."
+"Гэта налада дазволіць стварыць пад-папку з назвай торэнта ўнутры выбранай "
+"папкі і перамясціць туды распакаваныя файлы."
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:196
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:359
msgid "Scheduler"
-msgstr ""
+msgstr "Планавальнік"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:289
msgid "<b>Schedule</b>"
-msgstr ""
+msgstr "<b>Запланаваць</b>"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:301
msgid "Download Limit:"
-msgstr "Абмежаванне хуткасці запампоўкі:"
+msgstr "Абмежаванне спампоўкі:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:310
msgid "Upload Limit:"
-msgstr "Абмежаванне хуткасці раздачы:"
+msgstr "Абмежаванне раздачы:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:319
msgid "Active Torrents:"
@@ -5672,11 +5787,11 @@ msgstr "Актыўныя торэнты:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:328
msgid "Active Downloading:"
-msgstr ""
+msgstr "Актыўныя спампоўкі:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:337
msgid "Active Seeding:"
-msgstr ""
+msgstr "Актыўныя сідаванні:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:350
msgid "<b>Slow Settings</b>"
@@ -5684,51 +5799,51 @@ msgstr "<b>Абмежаванні</ b>"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:13
msgid "File Browser"
-msgstr ""
+msgstr "Агляд файлаў"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:25
msgid "Back"
-msgstr ""
+msgstr "Назад"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:29
msgid "Forward"
-msgstr ""
+msgstr "Наперад"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:37
msgid "Home"
-msgstr ""
+msgstr "Хатняя"
#: deluge/ui/web/js/deluge-all/Toolbar.js:32
msgid "Create"
-msgstr ""
+msgstr "Стварыць"
#: deluge/ui/web/js/deluge-all/Toolbar.js:100
msgid "Help"
-msgstr ""
+msgstr "Даведка"
#: deluge/ui/web/js/deluge-all/Toolbar.js:108
msgid "Logout"
-msgstr ""
+msgstr "Скончыць сеанс"
#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:34
msgid "Save"
-msgstr ""
+msgstr "Захаваць"
#: deluge/ui/web/js/deluge-all/AboutWindow.js:19
msgid "About Deluge"
-msgstr ""
+msgstr "Аб Deluge"
#: deluge/ui/web/js/deluge-all/AboutWindow.js:102
msgid "Copyright 2007-2018 Deluge Team"
-msgstr ""
+msgstr "Copyright 2007-2018 Deluge Team"
#: deluge/ui/web/js/deluge-all/RemoveWindow.js:33
msgid "Remove With Data"
-msgstr ""
+msgstr "Выдаліць з данымі"
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:17
msgid "Add Connection"
-msgstr ""
+msgstr "Дадаць злучэнне"
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:44
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:44
@@ -5739,48 +5854,48 @@ msgstr "Хост:"
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:96
#, python-brace-format
msgid "Unable to add host: {0}"
-msgstr ""
+msgstr "Немагчыма дадаць хост: {0}"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:37
msgid "Move"
-msgstr ""
+msgstr "Перамясціць"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:54
msgid "Browse"
-msgstr ""
+msgstr "Аглядзець"
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:17
msgid "Edit Connection"
-msgstr ""
+msgstr "Рэдагаваць злучэнне"
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:115
msgid "Unable to edit host"
-msgstr ""
+msgstr "Немагчыма рэдагаваць хост"
#: deluge/ui/web/js/deluge-all/LoginWindow.js:22
#: deluge/ui/web/js/deluge-all/LoginWindow.js:31
msgid "Login"
-msgstr ""
+msgstr "Уваход"
#: deluge/ui/web/js/deluge-all/LoginWindow.js:108
msgid "Login Failed"
-msgstr ""
+msgstr "Памылка ўваходу"
#: deluge/ui/web/js/deluge-all/LoginWindow.js:109
msgid "You entered an incorrect password"
-msgstr ""
+msgstr "Вы ўвялі няправільны пароль"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:228
msgid "Public"
-msgstr ""
+msgstr "Публічны"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:292
msgid "Last Transfer"
-msgstr ""
+msgstr "Апошняя перадача"
#: deluge/ui/web/js/deluge-all/Deluge.js:158
msgid "Mixed"
-msgstr ""
+msgstr "Змешаны"
#: deluge/ui/web/js/deluge-all/Statusbar.js:87
msgid "Set Maximum Connections"
@@ -5788,46 +5903,46 @@ msgstr "Максімальная колькасць злучэнняў"
#: deluge/ui/web/js/deluge-all/Statusbar.js:97
msgid "Download Speed"
-msgstr "Хуткасць запампоўкі"
+msgstr "Хуткасць спампоўкі"
#: deluge/ui/web/js/deluge-all/Statusbar.js:102
#: deluge/ui/web/js/deluge-all/Statusbar.js:161
#: deluge/ui/web/js/deluge-all/Menus.js:79
#: deluge/ui/web/js/deluge-all/Menus.js:124
msgid "5 KiB/s"
-msgstr ""
+msgstr "5 КіБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:108
#: deluge/ui/web/js/deluge-all/Statusbar.js:167
#: deluge/ui/web/js/deluge-all/Menus.js:85
#: deluge/ui/web/js/deluge-all/Menus.js:130
msgid "10 KiB/s"
-msgstr ""
+msgstr "10 КіБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:114
#: deluge/ui/web/js/deluge-all/Statusbar.js:173
#: deluge/ui/web/js/deluge-all/Menus.js:91
#: deluge/ui/web/js/deluge-all/Menus.js:136
msgid "30 KiB/s"
-msgstr ""
+msgstr "30 КіБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:120
#: deluge/ui/web/js/deluge-all/Statusbar.js:179
#: deluge/ui/web/js/deluge-all/Menus.js:97
#: deluge/ui/web/js/deluge-all/Menus.js:142
msgid "80 KiB/s"
-msgstr ""
+msgstr "80 КіБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:126
#: deluge/ui/web/js/deluge-all/Statusbar.js:185
#: deluge/ui/web/js/deluge-all/Menus.js:103
#: deluge/ui/web/js/deluge-all/Menus.js:148
msgid "300 KiB/s"
-msgstr ""
+msgstr "300 КіБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:145
msgid "Set Maximum Download Speed"
-msgstr "Усталяваць максімальную хуткасць запампоўкі"
+msgstr "Усталяваць максімальную хуткасць спампоўкі"
#: deluge/ui/web/js/deluge-all/Statusbar.js:156
msgid "Upload Speed"
@@ -5839,49 +5954,50 @@ msgstr "Усталяваць максімальную хуткасць разд
#: deluge/ui/web/js/deluge-all/Statusbar.js:215
msgid "Protocol Traffic Download/Upload"
-msgstr "Трафік пратакола - Запампоўка/Аддача"
+msgstr "Трафік пратакола Спампоўка/Раздача"
#: deluge/ui/web/js/deluge-all/Statusbar.js:242
msgid "Freespace in download folder"
-msgstr ""
+msgstr "Вольнае месца ў папцы спампоўкі"
#: deluge/ui/web/js/deluge-all/Statusbar.js:357
#, python-brace-format
msgid "<b>IP</b> {0}"
-msgstr ""
+msgstr "<b>IP</b> {0}"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:33
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:187
msgid "Connect"
-msgstr ""
+msgstr "Злучыцца"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:120
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:197
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:379
msgid "Stop Daemon"
-msgstr ""
+msgstr "Спыніць дэман"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:185
msgid "Disconnect"
-msgstr ""
+msgstr "Адлучыцца"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:204
msgid "Start Daemon"
-msgstr ""
+msgstr "Запусціць дэман"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:322
msgid "Change Default Password"
-msgstr ""
+msgstr "Змяніць пароль па змаўчанні"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:324
msgid ""
"We recommend changing the default password.<br><br>Would you like to change "
"it now?"
msgstr ""
+"Мы рэкамендавалі змену пароля па змаўчанні.<br><br>Жадаеце змяніць яго цяпер?"
#: deluge/ui/web/js/deluge-all/Sidebar.js:13
msgid "Tracker Host"
-msgstr ""
+msgstr "Хост трэкера"
#: deluge/ui/web/js/deluge-all/Sidebar.js:33
msgid "Filters"
@@ -5889,68 +6005,68 @@ msgstr "Фільтры"
#: deluge/ui/web/js/deluge-all/UI.js:142
msgid "Connection restored"
-msgstr ""
+msgstr "Злучэнне адноўлена"
#: deluge/ui/web/js/deluge-all/UI.js:153
msgid "Lost Connection"
-msgstr ""
+msgstr "Злучэнне згублена"
#: deluge/ui/web/js/deluge-all/UI.js:154
msgid "The connection to the webserver has been lost!"
-msgstr ""
+msgstr "Злучэнне з вэб-серверам згублена!"
#: deluge/ui/web/js/deluge-all/UI.js:160
msgid "Lost connection to webserver"
-msgstr ""
+msgstr "Згублена злучэнне з вэб-серверам"
#: deluge/ui/web/js/deluge-all/Menus.js:72
msgid "D/L Speed Limit"
-msgstr ""
+msgstr "Ліміт хуткасці Спам"
#: deluge/ui/web/js/deluge-all/Menus.js:117
msgid "U/L Speed Limit"
-msgstr ""
+msgstr "Ліміт хуткасці Разд"
#: deluge/ui/web/js/deluge-all/Menus.js:162
msgid "Connection Limit"
-msgstr ""
+msgstr "Ліміт злучэння"
#: deluge/ui/web/js/deluge-all/Menus.js:207
msgid "Upload Slot Limit"
-msgstr ""
+msgstr "Ліміт слота раздачы"
#: deluge/ui/web/js/deluge-all/Menus.js:316
msgid "Update Tracker"
-msgstr ""
+msgstr "Абнавіць трэкер"
#: deluge/ui/web/js/deluge-all/Menus.js:339
msgid "Force Recheck"
-msgstr ""
+msgstr "Прымусовая праверка"
#: deluge/ui/web/js/deluge-all/Menus.js:359
msgid "Expand All"
-msgstr ""
+msgstr "Разгарнуць усё"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:13
msgid "Details"
-msgstr ""
+msgstr "Падрабязнасці"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:28
msgid "Comment:"
-msgstr ""
+msgstr "Каментар:"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:29
msgid "Status:"
-msgstr ""
+msgstr "Статус:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:242
msgid "Move Completed:"
-msgstr ""
+msgstr "Перамяшчэнне завершана:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:272
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:116
msgid "General"
-msgstr ""
+msgstr "Асноўныя"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:279
msgid "Private"
@@ -5958,11 +6074,11 @@ msgstr "Прыватны"
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:39
msgid "Loading"
-msgstr ""
+msgstr "Загрузка"
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:118
msgid "True"
-msgstr ""
+msgstr "True"
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:50
msgid "Be alerted about new releases"
@@ -5973,224 +6089,226 @@ msgid ""
"Help us improve Deluge by sending us your Python version, PyGTK version, OS "
"and processor types. Absolutely no other information is sent."
msgstr ""
+"Дапамажыце нам палепшыць Deluge, дашліце вашы версіі Python, PyGTK, АС і тып "
+"працэсара. Больш ніякай інфармацыі не адсылаецца."
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:215
msgid "Pause torrent"
-msgstr ""
+msgstr "Прыпыніць торэнт"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:17
msgid "Install Plugin"
-msgstr ""
+msgstr "Усталяваць плагін"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:33
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:109
msgid "Install"
-msgstr ""
+msgstr "Усталяваць"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:45
msgid "Select an egg"
-msgstr ""
+msgstr "Выбраць"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:46
msgid "Plugin Egg"
-msgstr ""
+msgstr "Плагін"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:49
msgid "Browse..."
-msgstr ""
+msgstr "Аглядзець..."
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:59
msgid "Uploading your plugin..."
-msgstr ""
+msgstr "Запампоўваецца ваш плагін..."
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:52
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:162
msgid "Maximum Connections:"
-msgstr "Максімальную колькасць злучэнняў:"
+msgstr "Макс колькасць злучэнняў:"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:74
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:184
msgid "Maximum Download Speed (KiB/s):"
-msgstr "Максімальная хуткасць прыёму (КБ/с):"
+msgstr "Макс хуткасць спампоўкі (КБ/с):"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:85
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:195
msgid "Maximum Upload Speed (KiB/s):"
-msgstr "Максімальная хуткасць раздачы (КБ/с):"
+msgstr "Макс хуткасць раздачы (КБ/с):"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:96
msgid "Maximum Half-Open Connections:"
-msgstr "Максімальную колькасць паў-адкрытых злучэнняў:"
+msgstr "Макс колькасць паў-адкрытых злучэнняў:"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:107
msgid "Maximum Connection Attempts per Second:"
-msgstr "Максімальная колькасць спробаў злучэння ў секунду:"
+msgstr "Макс колькасць спроб злучэння ў секунду:"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:173
msgid "Maximum Upload Slots:"
-msgstr "Максімальная колькасць слотаў раздачы:"
+msgstr "Макс колькасць слотаў раздачы:"
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:43
msgid "Cache Size (16 KiB Blocks):"
-msgstr ""
+msgstr "Памер кэша (блокі 16 Кіб):"
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:132
msgid "Force Use of Proxy"
-msgstr ""
+msgstr "Прымусова выкарыстоўваць проксі"
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:116
msgid "Find More"
-msgstr ""
+msgstr "Знайсці больш"
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:69
msgid "Use Random Port"
-msgstr ""
+msgstr "Выкарыстоўваць выпадковы порт"
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:241
msgid "Type Of Service"
-msgstr ""
+msgstr "Тып сервіса"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:53
msgid "Show filters with zero torrents"
-msgstr ""
+msgstr "Паказваць фільтры з нулявымі торэнтамі"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:60
msgid "Allow the use of multiple filters at once"
-msgstr ""
+msgstr "Дазволіць выкарыстанне адразу некалькіх фільтраў"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:94
msgid "WebUI Password"
-msgstr ""
+msgstr "Пароль WebUI"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:110
msgid "Old:"
-msgstr ""
+msgstr "Стары:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:114
msgid "New:"
-msgstr ""
+msgstr "Новы:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:118
msgid "Confirm:"
-msgstr ""
+msgstr "Пацвердзіць:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:124
msgid "Server"
-msgstr ""
+msgstr "Сервер"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:140
msgid "Session Timeout:"
-msgstr ""
+msgstr "Таймаўт сесіі:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:165
msgid "Enable SSL (paths relative to Deluge config folder)"
-msgstr ""
+msgstr "Уключыць SSL (шляхі адносна папкі з канфігамі Deluge)"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:177
msgid "Private Key:"
-msgstr ""
+msgstr "Прыватны ключ:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:187
msgid "Certificate:"
-msgstr ""
+msgstr "Сертыфікат:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:205
msgid "WebUI Language Changed"
-msgstr ""
+msgstr "Мова WebUI зменена"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:207
msgid "Do you want to refresh the page now to use the new language?"
-msgstr ""
+msgstr "Жадаеце перазапусціць старонку цяпер каб выкарыстоўваць новую мову?"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:210
msgid "Refresh"
-msgstr ""
+msgstr "Абнавіць"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:244
msgid "Invalid Password"
-msgstr ""
+msgstr "Няправільны пароль"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:245
msgid "Your passwords don't match!"
-msgstr ""
+msgstr "Ваш пароль не супадае!"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:260
msgid "Your old password was incorrect!"
-msgstr ""
+msgstr "Ваш стары пароль быў няправільны!"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:269
msgid "Change Successful"
-msgstr ""
+msgstr "Паспяховая змена"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:270
msgid "Your password was successfully changed!"
-msgstr ""
+msgstr "Ваш пароль быў паспяхова зменены!"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:13
msgid "Add from Url"
-msgstr ""
+msgstr "Дадаць з Url"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:37
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:143
msgid "Url"
-msgstr ""
+msgstr "Url"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:45
msgid "Cookies"
-msgstr ""
+msgstr "Кукі"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:99
msgid "Failed to download torrent"
-msgstr ""
+msgstr "Не ўдалося спапмаваць торэнт"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:133
msgid "File"
-msgstr ""
+msgstr "Файл"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:149
msgid "Infohash"
-msgstr ""
+msgstr "Infohash"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:260
msgid "Uploading your torrent..."
-msgstr ""
+msgstr "Раздача вашага торэнта..."
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:292
msgid "Failed to upload torrent"
-msgstr ""
+msgstr "Не ўдалося раздаць торэнт"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:317
msgid "Not a valid torrent"
-msgstr ""
+msgstr "Некарэктны торэнт"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:50
msgid "Move Completed Folder"
-msgstr ""
+msgstr "Перамясціць завершаную папку"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:85
msgid "Max Down Speed"
-msgstr ""
+msgstr "Макс хуткасць спампоўкі"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:93
msgid "Max Up Speed"
-msgstr ""
+msgstr "Макс хуткасць раздачы"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:125
msgid "Add In Paused State"
-msgstr ""
+msgstr "Дадаць у спыненым стане"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:161
msgid "Super Seed"
-msgstr ""
+msgstr "Супер сід"
#: deluge/ui/web/js/deluge-all/add/FilesTab.js:43
msgid "Download"
-msgstr ""
+msgstr "Спампаваць"
#: deluge/ui/web/render/tab_status.html:11
msgid "ETA:"
-msgstr ""
+msgstr "ETA:"
#: deluge/ui/web/render/tab_status.html:26
msgid "Date Added:"
-msgstr ""
+msgstr "Дата дададзена:"
diff --git a/deluge/i18n/bg.po b/deluge/i18n/bg.po
index 0f03e63b0..027b801c9 100644
--- a/deluge/i18n/bg.po
+++ b/deluge/i18n/bg.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4440,16 +4440,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4458,7 +4458,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/bn.po b/deluge/i18n/bn.po
index 029bf014b..876703145 100644
--- a/deluge/i18n/bn.po
+++ b/deluge/i18n/bn.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/bs.po b/deluge/i18n/bs.po
index d66224b3b..3261557f0 100644
--- a/deluge/i18n/bs.po
+++ b/deluge/i18n/bs.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ca.po b/deluge/i18n/ca.po
index 926dfea5e..e060aa186 100644
--- a/deluge/i18n/ca.po
+++ b/deluge/i18n/ca.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-12-30 00:59+0000\n"
+"PO-Revision-Date: 2022-12-21 00:21+0000\n"
"Last-Translator: Pere Orga <Unknown>\n"
"Language-Team: Catalan <ca@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -1210,7 +1210,7 @@ msgstr "Namíbia"
#: deluge/ui/countries.py:162
msgid "Nauru"
-msgstr "Nauru"
+msgstr "nauruà"
#: deluge/ui/countries.py:163
msgid "Nepal"
@@ -1482,7 +1482,7 @@ msgstr "Togo"
#: deluge/ui/countries.py:230
msgid "Tokelau"
-msgstr "Tokelau"
+msgstr "tokelauès"
#: deluge/ui/countries.py:231
msgid "Tonga"
@@ -1800,7 +1800,7 @@ msgstr ""
"Aquest programa és programari lliure (\"free software\" en anglès); podeu "
"redistribuir-lo i/o modificar-lo sota els termes de la llicència GNU Public "
"License tal com està publicada per la Free Software Foundation; ja sigui la "
-"versió 3 de la llicència, o (a la vostra decisió) qualsevol altre versió "
+"versió 3 de la llicència, o (a la vostra decisió) qualsevol altra versió "
"posterior.\n"
"Aquest programari es distribueix amb l'esperança de que serà útil, però "
"SENSE CAP GARANTIA; fins i tot sense la garantia implícita de "
@@ -1841,7 +1841,7 @@ msgstr "Afegeix els torrents (%d)"
#: deluge/ui/gtk3/addtorrentdialog.py:238
msgid "Duplicate torrent(s)"
-msgstr "Torrent(s) duplicat(s)."
+msgstr "Torrent(s) duplicat(s)"
#: deluge/ui/gtk3/addtorrentdialog.py:240
#, python-format
@@ -1870,11 +1870,11 @@ msgstr "Seleccioneu un fitxer .torrent"
#: deluge/ui/gtk3/addtorrentdialog.py:777
msgid "Invalid URL"
-msgstr "URL invàlida"
+msgstr "L'URL no és vàlid"
#: deluge/ui/gtk3/addtorrentdialog.py:778
msgid "is not a valid URL."
-msgstr "no és una URL vàlida."
+msgstr "no és un URL vàlid."
#: deluge/ui/gtk3/addtorrentdialog.py:784
msgid "Downloading..."
@@ -1975,7 +1975,7 @@ msgstr "_Afegeix"
#: deluge/ui/gtk3/dialogs.py:289
msgid "Authentication Level:"
-msgstr "Nivel d'autenticació"
+msgstr "Nivel d'autenticació:"
#: deluge/ui/gtk3/dialogs.py:423
msgid "Password Protected"
@@ -2296,7 +2296,7 @@ msgstr "Error en afegir l'amfitrió"
#: deluge/ui/gtk3/connectionmanager.py:464
msgid "Error Updating Host"
-msgstr "Ha fallat l'actualització del l'amfitrió"
+msgstr "Ha fallat l'actualització de l'amfitrió"
#: deluge/ui/gtk3/preferences.py:131
#: deluge/ui/console/cmdline/commands/connect.py:33
@@ -2535,8 +2535,8 @@ msgid ""
"Add one or more torrent files, torrent URLs or magnet URIs to a currently "
"running Deluge GTK instance"
msgstr ""
-"Afegeix un o més fitxers torrent, URLs de fitxers torrent URIs magnet a una "
-"instància en execució del Deluge GTK."
+"Afegeix un o més fitxers torrent, URLs de fitxers torrent o URIs magnet a "
+"una instància GTK en execució del Deluge"
#: deluge/ui/gtk3/glade/create_torrent_dialog.progress.ui.h:1
msgid "Creating Torrent"
@@ -3064,7 +3064,7 @@ msgstr "Diàleg «Afegeix torrents»"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:71
msgid "Connection Attempts per Second:"
-msgstr "Intents de connexió per segon"
+msgstr "Intents de connexió per segon:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:72
msgid "Half-Open Connections:"
@@ -3137,7 +3137,7 @@ msgid ""
"to avoid exceeding the limits with the total traffic"
msgstr ""
"Si es marca, la sobrecàrrega TCP/IP estimada no es tindrà en compte en els "
-"límits de relació, per tal d'evitar excedir els límits amb el trànsit total."
+"límits de relació, per tal d'evitar excedir els límits amb el trànsit total"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:85
msgid "Global Bandwidth Limits"
@@ -3442,7 +3442,7 @@ msgstr "Clients del servidor intermediari"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:144
msgid "Proxy peer and web seed connections."
-msgstr "Connexions de clients del servidor intermediari i de llavors web"
+msgstr "Connexions de clients del servidor intermediari i de llavors web."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:145
#: deluge/ui/console/modes/preferences/preference_panes.py:665
@@ -3726,7 +3726,7 @@ msgstr "_Troba'n més..."
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:4
msgid "Remove the selected torrent(s)?"
-msgstr "Voleu suprimit els torrents seleccionats?"
+msgstr "Voleu suprimir els torrents seleccionats?"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:5
msgid "Include downloaded files"
@@ -3961,7 +3961,7 @@ msgstr "Nova versió"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:3
msgid "_Goto Website"
-msgstr "_Vés al lloc web"
+msgstr "_Ves al lloc web"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:4
msgid "New Release Available!"
@@ -4405,7 +4405,7 @@ msgstr "Conti_nua"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:4
#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:4
msgid "Resume selected torrents."
-msgstr "Continua amb els torrents seleccionats"
+msgstr "Reprèn els torrents seleccionats."
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:5
msgid "Opt_ions"
@@ -4541,18 +4541,18 @@ msgstr "_Normal"
msgid "_High"
msgstr "_Alta"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Equip del Deluge"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
"El Deluge és un client de BitTorrent lleuger, de progamari lliure i "
"multiplataforma."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4561,13 +4561,13 @@ msgid ""
"of the features provided."
msgstr ""
"El Deluge conté les funcions comunes dels clients BitTorrent, com ara el "
-"xifratge del protocol, DHT, descobriment de clients locals (LSD), "
-"intercanvi de clients (PEX), UPnP, NAT-PMP, suport per servidors "
-"intermediaris, llavors web i límits de velocitat globals i per torrent. El "
-"Deluge es basa en la biblioteca libtorrent, i per tant incorpora la llista "
-"completa de les funcions que proporciona."
+"xifratge del protocol, DHT, descobriment de clients locals (LSD), intercanvi "
+"de clients (PEX), UPnP, NAT-PMP, suport per servidors intermediaris, llavors "
+"web i límits de velocitat globals i per torrent. El Deluge es basa en la "
+"biblioteca libtorrent, i, per tant, incorpora la llista completa de les "
+"funcions que proporciona."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -4701,7 +4701,8 @@ msgstr ""
#: deluge/ui/console/cmdline/commands/pause.py:29
msgid "One or more torrent ids. Use \"*\" to pause all torrents"
msgstr ""
-"Un o més identificadors de torrent. Useu \"*\" per pausar tots els torrents"
+"Un o més identificadors de torrent. Useu \"*\" per posar en pausa tots els "
+"torrents"
#: deluge/ui/console/cmdline/commands/add.py:38
msgid "Download folder for torrent"
@@ -4745,7 +4746,7 @@ msgid ""
"scripts that want to do their own parsing)"
msgstr ""
"Valors en brut de les velocitats de pujada i baixada (sense el sufix KiB/s) "
-"(útil per a scripts que vulguin fer el seu propi anàlisi)"
+"(útil per a scripts que vulguin analitzar-ho)"
#: deluge/ui/console/cmdline/commands/status.py:46
msgid "Do not show torrent status (Improves command speed)"
@@ -4792,7 +4793,7 @@ msgstr "Mostra més informació detallada com ara els fitxers i clients."
#: deluge/ui/console/cmdline/commands/info.py:116
#, python-format
msgid "Show torrents with state STATE: %s."
-msgstr "Mostra els torrents amb l'estat: %s"
+msgstr "Mostra els torrents amb l'estat: %s."
#: deluge/ui/console/cmdline/commands/info.py:132
msgid "Same as --sort but items are in reverse order."
@@ -4802,7 +4803,7 @@ msgstr "El mateix que --sort però els elements estan amb ordre invers."
msgid "One or more torrent ids. If none is given, list all"
msgstr ""
"Un o més identificadors de torrent. Si no se'n proporciona cap, mostra'ls "
-"tots."
+"tots"
#: deluge/ui/console/modes/connectionmanager.py:44
msgid "Select Host"
@@ -5116,7 +5117,7 @@ msgstr "L'adreça IP \"%s\" està mal formada"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:21
msgid "Emule IP list (GZip)"
-msgstr "Llistat de les IP d'Emule (GZip)"
+msgstr "Llistat d'IPs de l'eMule (GZip)"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:22
msgid "SafePeer Text (Zipped)"
@@ -5281,7 +5282,7 @@ msgstr "Camí"
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:125
msgid "Watch folder does not exist."
-msgstr "La carpeta vigilada no existeix"
+msgstr "La carpeta vigilada no existeix."
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:128
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:443
@@ -5740,7 +5741,7 @@ msgstr ""
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:1
msgid "Tray icon blinks enabled"
-msgstr "El parpadeig de les icones de la safata del sistema està activat"
+msgstr "El parpelleig de les icones de la safata del sistema està activat"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:2
msgid "Popups enabled"
@@ -5826,7 +5827,7 @@ msgstr "Torrents actius:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:328
msgid "Active Downloading:"
-msgstr "Nombre màxim de connexions actives"
+msgstr "Nombre de connexions actives:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:337
msgid "Active Seeding:"
@@ -6056,7 +6057,7 @@ msgstr "S'ha perdut la connexió amb el servidor web."
#: deluge/ui/web/js/deluge-all/UI.js:160
msgid "Lost connection to webserver"
-msgstr "Connexió al servidor web perduda."
+msgstr "S'ha perdut la connexió al servidor"
#: deluge/ui/web/js/deluge-all/Menus.js:72
msgid "D/L Speed Limit"
@@ -6353,7 +6354,7 @@ msgstr "Temps estimat:"
#: deluge/ui/web/render/tab_status.html:26
msgid "Date Added:"
-msgstr "Data d'addició"
+msgstr "Data d'addició:"
-#~ msgid "<b>Languge</b>"
+#~ msgid "<b>Language</b>"
#~ msgstr "<b>Idioma</b>"
diff --git a/deluge/i18n/cs.po b/deluge/i18n/cs.po
index 82c3c78f4..af5a9c8ee 100644
--- a/deluge/i18n/cs.po
+++ b/deluge/i18n/cs.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4463,16 +4463,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4481,7 +4481,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/cy.po b/deluge/i18n/cy.po
index 41ecee408..4f954491a 100644
--- a/deluge/i18n/cy.po
+++ b/deluge/i18n/cy.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/da.po b/deluge/i18n/da.po
index 758a19a8e..81c282521 100644
--- a/deluge/i18n/da.po
+++ b/deluge/i18n/da.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4478,16 +4478,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4496,7 +4496,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/de.po b/deluge/i18n/de.po
index 8ebb637cb..10f0dc255 100644
--- a/deluge/i18n/de.po
+++ b/deluge/i18n/de.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2020-02-13 08:19+0000\n"
-"Last-Translator: Dan Cooper <Unknown>\n"
+"PO-Revision-Date: 2023-11-06 17:15+0000\n"
+"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
"Language-Team: German <de@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -1580,7 +1580,7 @@ msgstr "Simbabwe"
#: deluge/ui/ui_entry.py:51
msgid "UI Options"
-msgstr ""
+msgstr "UI-Optionen"
#: deluge/ui/ui_entry.py:57
msgid "Set the default UI to be run, when no UI is specified"
@@ -2013,7 +2013,7 @@ msgstr "<b>IP</b> <small>%s</small>"
#: deluge/ui/console/widgets/statusbars.py:121
#: deluge/ui/web/js/deluge-all/Statusbar.js:358
msgid "n/a"
-msgstr ""
+msgstr "n/v"
#: deluge/ui/gtk3/statusbar.py:220
msgid "<b><small>Port Issue</small></b>"
@@ -2174,7 +2174,7 @@ msgstr ""
#: deluge/ui/gtk3/gtkui.py:350
msgid "Change User Interface Mode"
-msgstr ""
+msgstr "Benutzeroberflächenmodus ändern"
#: deluge/ui/gtk3/connectionmanager.py:52
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:56
@@ -2819,7 +2819,7 @@ msgstr "Mit einem Deluge-Daemon verbinden (deluged)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:22
msgid "Application Mode"
-msgstr ""
+msgstr "Anwendungsmodus"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:46
@@ -3208,7 +3208,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:108
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:38
msgid "Incoming Address"
-msgstr ""
+msgstr "Eingangsadresse"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:109
msgid "Random"
@@ -3229,7 +3229,7 @@ msgstr "Aktiven Port testen"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:113
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:58
msgid "Incoming Port"
-msgstr ""
+msgstr "Eingangsport"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:114
msgid ""
@@ -3706,7 +3706,7 @@ msgstr "Pfadeintrag anzeigen"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:6
msgid "Show file chooser"
-msgstr ""
+msgstr "Dateiauswahl anzeigen"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:7
msgid "Show folder name"
@@ -3762,7 +3762,7 @@ msgstr "Strg+D"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:22
msgid "Toggle hidden files"
-msgstr ""
+msgstr "Versteckte Dateien umschalten"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:23
msgid "Default path"
@@ -3770,7 +3770,7 @@ msgstr "Standardpfad"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:24
msgid "Shortcuts"
-msgstr ""
+msgstr "Tastenkombinationen"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:25
msgid "Select a Directory"
@@ -3813,7 +3813,7 @@ msgstr "Hinzufügen"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:32
msgid "Add the current entry value to the list"
-msgstr ""
+msgstr "Den aktuellen Eintragswert zur Liste hinzufügen"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:33
#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:98
@@ -3864,7 +3864,7 @@ msgstr "Datei-Quersumme hinzufügen"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
msgid "From Infohash"
-msgstr ""
+msgstr "Von Infohash"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
msgid "Infohash:"
@@ -4018,7 +4018,7 @@ msgstr "Gesamtgröße:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:21
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:27
msgid "Total Files:"
-msgstr ""
+msgstr "Dateien insgesamt:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:22
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:24
@@ -4067,7 +4067,7 @@ msgstr "Aktueller Tracker:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:47
msgid "Total Trackers:"
-msgstr ""
+msgstr "Tracker insgesamt:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:48
#: deluge/ui/web/render/tab_status.html:6
@@ -4481,16 +4481,16 @@ msgstr "_Normal"
msgid "_High"
msgstr "_Hoch"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Deluge-Team"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4499,7 +4499,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -4827,7 +4827,7 @@ msgstr "Festplattenspeicher vorbelegen"
#: deluge/ui/console/modes/preferences/preference_panes.py:304
msgid "Incomming Ports"
-msgstr ""
+msgstr "Eingangsports"
#: deluge/ui/console/modes/preferences/preference_panes.py:313
#: deluge/ui/console/modes/preferences/preference_panes.py:337
@@ -4846,7 +4846,7 @@ msgstr "Benutze Zufallsports"
#: deluge/ui/console/modes/preferences/preference_panes.py:352
msgid "Incoming Interface"
-msgstr ""
+msgstr "Eingangsschnittstelle"
#: deluge/ui/console/modes/preferences/preference_panes.py:355
msgid "IP address of the interface to listen on (leave empty for default):"
@@ -4935,7 +4935,7 @@ msgstr "Tauschverhältnis"
#: deluge/ui/console/modes/preferences/preference_panes.py:601
msgid "Time Ratio"
-msgstr ""
+msgstr "Zeitverhältnis"
#: deluge/ui/console/modes/preferences/preference_panes.py:609
msgid "Time (m)"
@@ -4971,7 +4971,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:712
msgid "Blocks Written"
-msgstr ""
+msgstr "Geschriebene Blöcke"
#: deluge/ui/console/modes/preferences/preference_panes.py:716
msgid "Writes"
@@ -4983,7 +4983,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:725
msgid "Blocks Read"
-msgstr ""
+msgstr "Gelesene Blöcke"
#: deluge/ui/console/modes/preferences/preference_panes.py:729
msgid "Blocks Read hit"
@@ -5400,7 +5400,7 @@ msgstr "Uploadfarbe:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:3
msgid "<b>Connections Graph</b>"
-msgstr ""
+msgstr "<b>Verbindungsgraph</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:4
msgid "<b>Bandwidth Graph</b>"
@@ -5408,7 +5408,7 @@ msgstr "<b>Bandbreitengraph</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:5
msgid "DHT nodes:"
-msgstr ""
+msgstr "DHT-Knoten:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:6
msgid "Cached DHT nodes:"
@@ -5424,7 +5424,7 @@ msgstr "<b>Seeds / Peers</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:11
msgid "<b>Graph Colors</b>"
-msgstr ""
+msgstr "<b>Graphfarben</b>"
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:35
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:47
@@ -5997,7 +5997,7 @@ msgstr "Erzwinge erneute Überprüfung"
#: deluge/ui/web/js/deluge-all/Menus.js:359
msgid "Expand All"
-msgstr ""
+msgstr "Alles ausklappen"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:13
msgid "Details"
@@ -6119,7 +6119,7 @@ msgstr "Zufälligen Port verwenden"
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:241
msgid "Type Of Service"
-msgstr ""
+msgstr "Diensttyp"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:53
msgid "Show filters with zero torrents"
@@ -6151,7 +6151,7 @@ msgstr "Server"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:140
msgid "Session Timeout:"
-msgstr ""
+msgstr "Zeitüberschreitung der Sitzung:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:165
msgid "Enable SSL (paths relative to Deluge config folder)"
@@ -6221,7 +6221,7 @@ msgstr "Datei"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:149
msgid "Infohash"
-msgstr ""
+msgstr "Infohash"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:260
msgid "Uploading your torrent..."
diff --git a/deluge/i18n/el.po b/deluge/i18n/el.po
index 310e2a9f5..0b69faafa 100644
--- a/deluge/i18n/el.po
+++ b/deluge/i18n/el.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4491,16 +4491,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4509,7 +4509,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/en_AU.po b/deluge/i18n/en_AU.po
index 62fbacbaa..4e57aaa82 100644
--- a/deluge/i18n/en_AU.po
+++ b/deluge/i18n/en_AU.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4469,16 +4469,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4487,7 +4487,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/en_CA.po b/deluge/i18n/en_CA.po
index fe0a5806f..57e64826f 100644
--- a/deluge/i18n/en_CA.po
+++ b/deluge/i18n/en_CA.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4469,16 +4469,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4487,7 +4487,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/en_GB.po b/deluge/i18n/en_GB.po
index 64c21db44..e31fc0284 100644
--- a/deluge/i18n/en_GB.po
+++ b/deluge/i18n/en_GB.po
@@ -8,18 +8,18 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-06-06 10:57+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2023-09-19 08:11+0000\n"
+"Last-Translator: Andi Chandler <Unknown>\n"
"Language-Team: English (United Kingdom) <en_GB@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
-msgstr ""
+msgstr "B"
#: deluge/common.py:412
msgid "KiB"
@@ -35,7 +35,7 @@ msgstr "GiB"
#: deluge/common.py:415
msgid "TiB"
-msgstr ""
+msgstr "TiB"
#: deluge/common.py:416
msgid "K"
@@ -51,7 +51,7 @@ msgstr "G"
#: deluge/common.py:419
msgid "T"
-msgstr ""
+msgstr "T"
#: deluge/common.py:515 deluge/ui/gtk3/statusbar.py:442
#: deluge/ui/gtk3/statusbar.py:455 deluge/ui/gtk3/statusbar.py:464
@@ -62,7 +62,7 @@ msgstr ""
#: deluge/ui/gtk3/systemtray.py:274 deluge/ui/gtk3/systemtray.py:442
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:40
msgid "K/s"
-msgstr ""
+msgstr "K/s"
#: deluge/common.py:515 deluge/ui/gtk3/menubar.py:449
#: deluge/ui/gtk3/menubar.py:455
@@ -80,7 +80,7 @@ msgstr "KiB/s"
#: deluge/common.py:521
msgid "M/s"
-msgstr ""
+msgstr "M/s"
#: deluge/common.py:521
msgid "MiB/s"
@@ -88,7 +88,7 @@ msgstr "MiB/s"
#: deluge/common.py:527
msgid "G/s"
-msgstr ""
+msgstr "G/s"
#: deluge/common.py:527
msgid "GiB/s"
@@ -96,35 +96,35 @@ msgstr "GiB/s"
#: deluge/common.py:533
msgid "T/s"
-msgstr ""
+msgstr "T/s"
#: deluge/common.py:533
msgid "TiB/s"
-msgstr ""
+msgstr "TiB/s"
#: deluge/argparserbase.py:172
msgid "Common Options"
-msgstr ""
+msgstr "Common Options"
#: deluge/argparserbase.py:175
msgid "Print this help message"
-msgstr ""
+msgstr "Print this help message"
#: deluge/argparserbase.py:182
msgid "Print version information"
-msgstr ""
+msgstr "Print version information"
#: deluge/argparserbase.py:194
msgid "Set the config directory path"
-msgstr ""
+msgstr "Set the config directory path"
#: deluge/argparserbase.py:200
msgid "Output to specified logfile instead of stdout"
-msgstr ""
+msgstr "Output to specified logfile instead of stdout"
#: deluge/argparserbase.py:206
msgid "Set the log level (none, error, warning, info, debug)"
-msgstr ""
+msgstr "Set the log level (none, error, warning, info, debug)"
#: deluge/argparserbase.py:215
#, python-format
@@ -132,10 +132,12 @@ msgid ""
"Enable logfile rotation, with optional maximum logfile size, default: "
"%(const)s (Logfile rotation count is 5)"
msgstr ""
+"Enable logfile rotation, with optional maximum logfile size, default: "
+"%(const)s (Logfile rotation count is 5)"
#: deluge/argparserbase.py:223
msgid "Quieten logging output (Same as `--loglevel none`)"
-msgstr ""
+msgstr "Quieten logging output (Same as `--loglevel none`)"
#: deluge/argparserbase.py:231
#, python-format
@@ -143,51 +145,54 @@ msgid ""
"Profile %(prog)s with cProfile. Outputs to stdout unless a filename is "
"specified"
msgstr ""
+"Profile %(prog)s with cProfile. Outputs to stdout unless a filename is "
+"specified"
#: deluge/argparserbase.py:351
msgid "Process Control Options"
-msgstr ""
+msgstr "Process Control Options"
#: deluge/argparserbase.py:357
msgid "Pidfile to store the process id"
-msgstr ""
+msgstr "Pidfile to store the process id"
#: deluge/argparserbase.py:365
msgid "Do not daemonize (fork) this process"
-msgstr ""
+msgstr "Do not daemonise (fork) this process"
#: deluge/argparserbase.py:379
msgid "Change to this user on startup (Requires root)"
-msgstr ""
+msgstr "Change to this user on startup (Requires root)"
#: deluge/argparserbase.py:386
msgid "Change to this group on startup (Requires root)"
-msgstr ""
+msgstr "Change to this group on startup (Requires root)"
#: deluge/core/daemon_entry.py:25
msgid "Daemon Options"
-msgstr ""
+msgstr "Daemon Options"
#: deluge/core/daemon_entry.py:31
msgid "IP address to listen for UI connections"
-msgstr ""
+msgstr "IP address to listen for UI connections"
#: deluge/core/daemon_entry.py:39
msgid "Port to listen for UI connections on"
-msgstr ""
+msgstr "Port to listen for UI connections on"
#: deluge/core/daemon_entry.py:47
msgid "IP address to listen for BitTorrent connections"
-msgstr ""
+msgstr "IP address to listen for BitTorrent connections"
#: deluge/core/daemon_entry.py:56
msgid ""
"The network interface name or IP address for outgoing BitTorrent connections."
msgstr ""
+"The network interface name or IP address for outgoing BitTorrent connections."
#: deluge/core/daemon_entry.py:63
msgid "Config keys to be unmodified by `set_config` RPC"
-msgstr ""
+msgstr "Config keys to be unmodified by `set_config` RPC"
#: deluge/ui/common.py:37 deluge/ui/gtk3/filtertreeview.py:130
#: deluge/ui/web/js/deluge-all/UI.js:18
@@ -202,7 +207,7 @@ msgstr "Active"
#: deluge/ui/common.py:39 deluge/ui/web/js/deluge-all/UI.js:20
msgid "Allocating"
-msgstr ""
+msgstr "Allocating"
#: deluge/ui/common.py:40 deluge/ui/web/js/deluge-all/UI.js:21
#: deluge/ui/web/js/deluge-all/UI.js:25
@@ -288,7 +293,7 @@ msgstr "Uploaded"
#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
msgid "Remaining"
-msgstr ""
+msgstr "Remaining"
#: deluge/ui/common.py:58 deluge/ui/gtk3/torrentview.py:373
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:165
@@ -321,11 +326,11 @@ msgstr "Up Limit"
#: deluge/ui/common.py:63 deluge/ui/web/js/deluge-all/add/OptionsTab.js:101
msgid "Max Connections"
-msgstr ""
+msgstr "Max Connections"
#: deluge/ui/common.py:64 deluge/ui/web/js/deluge-all/add/OptionsTab.js:109
msgid "Max Upload Slots"
-msgstr ""
+msgstr "Max Upload Slots"
#: deluge/ui/common.py:65 deluge/ui/gtk3/torrentview.py:325
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:136
@@ -336,7 +341,7 @@ msgstr "Peers"
#: deluge/ui/common.py:66 deluge/ui/gtk3/torrentview.py:317
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:128
msgid "Seeds"
-msgstr ""
+msgstr "Seeds"
#: deluge/ui/common.py:67 deluge/ui/gtk3/torrentview.py:380
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:173
@@ -346,7 +351,7 @@ msgstr "Avail"
#: deluge/ui/common.py:68 deluge/ui/gtk3/torrentview.py:333
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:284
msgid "Seeds:Peers"
-msgstr ""
+msgstr "Seeds:Peers"
#: deluge/ui/common.py:69 deluge/ui/gtk3/listview.py:203
#: deluge/ui/gtk3/torrentview.py:387
@@ -367,33 +372,33 @@ msgstr "Tracker"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:213
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:31
msgid "Download Folder"
-msgstr ""
+msgstr "Download Folder"
#: deluge/ui/common.py:75
msgid "Seeding Time"
-msgstr ""
+msgstr "Seeding Time"
#: deluge/ui/common.py:76
msgid "Active Time"
-msgstr ""
+msgstr "Active Time"
#: deluge/ui/common.py:78
msgid "Last Activity"
-msgstr ""
+msgstr "Last Activity"
#: deluge/ui/common.py:81
msgid "Finished Time"
-msgstr ""
+msgstr "Finished Time"
#: deluge/ui/common.py:83 deluge/ui/gtk3/torrentview.py:401
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:189
msgid "Complete Seen"
-msgstr ""
+msgstr "Complete Seen"
#: deluge/ui/common.py:86 deluge/ui/gtk3/torrentview.py:394
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:197
msgid "Completed"
-msgstr ""
+msgstr "Completed"
#: deluge/ui/common.py:87 deluge/ui/gtk3/torrentview.py:366
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:158
@@ -404,7 +409,7 @@ msgstr "ETA"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:236
msgid "Shared"
-msgstr ""
+msgstr "Shared"
#: deluge/ui/common.py:90 deluge/ui/gtk3/glade/main_window.tabs.ui.h:31
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:287
@@ -415,7 +420,7 @@ msgstr "Prioritise First/Last"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:14
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:143
msgid "Sequential Download"
-msgstr ""
+msgstr "Sequential Download"
#: deluge/ui/common.py:97 deluge/ui/common.py:98
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:35
@@ -427,27 +432,27 @@ msgstr "Auto Managed"
#: deluge/ui/common.py:99
msgid "Stop At Ratio"
-msgstr ""
+msgstr "Stop At Ratio"
#: deluge/ui/common.py:100
msgid "Stop Ratio"
-msgstr ""
+msgstr "Stop Ratio"
#: deluge/ui/common.py:101
msgid "Remove At Ratio"
-msgstr ""
+msgstr "Remove At Ratio"
#: deluge/ui/common.py:102 deluge/ui/common.py:108
msgid "Move On Completed"
-msgstr ""
+msgstr "Move On Completed"
#: deluge/ui/common.py:104
msgid "Move Completed Path"
-msgstr ""
+msgstr "Move Completed Path"
#: deluge/ui/common.py:112
msgid "Move On Completed Path"
-msgstr ""
+msgstr "Move On Completed Path"
#: deluge/ui/common.py:115 deluge/ui/gtk3/filtertreeview.py:135
#: deluge/ui/gtk3/torrentview.py:416
@@ -455,7 +460,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/FilterPanel.js:32
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:221
msgid "Owner"
-msgstr ""
+msgstr "Owner"
#: deluge/ui/common.py:116
msgid "Pieces"
@@ -463,13 +468,13 @@ msgstr "Pieces"
#: deluge/ui/common.py:117
msgid "Seed Rank"
-msgstr ""
+msgstr "Seed Rank"
#: deluge/ui/common.py:118 deluge/ui/gtk3/glade/main_window.tabs.ui.h:33
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:22
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:294
msgid "Super Seeding"
-msgstr ""
+msgstr "Super Seeding"
#: deluge/ui/common.py:123 deluge/ui/web/js/deluge-all/details/StatusTab.js:122
msgid "Warning"
@@ -567,28 +572,30 @@ msgstr "Plug-ins"
#: deluge/ui/common.py:150 deluge/ui/web/js/deluge-all/Deluge.js:154
#: deluge/ui/web/js/deluge-all/Menus.js:365
msgid "Skip"
-msgstr ""
+msgstr "Skip"
#: deluge/ui/common.py:151 deluge/ui/web/js/deluge-all/Deluge.js:155
#: deluge/ui/web/js/deluge-all/Menus.js:371
msgid "Low"
-msgstr ""
+msgstr "Low"
#: deluge/ui/common.py:152 deluge/ui/web/js/deluge-all/Deluge.js:156
#: deluge/ui/web/js/deluge-all/Menus.js:377
msgid "Normal"
-msgstr ""
+msgstr "Normal"
#: deluge/ui/common.py:153 deluge/ui/web/js/deluge-all/Deluge.js:157
#: deluge/ui/web/js/deluge-all/Menus.js:383
msgid "High"
-msgstr ""
+msgstr "High"
#: deluge/ui/client.py:681
msgid ""
"Deluge cannot find the `deluged` executable, check that the deluged package "
"is installed, or added to your PATH."
msgstr ""
+"Deluge cannot find the `deluged` executable, check that the deluged package "
+"is installed, or added to your PATH."
#: deluge/ui/countries.py:10
msgid "Afghanistan"
@@ -1448,7 +1455,7 @@ msgstr "Syrian Arab Republic"
#: deluge/ui/countries.py:224
msgid "Taiwan"
-msgstr ""
+msgstr "Taiwan"
#: deluge/ui/countries.py:225
msgid "Tajikistan"
@@ -1576,45 +1583,47 @@ msgstr "Zimbabwe"
#: deluge/ui/ui_entry.py:51
msgid "UI Options"
-msgstr ""
+msgstr "UI Options"
#: deluge/ui/ui_entry.py:57
msgid "Set the default UI to be run, when no UI is specified"
-msgstr ""
+msgstr "Set the default UI to be run, when no UI is specified"
#: deluge/ui/ui_entry.py:91
msgid ""
"Alternative UI to launch, with optional ui args \n"
" (default UI: *)"
msgstr ""
+"Alternative UI to launch, with optional UI args \n"
+" (default UI: *)"
#: deluge/ui/web/web.py:32
msgid "Web Server Options"
-msgstr ""
+msgstr "Web Server Options"
#: deluge/ui/web/web.py:38
msgid "IP address for web server to listen on"
-msgstr ""
+msgstr "IP address for web server to listen on"
#: deluge/ui/web/web.py:46
msgid "Port for web server to listen on"
-msgstr ""
+msgstr "Port for web server to listen on"
#: deluge/ui/web/web.py:53
msgid "Set the base path that the ui is running on"
-msgstr ""
+msgstr "Set the base path that the UI is running on"
#: deluge/ui/web/web.py:56
msgid "Force the web server to use SSL"
-msgstr ""
+msgstr "Force the web server to use SSL"
#: deluge/ui/web/web.py:61
msgid "Force the web server to disable SSL"
-msgstr ""
+msgstr "Force the web server to disable SSL"
#: deluge/ui/web/json_api.py:868
msgid "Daemon does not exist"
-msgstr ""
+msgstr "Daemon does not exist"
#: deluge/ui/web/json_api.py:875
msgid "Daemon not running"
@@ -1660,13 +1669,13 @@ msgstr "Choose a file"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:2
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:2
msgid "_Cancel"
-msgstr ""
+msgstr "_Cancel"
#: deluge/ui/gtk3/createtorrentdialog.py:134
#: deluge/ui/gtk3/createtorrentdialog.py:171
#: deluge/ui/gtk3/addtorrentdialog.py:700 deluge/ui/gtk3/preferences.py:1160
msgid "_Open"
-msgstr ""
+msgstr "_Open"
#: deluge/ui/gtk3/createtorrentdialog.py:165
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:28
@@ -1682,7 +1691,7 @@ msgstr "Save .torrent file"
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:3
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:30
msgid "_Save"
-msgstr ""
+msgstr "_Save"
#: deluge/ui/gtk3/createtorrentdialog.py:271
#: deluge/ui/gtk3/addtorrentdialog.py:712
@@ -1705,7 +1714,7 @@ msgstr "Enter your password to Quit Deluge..."
#: deluge/ui/gtk3/mainwindow.py:343
#, python-brace-format
msgid "D: {download_rate} U: {upload_rate} - Deluge"
-msgstr ""
+msgstr "D: {download_rate} U: {upload_rate} - Deluge"
#: deluge/ui/gtk3/mainwindow.py:357 deluge/ui/gtk3/aboutdialog.py:26
#: deluge/ui/gtk3/aboutdialog.py:27 deluge/ui/gtk3/systemtray.py:96
@@ -1719,16 +1728,16 @@ msgstr "Deluge"
#: deluge/ui/gtk3/path_combo_chooser.py:393
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:20
msgid "Edit path"
-msgstr ""
+msgstr "Edit path"
#: deluge/ui/gtk3/path_combo_chooser.py:395
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:21
msgid "Remove path"
-msgstr ""
+msgstr "Remove path"
#: deluge/ui/gtk3/options_tab.py:136
msgid "_Apply to selected"
-msgstr ""
+msgstr "_Apply to selected"
#: deluge/ui/gtk3/aboutdialog.py:40
#, python-format
@@ -1824,17 +1833,18 @@ msgstr "Torrent"
#: deluge/ui/gtk3/addtorrentdialog.py:232
#, python-format
msgid "Add Torrents (%d)"
-msgstr ""
+msgstr "Add Torrents (%d)"
#: deluge/ui/gtk3/addtorrentdialog.py:238
msgid "Duplicate torrent(s)"
-msgstr ""
+msgstr "Duplicate torrent(s)"
#: deluge/ui/gtk3/addtorrentdialog.py:240
#, python-format
msgid ""
"You cannot add the same torrent twice. %d torrents were already added."
msgstr ""
+"You cannot add the same torrent twice. %d torrents were already added."
#: deluge/ui/gtk3/addtorrentdialog.py:255
msgid "Invalid File"
@@ -1843,11 +1853,11 @@ msgstr "Invalid File"
#: deluge/ui/gtk3/addtorrentdialog.py:290
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:8
msgid "Please wait for files..."
-msgstr ""
+msgstr "Please wait for files..."
#: deluge/ui/gtk3/addtorrentdialog.py:296
msgid "Unable to download files for this magnet"
-msgstr ""
+msgstr "Unable to download files for this magnet"
#: deluge/ui/gtk3/addtorrentdialog.py:694
msgid "Choose a .torrent file"
@@ -1875,18 +1885,18 @@ msgstr "Failed to download:"
#: deluge/ui/gtk3/dialogs.py:110
msgid "_No"
-msgstr ""
+msgstr "_No"
#: deluge/ui/gtk3/dialogs.py:110
msgid "_Yes"
-msgstr ""
+msgstr "_Yes"
#: deluge/ui/gtk3/dialogs.py:132 deluge/ui/gtk3/dialogs.py:156
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:2
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:2
#: deluge/ui/gtk3/glade/connection_manager.ui.h:2
msgid "_Close"
-msgstr ""
+msgstr "_Close"
#: deluge/ui/gtk3/dialogs.py:179
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:195
@@ -1896,12 +1906,12 @@ msgstr "Details:"
#: deluge/ui/gtk3/dialogs.py:200
msgid "Authenticate"
-msgstr ""
+msgstr "Authenticate"
#: deluge/ui/gtk3/dialogs.py:203 deluge/ui/gtk3/connectionmanager.py:211
#: deluge/ui/gtk3/glade/connection_manager.ui.h:3
msgid "C_onnect"
-msgstr ""
+msgstr "C_onnect"
#: deluge/ui/gtk3/dialogs.py:209 deluge/ui/gtk3/dialogs.py:281
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:139
@@ -1929,25 +1939,25 @@ msgstr "Password:"
#: deluge/ui/gtk3/dialogs.py:257
msgid "Edit Account"
-msgstr ""
+msgstr "Edit Account"
#: deluge/ui/gtk3/dialogs.py:258
msgid "Edit existing account"
-msgstr ""
+msgstr "Edit existing account"
#: deluge/ui/gtk3/dialogs.py:263 deluge/ui/gtk3/dialogs.py:364
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:16
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:38
msgid "_Apply"
-msgstr ""
+msgstr "_Apply"
#: deluge/ui/gtk3/dialogs.py:270
msgid "New Account"
-msgstr ""
+msgstr "New Account"
#: deluge/ui/gtk3/dialogs.py:271
msgid "Create a new account"
-msgstr ""
+msgstr "Create a new account"
#: deluge/ui/gtk3/dialogs.py:273 deluge/ui/gtk3/glade/queuedtorrents.ui.h:3
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:191
@@ -1956,11 +1966,11 @@ msgstr ""
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:20
#: deluge/ui/gtk3/glade/connection_manager.ui.h:4
msgid "_Add"
-msgstr ""
+msgstr "_Add"
#: deluge/ui/gtk3/dialogs.py:289
msgid "Authentication Level:"
-msgstr ""
+msgstr "Authentication Level:"
#: deluge/ui/gtk3/dialogs.py:423
msgid "Password Protected"
@@ -1980,7 +1990,7 @@ msgstr "Password Protected"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:3
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:3
msgid "_OK"
-msgstr ""
+msgstr "_OK"
#: deluge/ui/gtk3/common.py:155 deluge/ui/gtk3/menubar.py:83
msgid "Other..."
@@ -1992,19 +2002,19 @@ msgstr "Not Connected"
#: deluge/ui/gtk3/statusbar.py:175
msgid "Connections (Limit)"
-msgstr ""
+msgstr "Connections (Limit)"
#: deluge/ui/gtk3/statusbar.py:182
msgid "Download Speed (Limit)"
-msgstr ""
+msgstr "Download Speed (Limit)"
#: deluge/ui/gtk3/statusbar.py:189
msgid "Upload Speed (Limit)"
-msgstr ""
+msgstr "Upload Speed (Limit)"
#: deluge/ui/gtk3/statusbar.py:196
msgid "Protocol Traffic (Down:Up)"
-msgstr ""
+msgstr "Protocol Traffic (Down:Up)"
#: deluge/ui/gtk3/statusbar.py:201 deluge/ui/web/js/deluge-all/Statusbar.js:234
msgid "DHT Nodes"
@@ -2016,70 +2026,70 @@ msgstr "Free Disk Space"
#: deluge/ui/gtk3/statusbar.py:212 deluge/ui/web/js/deluge-all/Statusbar.js:226
msgid "External IP Address"
-msgstr ""
+msgstr "External IP Address"
#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:409
#, python-format
msgid "<b>IP</b> <small>%s</small>"
-msgstr ""
+msgstr "<b>IP</b> <small>%s</small>"
#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:408
#: deluge/ui/console/widgets/statusbars.py:121
#: deluge/ui/web/js/deluge-all/Statusbar.js:358
msgid "n/a"
-msgstr ""
+msgstr "n/a"
#: deluge/ui/gtk3/statusbar.py:220
msgid "<b><small>Port Issue</small></b>"
-msgstr ""
+msgstr "<b><small>Port Issue</small></b>"
#: deluge/ui/gtk3/statusbar.py:222
msgid "No incoming connections, check port forwarding"
-msgstr ""
+msgstr "No incoming connections, check port forwarding"
#: deluge/ui/gtk3/statusbar.py:475 deluge/ui/gtk3/systemtray.py:394
#: deluge/ui/gtk3/menubar.py:447
msgid "Download Speed Limit"
-msgstr ""
+msgstr "Download Speed Limit"
#: deluge/ui/gtk3/statusbar.py:476 deluge/ui/gtk3/systemtray.py:395
#: deluge/ui/gtk3/menubar.py:448
msgid "Set the maximum download speed"
-msgstr ""
+msgstr "Set the maximum download speed"
#: deluge/ui/gtk3/statusbar.py:482 deluge/ui/gtk3/systemtray.py:409
#: deluge/ui/gtk3/menubar.py:453
msgid "Upload Speed Limit"
-msgstr ""
+msgstr "Upload Speed Limit"
#: deluge/ui/gtk3/statusbar.py:483 deluge/ui/gtk3/systemtray.py:410
#: deluge/ui/gtk3/menubar.py:454
msgid "Set the maximum upload speed"
-msgstr ""
+msgstr "Set the maximum upload speed"
#: deluge/ui/gtk3/statusbar.py:489 deluge/ui/gtk3/menubar.py:459
msgid "Incoming Connections"
-msgstr ""
+msgstr "Incoming Connections"
#: deluge/ui/gtk3/statusbar.py:490 deluge/ui/gtk3/menubar.py:460
msgid "Set the maximum incoming connections"
-msgstr ""
+msgstr "Set the maximum incoming connections"
#: deluge/ui/gtk3/tab_data_funcs.py:28
#, python-brace-format
msgid "{state} {percent}%"
-msgstr ""
+msgstr "{state} {percent}%"
#: deluge/ui/gtk3/tab_data_funcs.py:30
#, python-brace-format
msgid "{state}: {err_msg}"
-msgstr ""
+msgstr "{state}: {err_msg}"
#: deluge/ui/gtk3/tab_data_funcs.py:42
#: deluge/ui/gtk3/torrentview_data_funcs.py:284
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:74
msgid "Never"
-msgstr ""
+msgstr "Never"
#: deluge/ui/gtk3/tab_data_funcs.py:96
msgid "Yes"
@@ -2128,7 +2138,7 @@ msgstr "_Options"
#: deluge/ui/gtk3/torrentdetails.py:148
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:52
msgid "_Trackers"
-msgstr ""
+msgstr "_Trackers"
#: deluge/ui/gtk3/systemtray.py:184
msgid "Not Connected..."
@@ -2169,26 +2179,32 @@ msgid ""
"A Deluge daemon (deluged) is already running.\n"
"To use Standalone mode, stop local daemon and restart Deluge."
msgstr ""
+"A Deluge daemon (deluged) is already running.\n"
+"To use Standalone mode, stop the local daemon and restart Deluge."
#: 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 ""
+"Only Thin Client mode is available because libtorrent is not installed.\n"
+"To use Standalone mode, please install libtorrent package."
#: 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 ""
+"Only Thin Client mode is available due to unknown Import Error.\n"
+"To use Standalone mode, please see logs for error details."
#: deluge/ui/gtk3/gtkui.py:349
msgid "Continue in Thin Client mode?"
-msgstr ""
+msgstr "Continue in Thin Client mode?"
#: deluge/ui/gtk3/gtkui.py:350
msgid "Change User Interface Mode"
-msgstr ""
+msgstr "Change User Interface Mode"
#: deluge/ui/gtk3/connectionmanager.py:52
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:56
@@ -2233,7 +2249,7 @@ msgstr "_Stop Daemon"
#: deluge/ui/gtk3/connectionmanager.py:255
msgid "_Disconnect"
-msgstr ""
+msgstr "_Disconnect"
#: deluge/ui/gtk3/connectionmanager.py:280
msgid "Unable to start daemon!"
@@ -2241,25 +2257,27 @@ msgstr "Unable to start daemon!"
#: deluge/ui/gtk3/connectionmanager.py:281
msgid "Check deluged package is installed and logs for further details"
-msgstr ""
+msgstr "Check deluged package is installed and logs for further details"
#: deluge/ui/gtk3/connectionmanager.py:332
msgid "Incompatible Client"
-msgstr ""
+msgstr "Incompatible Client"
#: deluge/ui/gtk3/connectionmanager.py:343
msgid ""
"Auto-starting the daemon locally is not enabled. See \"Options\" on the "
"\"Connection Manager\"."
msgstr ""
+"Auto-starting the daemon locally is not enabled. See \"Options\" on the "
+"\"Connection Manager\"."
#: deluge/ui/gtk3/connectionmanager.py:346
msgid "Failed To Connect"
-msgstr ""
+msgstr "Failed To Connect"
#: deluge/ui/gtk3/connectionmanager.py:403
msgid "Edit Host"
-msgstr ""
+msgstr "Edit Host"
#: deluge/ui/gtk3/connectionmanager.py:428
msgid "Error Adding Host"
@@ -2267,7 +2285,7 @@ msgstr "Error Adding Host"
#: deluge/ui/gtk3/connectionmanager.py:464
msgid "Error Updating Host"
-msgstr ""
+msgstr "Error Updating Host"
#: deluge/ui/gtk3/preferences.py:131
#: deluge/ui/console/cmdline/commands/connect.py:33
@@ -2298,33 +2316,33 @@ msgstr "Plug-in"
#: deluge/ui/gtk3/preferences.py:876 deluge/ui/gtk3/preferences.py:886
msgid "Attention"
-msgstr ""
+msgstr "Attention"
#: deluge/ui/gtk3/preferences.py:876
msgid "You must choose a language"
-msgstr ""
+msgstr "You must choose a language"
#: deluge/ui/gtk3/preferences.py:887
msgid "You must now restart the deluge UI for the changes to take effect."
-msgstr ""
+msgstr "You must now restart the Deluge UI for the changes to take effect."
#: deluge/ui/gtk3/preferences.py:940
msgid "Thinclient"
-msgstr ""
+msgstr "Thinclient"
#: deluge/ui/gtk3/preferences.py:940
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:18
msgid "Standalone"
-msgstr ""
+msgstr "Standalone"
#: deluge/ui/gtk3/preferences.py:942
msgid "Switching Deluge Client Mode..."
-msgstr ""
+msgstr "Switching Deluge Client Mode..."
#: deluge/ui/gtk3/preferences.py:943
#, python-format
msgid "Do you want to restart to use %s mode?"
-msgstr ""
+msgstr "Do you want to restart to use %s mode?"
#: deluge/ui/gtk3/preferences.py:1154
msgid "Select the Plugin"
@@ -2336,35 +2354,35 @@ msgstr "Plug-in Eggs"
#: deluge/ui/gtk3/preferences.py:1297
msgid "Server Side Error"
-msgstr ""
+msgstr "Server Side Error"
#: deluge/ui/gtk3/preferences.py:1298
msgid "An error occurred on the server"
-msgstr ""
+msgstr "An error occurred on the server"
#: deluge/ui/gtk3/preferences.py:1368 deluge/ui/gtk3/preferences.py:1375
msgid "Error Adding Account"
-msgstr ""
+msgstr "Error Adding Account"
#: deluge/ui/gtk3/preferences.py:1369
msgid "Authentication failed"
-msgstr ""
+msgstr "Authentication failed"
#: deluge/ui/gtk3/preferences.py:1376
msgid "An error occurred while adding account"
-msgstr ""
+msgstr "An error occurred while adding account"
#: deluge/ui/gtk3/preferences.py:1408
msgid "Error Updating Account"
-msgstr ""
+msgstr "Error Updating Account"
#: deluge/ui/gtk3/preferences.py:1409
msgid "An error occurred while updating account"
-msgstr ""
+msgstr "An error occurred while updating account"
#: deluge/ui/gtk3/preferences.py:1427
msgid "Remove Account"
-msgstr ""
+msgstr "Remove Account"
#: deluge/ui/gtk3/preferences.py:1429
#, python-format
@@ -2372,18 +2390,20 @@ msgid ""
"Are you sure you want to remove the account with the username "
"\"%(username)s\"?"
msgstr ""
+"Are you sure you want to remove the account with the username "
+"\"%(username)s\"?"
#: deluge/ui/gtk3/preferences.py:1441 deluge/ui/gtk3/preferences.py:1448
msgid "Error Removing Account"
-msgstr ""
+msgstr "Error Removing Account"
#: deluge/ui/gtk3/preferences.py:1442
msgid "Auhentication failed"
-msgstr ""
+msgstr "Auhentication failed"
#: deluge/ui/gtk3/preferences.py:1449
msgid "An error occurred while removing account"
-msgstr ""
+msgstr "An error occurred while removing account"
#: deluge/ui/gtk3/filtertreeview.py:122
#: deluge/ui/web/js/deluge-all/FilterPanel.js:28
@@ -2419,7 +2439,7 @@ msgstr "No Label"
#: deluge/ui/gtk3/filtertreeview.py:206
msgid "No Owner"
-msgstr ""
+msgstr "No Owner"
#: deluge/ui/gtk3/new_release_dialog.py:60
msgid "<i>Client Version</i>"
@@ -2435,16 +2455,16 @@ msgstr " Torrent Queued"
#: deluge/ui/gtk3/torrentview.py:421
msgid "Torrent is shared between other Deluge users or not."
-msgstr ""
+msgstr "Torrent is shared between other Deluge users or not."
#: deluge/ui/gtk3/removetorrentdialog.py:67
msgid "Remove the selected torrents?"
-msgstr ""
+msgstr "Remove the selected torrents?"
#: deluge/ui/gtk3/removetorrentdialog.py:68
#, python-format
msgid "Total of %s torrents selected"
-msgstr ""
+msgstr "Total of %s torrents selected"
#: deluge/ui/gtk3/menubar.py:79
msgid "Set Unlimited"
@@ -2464,27 +2484,27 @@ msgstr "Disable"
#: deluge/ui/gtk3/menubar.py:104
msgid "Enable..."
-msgstr ""
+msgstr "Enable..."
#: deluge/ui/gtk3/menubar.py:465
msgid "Peer Upload Slots"
-msgstr ""
+msgstr "Peer Upload Slots"
#: deluge/ui/gtk3/menubar.py:466
msgid "Set the maximum upload slots"
-msgstr ""
+msgstr "Set the maximum upload slots"
#: deluge/ui/gtk3/menubar.py:471
msgid "Stop Seed At Ratio"
-msgstr ""
+msgstr "Stop Seed At Ratio"
#: deluge/ui/gtk3/menubar.py:606
msgid "Ownership Change Error"
-msgstr ""
+msgstr "Ownership Change Error"
#: deluge/ui/gtk3/menubar.py:607
msgid "There was an error while trying changing ownership."
-msgstr ""
+msgstr "There was an error while trying changing ownership."
#: deluge/ui/gtk3/peers_tab.py:91
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:66
@@ -2498,13 +2518,15 @@ msgstr "Client"
#: deluge/ui/gtk3/__init__.py:29
msgid "GTK Options"
-msgstr ""
+msgstr "GTK Options"
#: deluge/ui/gtk3/__init__.py:36
msgid ""
"Add one or more torrent files, torrent URLs or magnet URIs to a currently "
"running Deluge GTK instance"
msgstr ""
+"Add one or more torrent files, torrent URLs or magnet URIs to a currently "
+"running Deluge GTK instance"
#: deluge/ui/gtk3/glade/create_torrent_dialog.progress.ui.h:1
msgid "Creating Torrent"
@@ -2516,7 +2538,7 @@ msgstr "Queued Torrents"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:4
msgid "Add Queued Torrents"
-msgstr ""
+msgstr "Add Queued Torrents"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:5
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:7
@@ -2528,7 +2550,7 @@ msgstr "_Remove"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:6
msgid "_Clear"
-msgstr ""
+msgstr "_Clear"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:7
msgid "Automatically add torrents on connect"
@@ -2567,7 +2589,7 @@ msgstr "_Edit"
#: deluge/ui/gtk3/glade/main_window.ui.h:7
msgid "_Preferences"
-msgstr ""
+msgstr "_Preferences"
#: deluge/ui/gtk3/glade/main_window.ui.h:8
msgid "_Connection Manager"
@@ -2603,7 +2625,7 @@ msgstr "_Columns"
#: deluge/ui/gtk3/glade/main_window.ui.h:16
msgid "_Find ..."
-msgstr ""
+msgstr "_Find ..."
#: deluge/ui/gtk3/glade/main_window.ui.h:17
msgid "S_idebar"
@@ -2619,7 +2641,7 @@ msgstr "Show _Trackers"
#: deluge/ui/gtk3/glade/main_window.ui.h:20
msgid "Show _Owners"
-msgstr ""
+msgstr "Show _Owners"
#: deluge/ui/gtk3/glade/main_window.ui.h:21
msgid "_Help"
@@ -2643,7 +2665,7 @@ msgstr "_Community"
#: deluge/ui/gtk3/glade/main_window.ui.h:26
msgid "_About"
-msgstr ""
+msgstr "_About"
#: deluge/ui/gtk3/glade/main_window.ui.h:27
msgid "Add torrent"
@@ -2672,10 +2694,12 @@ msgid ""
"Filter torrents by name.\n"
"This will filter torrents for the current selection on the sidebar."
msgstr ""
+"Filter torrents by name.\n"
+"This will filter torrents for the current selection on the sidebar."
#: deluge/ui/gtk3/glade/main_window.ui.h:33
msgid "Filter"
-msgstr ""
+msgstr "Filter"
#: deluge/ui/gtk3/glade/main_window.ui.h:34
msgid "Pause the selected torrents"
@@ -2736,19 +2760,19 @@ msgstr "Connection Manager"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:211
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:86
msgid "Close"
-msgstr ""
+msgstr "Close"
#: deluge/ui/gtk3/glade/main_window.ui.h:45
msgid "Filter:"
-msgstr ""
+msgstr "Filter:"
#: deluge/ui/gtk3/glade/main_window.ui.h:46
msgid "Clear the search"
-msgstr ""
+msgstr "Clear the search"
#: deluge/ui/gtk3/glade/main_window.ui.h:47
msgid "_Match Case"
-msgstr ""
+msgstr "_Match Case"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:1
#: deluge/ui/console/modes/preferences/preference_panes.py:383
@@ -2776,7 +2800,7 @@ msgstr "Handshake"
#: deluge/ui/console/modes/preferences/preference_panes.py:400
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:88
msgid "Full Stream"
-msgstr ""
+msgstr "Full Stream"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:6
#: deluge/ui/console/modes/preferences/preference_panes.py:400
@@ -2787,17 +2811,17 @@ msgstr "Either"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:8
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:34
msgid "Socks4"
-msgstr ""
+msgstr "Socks4"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:9
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:35
msgid "Socks5"
-msgstr ""
+msgstr "Socks5"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:10
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:36
msgid "Socks5 Auth"
-msgstr ""
+msgstr "Socks5 Auth"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:11
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:37
@@ -2807,28 +2831,28 @@ msgstr "HTTP"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:12
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:38
msgid "HTTP Auth"
-msgstr ""
+msgstr "HTTP Auth"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:13
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:39
msgid "I2P"
-msgstr ""
+msgstr "I2P"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:19
msgid "The standalone self-contained application"
-msgstr ""
+msgstr "The standalone self-contained application"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:20
msgid "Thin Client"
-msgstr ""
+msgstr "Thin Client"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:21
msgid "Connect to a Deluge daemon (deluged)"
-msgstr ""
+msgstr "Connect to a Deluge daemon (deluged)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:22
msgid "Application Mode"
-msgstr ""
+msgstr "Application Mode"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:46
@@ -2845,45 +2869,48 @@ msgid ""
"will increase bandwidth use between client\n"
"and daemon (does not apply in Standalone mode)."
msgstr ""
+"The pieces bar\n"
+"will increase bandwidth use between client\n"
+"and daemon (does not apply in Standalone mode)."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:28
msgid "Show a pieces bar in Status tab"
-msgstr ""
+msgstr "Show a pieces bar in Status tab"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:29
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:25
#: deluge/ui/web/render/tab_status.html:27
msgid "Completed:"
-msgstr ""
+msgstr "Completed:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:30
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:72
msgid "Downloading:"
-msgstr ""
+msgstr "Downloading:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:31
msgid "Waiting:"
-msgstr ""
+msgstr "Waiting:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:32
msgid "Missing:"
-msgstr ""
+msgstr "Missing:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:33
msgid "_Revert"
-msgstr ""
+msgstr "_Revert"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:34
msgid "Revert color to default"
-msgstr ""
+msgstr "Revert colour to default"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:35
msgid "Piece Colors"
-msgstr ""
+msgstr "Piece Colours"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:36
msgid "Main Window"
-msgstr ""
+msgstr "Main Window"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:37
msgid "Enable system tray icon"
@@ -2891,11 +2918,11 @@ msgstr "Enable system tray icon"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:38
msgid "App Indicator"
-msgstr ""
+msgstr "App Indicator"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:39
msgid "Systray"
-msgstr ""
+msgstr "Systray"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:40
msgid "Minimize to tray on close"
@@ -2911,25 +2938,25 @@ msgstr "Password protect system tray"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:44
msgid "System Tray"
-msgstr ""
+msgstr "System Tray"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:45
msgid "Notify about new releases"
-msgstr ""
+msgstr "Notify about new releases"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:46
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:38
msgid "Updates"
-msgstr ""
+msgstr "Updates"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:47
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:235
msgid "System Default"
-msgstr ""
+msgstr "System Default"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:48
msgid "<b>Language</b>"
-msgstr ""
+msgstr "<b>Language</b>"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:49
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:16
@@ -2962,7 +2989,7 @@ msgstr "Download to:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:54
msgid "Download Folders"
-msgstr ""
+msgstr "Download Folders"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:55
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:93
@@ -2977,7 +3004,7 @@ msgstr "Prioritise first and last pieces of files in torrent"
#: deluge/ui/console/modes/preferences/preference_panes.py:287
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:102
msgid "Sequential download"
-msgstr ""
+msgstr "Sequential download"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:58
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:15
@@ -2989,6 +3016,12 @@ msgid ""
"distribution negatively in the swarm. It should be\n"
"used sparingly."
msgstr ""
+"When enabled, the piece picker will pick pieces in\n"
+"sequence instead of rarest first.\n"
+"\n"
+"Enabling sequential download will affect the piece\n"
+"distribution negatively in the swarm. It should be\n"
+"used sparingly."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:64
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:111
@@ -2998,15 +3031,15 @@ msgstr "Add torrents in Paused state"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:65
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:120
msgid "Pre-allocate disk space"
-msgstr ""
+msgstr "Pre-allocate disk space"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:66
msgid "Pre-allocate the disk space for the torrent files"
-msgstr ""
+msgstr "Pre-allocate the disk space for the torrent files"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:67
msgid "Add Torrent Options"
-msgstr ""
+msgstr "Add Torrent Options"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:68
msgid "Always show"
@@ -3018,15 +3051,15 @@ msgstr "Bring the dialogue to focus"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:70
msgid "Add Torrents Dialog"
-msgstr ""
+msgstr "Add Torrents Dialogue"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:71
msgid "Connection Attempts per Second:"
-msgstr ""
+msgstr "Connection Attempts per Second:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:72
msgid "Half-Open Connections:"
-msgstr ""
+msgstr "Half-Open Connections:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:73
msgid "The maximum number of connections allowed. Set -1 for unlimited."
@@ -3092,7 +3125,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:85
msgid "Global Bandwidth Limits"
-msgstr ""
+msgstr "Global Bandwidth Limits"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:86
msgid "The maximum upload slots per torrent. Set -1 for unlimited."
@@ -3106,6 +3139,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:88
msgid "The maximum number download speed per torrent. Set -1 for unlimited."
msgstr ""
+"The maximum number download speed per torrent. Set -1 for unlimited."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:89
msgid "The maximum upload speed per torrent. Set -1 for unlimited."
@@ -3113,55 +3147,57 @@ msgstr "The maximum upload speed per torrent. Set -1 for unlimited."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:90
msgid "Per-Torrent Bandwidth Limits"
-msgstr ""
+msgstr "Per-Torrent Bandwidth Limits"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:91
#: deluge/ui/console/modes/preferences/preference_panes.py:556
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:42
msgid "Queue to top"
-msgstr ""
+msgstr "Queue to top"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:92
#: deluge/ui/console/modes/preferences/preference_panes.py:554
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:30
msgid "New Torrents"
-msgstr ""
+msgstr "New Torrents"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:93
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:85
msgid "Seeding:"
-msgstr ""
+msgstr "Seeding:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:94
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:59
msgid "Total:"
-msgstr ""
+msgstr "Total:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:95
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:102
msgid "Ignore slow torrents"
-msgstr ""
+msgstr "Ignore slow torrents"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:96
msgid ""
"Torrents not transfering any data do not count towards download/seeding "
"active count."
msgstr ""
+"Torrents not transferring any data do not count towards download/seeding "
+"active count."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:97
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:111
msgid "Prefer seeding torrents"
-msgstr ""
+msgstr "Prefer seeding torrents"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:98
msgid "Give preference to seeding torrents over downloading torrents."
-msgstr ""
+msgstr "Give preference to seeding torrents over downloading torrents."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:99
#: deluge/ui/console/modes/preferences/preference_panes.py:558
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:50
msgid "Active Torrents"
-msgstr ""
+msgstr "Active Torrents"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:100
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:7
@@ -3169,44 +3205,46 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:187
#: deluge/ui/web/render/tab_status.html:4
msgid "Share Ratio:"
-msgstr ""
+msgstr "Share Ratio:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:101
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:142
msgid "Time Ratio:"
-msgstr ""
+msgstr "Time Ratio:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:102
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:157
msgid "Time (m):"
-msgstr ""
+msgstr "Time (m):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:103
#: deluge/ui/console/modes/preferences/preference_panes.py:590
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:118
msgid "Seeding Rotation"
-msgstr ""
+msgstr "Seeding Rotation"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:104
msgid "Pause Torrent"
-msgstr ""
+msgstr "Pause Torrent"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:106
#: deluge/ui/console/modes/preferences/preference_panes.py:627
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:173
msgid "Share Ratio Reached"
-msgstr ""
+msgstr "Share Ratio Reached"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:107
msgid ""
"The IP address of the interface to listen for incoming bittorrent "
"connections on. Leave this empty if you want to use the default."
msgstr ""
+"The IP address of the interface to listen for incoming BitTorrent "
+"connections. Leave this empty if you want to use the default."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:108
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:38
msgid "Incoming Address"
-msgstr ""
+msgstr "Incoming Address"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:109
msgid "Random"
@@ -3214,7 +3252,7 @@ msgstr "Random"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:110
msgid "Uses random ports in range 49152 to 65525"
-msgstr ""
+msgstr "Uses random ports in range 49152 to 65525"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:111
msgid "Active Port:"
@@ -3227,7 +3265,7 @@ msgstr "Test Active Port"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:113
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:58
msgid "Incoming Port"
-msgstr ""
+msgstr "Incoming Port"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:114
msgid ""
@@ -3236,12 +3274,16 @@ msgid ""
"connections. (Leave empty for default.)\n"
" "
msgstr ""
+"\n"
+"The network interface name or IP address for outgoing BitTorrent "
+"connections. (Leave empty for default.)\n"
+" "
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:117
#: deluge/ui/console/modes/preferences/preference_panes.py:359
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:101
msgid "Outgoing Interface"
-msgstr ""
+msgstr "Outgoing Interface"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:118
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:11
@@ -3258,17 +3300,17 @@ msgstr "To:"
#: deluge/ui/console/modes/preferences/preference_panes.py:328
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:120
msgid "Outgoing Ports"
-msgstr ""
+msgstr "Outgoing Ports"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:121
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:59
msgid "Outgoing:"
-msgstr ""
+msgstr "Outgoing:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:122
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:38
msgid "Incoming:"
-msgstr ""
+msgstr "Incoming:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:123
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:80
@@ -3306,7 +3348,7 @@ msgstr "Peer Exchange"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:130
msgid "Exchanges peers between clients. (Disabling requires restart)"
-msgstr ""
+msgstr "Exchanges peers between clients. (Disabling requires restart)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:131
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:223
@@ -3336,7 +3378,7 @@ msgstr "Peer TOS Byte:"
#: deluge/ui/console/modes/preferences/preference_panes.py:372
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:181
msgid "Network Extras"
-msgstr ""
+msgstr "Network Extras"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:137
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:4
@@ -3360,51 +3402,54 @@ msgstr "Port:"
#: deluge/ui/console/modes/preferences/preference_panes.py:658
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:94
msgid "Proxy Hostnames"
-msgstr ""
+msgstr "Proxy Hostnames"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:141
msgid ""
"Hostnames should be attempted to be resolved through\n"
"the proxy instead of using the local DNS service"
msgstr ""
+"Hostnames should be attempted to be resolved through\n"
+"the proxy instead of using the local DNS service"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:143
#: deluge/ui/console/modes/preferences/preference_panes.py:661
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:103
msgid "Proxy Peers"
-msgstr ""
+msgstr "Proxy Peers"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:144
msgid "Proxy peer and web seed connections."
-msgstr ""
+msgstr "Proxy peer and web seed connections."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:145
#: deluge/ui/console/modes/preferences/preference_panes.py:665
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:112
msgid "Proxy Trackers"
-msgstr ""
+msgstr "Proxy Trackers"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:147
msgid "Force Proxy Use"
-msgstr ""
+msgstr "Force Proxy Use"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:148
#: deluge/ui/console/modes/preferences/preference_panes.py:671
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:141
msgid "Hide Client Identity"
-msgstr ""
+msgstr "Hide Client Identity"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:149
msgid ""
"Attempt to hide client identity and only use proxy for incoming connections."
msgstr ""
+"Attempt to hide client identity and only use proxy for incoming connections."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:150
#: deluge/ui/console/modes/preferences/preference_panes.py:668
#: deluge/ui/console/modes/preferences/preference_panes.py:669
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:120
msgid "Force Proxy"
-msgstr ""
+msgstr "Force Proxy"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:151
msgid "Cache Size (16 KiB blocks):"
@@ -3472,7 +3517,7 @@ msgstr "Write Cache Hit Ratio:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:161
#: deluge/ui/console/modes/preferences/preference_panes.py:709
msgid "Write"
-msgstr ""
+msgstr "Write"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:162
msgid ""
@@ -3515,7 +3560,7 @@ msgstr "Reads:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:170
#: deluge/ui/console/modes/preferences/preference_panes.py:723
msgid "Read"
-msgstr ""
+msgstr "Read"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:171
msgid ""
@@ -3536,7 +3581,7 @@ msgstr "Read Cache Size:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:175
#: deluge/ui/gtk3/glade/connection_manager.ui.h:7
msgid "_Refresh"
-msgstr ""
+msgstr "_Refresh"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:177
msgid ""
@@ -3555,7 +3600,7 @@ msgstr "Yes, please send anonymous statistics"
#: deluge/ui/console/modes/preferences/preference_panes.py:503
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:57
msgid "System Information"
-msgstr ""
+msgstr "System Information"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:180
msgid "Location:"
@@ -3573,15 +3618,15 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:516
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:85
msgid "GeoIP Database"
-msgstr ""
+msgstr "GeoIP Database"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:183
msgid "Associate with Deluge"
-msgstr ""
+msgstr "Associate with Deluge"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:184
msgid "Magnet Links"
-msgstr ""
+msgstr "Magnet Links"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:185
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:37
@@ -3614,11 +3659,11 @@ msgstr "Periodically check the website for new releases"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:193
msgid "_Delete"
-msgstr ""
+msgstr "_Delete"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:194
msgid "Accounts"
-msgstr ""
+msgstr "Accounts"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:196
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:29
@@ -3648,23 +3693,23 @@ msgstr "Info"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:201
msgid "_Install"
-msgstr ""
+msgstr "_Install"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:202
msgid "_Find More..."
-msgstr ""
+msgstr "_Find More..."
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:4
msgid "Remove the selected torrent(s)?"
-msgstr ""
+msgstr "Remove the selected torrent(s)?"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:5
msgid "Include downloaded files"
-msgstr ""
+msgstr "Include downloaded files"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:6
msgid "(This is permanent!)"
-msgstr ""
+msgstr "(This is permanent!)"
#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:1
msgid "Add Peer"
@@ -3676,11 +3721,11 @@ msgstr "hostname:port"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:1
msgid "Properties"
-msgstr ""
+msgstr "Properties"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:3
msgid "Max drop down rows"
-msgstr ""
+msgstr "Max drop down rows"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:4
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:2
@@ -3690,87 +3735,87 @@ msgstr "<b>General</b>"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:5
msgid "Show path entry"
-msgstr ""
+msgstr "Show path entry"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:6
msgid "Show file chooser"
-msgstr ""
+msgstr "Show file chooser"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:7
msgid "Show folder name"
-msgstr ""
+msgstr "Show folder name"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:8
msgid "Path Chooser Type"
-msgstr ""
+msgstr "Path Chooser Type"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:9
msgid "Enable autocomplete"
-msgstr ""
+msgstr "Enable autocomplete"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:10
msgid "Show hidden files"
-msgstr ""
+msgstr "Show hidden files"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:11
msgid "Set new key"
-msgstr ""
+msgstr "Set new key"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:12
msgid "Press this key to set new key accelerators to trigger auto-complete"
-msgstr ""
+msgstr "Press this key to set new key accelerators to trigger auto-complete"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:13
msgid "Autocomplete"
-msgstr ""
+msgstr "Autocomplete"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:14
msgid "Save path"
-msgstr ""
+msgstr "Save path"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:15
msgid "Ctrl+S"
-msgstr ""
+msgstr "Ctrl+S"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:16
msgid "Ctrl+E"
-msgstr ""
+msgstr "Ctrl+E"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:17
msgid "Ctrl+R"
-msgstr ""
+msgstr "Ctrl+R"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:18
msgid "Ctrl+H"
-msgstr ""
+msgstr "Ctrl+H"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:19
msgid "Ctrl+D"
-msgstr ""
+msgstr "Ctrl+D"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:22
msgid "Toggle hidden files"
-msgstr ""
+msgstr "Toggle hidden files"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:23
msgid "Default path"
-msgstr ""
+msgstr "Default path"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:24
msgid "Shortcuts"
-msgstr ""
+msgstr "Shortcuts"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:25
msgid "Select a Directory"
-msgstr ""
+msgstr "Select a Directory"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:26
msgid "Saved paths"
-msgstr ""
+msgstr "Saved paths"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:27
msgid "column"
-msgstr ""
+msgstr "column"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:29
#: deluge/ui/console/modes/preferences/preferences.py:145
@@ -3786,7 +3831,7 @@ msgstr "Cancel"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:30
msgid "Open"
-msgstr ""
+msgstr "Open"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:31
#: deluge/ui/web/js/deluge-all/Toolbar.js:39
@@ -3801,18 +3846,18 @@ msgstr "Add"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:32
msgid "Add the current entry value to the list"
-msgstr ""
+msgstr "Add the current entry value to the list"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:33
#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:98
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:33
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:102
msgid "Edit"
-msgstr ""
+msgstr "Edit"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:34
msgid "Edit the selected entry"
-msgstr ""
+msgstr "Edit the selected entry"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:35
#: deluge/ui/web/js/deluge-all/Toolbar.js:46
@@ -3824,27 +3869,27 @@ msgstr "Remove"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:36
msgid "Remove the selected entry"
-msgstr ""
+msgstr "Remove the selected entry"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:38
msgid "Move the selected entry up"
-msgstr ""
+msgstr "Move the selected entry up"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:40
msgid "Move the selected entry down"
-msgstr ""
+msgstr "Move the selected entry down"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:41
msgid "Default"
-msgstr ""
+msgstr "Default"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:42
msgid "No default path set"
-msgstr ""
+msgstr "No default path set"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:43
msgid "Open properties dialog"
-msgstr ""
+msgstr "Open properties dialogue"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:1
msgid "Add Infohash"
@@ -3852,7 +3897,7 @@ msgstr "Add Infohash"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
msgid "From Infohash"
-msgstr ""
+msgstr "From Infohash"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
msgid "Infohash:"
@@ -3873,11 +3918,11 @@ msgstr "Add Host"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:16
#: deluge/ui/web/js/deluge-all/Menus.js:346
msgid "Move Download Folder"
-msgstr ""
+msgstr "Move Download Folder"
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:4
msgid "Move the torrent(s) download folder."
-msgstr ""
+msgstr "Move the torrent(s) download folder."
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:5
msgid "Destination:"
@@ -3893,19 +3938,19 @@ msgstr "_Goto Website"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:4
msgid "New Release Available!"
-msgstr ""
+msgstr "New Release Available!"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:5
msgid "Available Version:"
-msgstr ""
+msgstr "Available Version:"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:6
msgid "Server Version"
-msgstr ""
+msgstr "Server Version"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:7
msgid "Current Version:"
-msgstr ""
+msgstr "Current Version:"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:8
msgid "Do not show this dialog in the future"
@@ -3915,73 +3960,73 @@ msgstr "Do not show this dialogue in the future"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:26
#: deluge/ui/web/render/tab_status.html:9
msgid "Down Speed:"
-msgstr ""
+msgstr "Down Speed:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:2
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:28
#: deluge/ui/web/render/tab_status.html:10
msgid "Up Speed:"
-msgstr ""
+msgstr "Up Speed:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:3
#: deluge/ui/web/render/tab_status.html:2
msgid "Downloaded:"
-msgstr ""
+msgstr "Downloaded:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:4
#: deluge/ui/web/render/tab_status.html:3
msgid "Uploaded:"
-msgstr ""
+msgstr "Uploaded:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:5
#: deluge/ui/web/render/tab_status.html:16
msgid "Seeds:"
-msgstr ""
+msgstr "Seeds:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:6
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:10
#: deluge/ui/web/render/tab_status.html:17
msgid "Peers:"
-msgstr ""
+msgstr "Peers:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:8
#: deluge/ui/web/render/tab_status.html:18
msgid "Availability:"
-msgstr ""
+msgstr "Availability:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:9
#: deluge/ui/web/render/tab_status.html:25
msgid "Seed Rank:"
-msgstr ""
+msgstr "Seed Rank:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:10
msgid "ETA Time:"
-msgstr ""
+msgstr "ETA Time:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:11
#: deluge/ui/web/render/tab_status.html:13
msgid "Last Transfer:"
-msgstr ""
+msgstr "Last Transfer:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:12
#: deluge/ui/web/render/tab_status.html:23
msgid "Active Time:"
-msgstr ""
+msgstr "Active Time:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:13
#: deluge/ui/web/render/tab_status.html:20
msgid "Complete Seen:"
-msgstr ""
+msgstr "Complete Seen:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:14
#: deluge/ui/web/render/tab_status.html:24
msgid "Seeding Time:"
-msgstr ""
+msgstr "Seeding Time:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:16
#: deluge/ui/web/render/tab_status.html:12
msgid "Pieces:"
-msgstr ""
+msgstr "Pieces:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:17
#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:3
@@ -3992,31 +4037,31 @@ msgstr "Name:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:18
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:25
msgid "Download Folder:"
-msgstr ""
+msgstr "Download Folder:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:19
msgid "Added:"
-msgstr ""
+msgstr "Added:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:20
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:26
msgid "Total Size:"
-msgstr ""
+msgstr "Total Size:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:21
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:27
msgid "Total Files:"
-msgstr ""
+msgstr "Total Files:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:22
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:24
msgid "Hash:"
-msgstr ""
+msgstr "Hash:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:23
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:31
msgid "Created By:"
-msgstr ""
+msgstr "Created By:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:24
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:17
@@ -4025,7 +4070,7 @@ msgstr "Comments:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:29
msgid "Owner:"
-msgstr ""
+msgstr "Owner:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:34
msgid "Move completed:"
@@ -4047,29 +4092,29 @@ msgstr "Remove at ratio"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:44
msgid "Bandwidth Limits"
-msgstr ""
+msgstr "Bandwidth Limits"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:46
msgid "Current Tracker:"
-msgstr ""
+msgstr "Current Tracker:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:47
msgid "Total Trackers:"
-msgstr ""
+msgstr "Total Trackers:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:48
#: deluge/ui/web/render/tab_status.html:6
msgid "Tracker Status:"
-msgstr ""
+msgstr "Tracker Status:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:49
#: deluge/ui/web/render/tab_status.html:5
msgid "Next Announce:"
-msgstr ""
+msgstr "Next Announce:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:50
msgid "Private Torrent:"
-msgstr ""
+msgstr "Private Torrent:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:51
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:8
@@ -4103,7 +4148,7 @@ msgstr "Info_hash"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:11
msgid "Move Complete Folder"
-msgstr ""
+msgstr "Move Complete Folder"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:12
msgid "Add In _Paused State"
@@ -4118,32 +4163,32 @@ msgstr "Prioritise First/Last Pieces"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:46
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:152
msgid "Skip File Hash Check"
-msgstr ""
+msgstr "Skip File Hash Check"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:170
msgid "Preallocate Disk Space"
-msgstr ""
+msgstr "Preallocate Disk Space"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:24
msgid "Preallocate the disk space for the torrent files"
-msgstr ""
+msgstr "Preallocate the disk space for the torrent files"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:25
msgid "Maximum torrent download speed"
-msgstr ""
+msgstr "Maximum torrent download speed"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:27
msgid "Maximum torrent upload speed"
-msgstr ""
+msgstr "Maximum torrent upload speed"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:29
msgid "Maximum torrent connections"
-msgstr ""
+msgstr "Maximum torrent connections"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:31
msgid "Maximum torrent upload slots"
-msgstr ""
+msgstr "Maximum torrent upload slots"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:34
msgid "Apply To All"
@@ -4159,11 +4204,11 @@ msgstr "_Show Deluge"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:3
msgid "_Pause Session"
-msgstr ""
+msgstr "_Pause Session"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:4
msgid "_Resume Session"
-msgstr ""
+msgstr "_Resume Session"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:5
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:1
@@ -4189,12 +4234,12 @@ msgstr "Edit Trackers"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:4
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:19
msgid "_Up"
-msgstr ""
+msgstr "_Up"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:8
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:22
msgid "_Down"
-msgstr ""
+msgstr "_Down"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:1
msgid "_Add Peer"
@@ -4221,7 +4266,7 @@ msgstr "Enter Remote Path"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:4
msgid "Remote Path"
-msgstr ""
+msgstr "Remote Path"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:5
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:5
@@ -4231,43 +4276,43 @@ msgstr "Path:"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:1
msgid "32 KiB"
-msgstr ""
+msgstr "32 KiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:2
msgid "64 KiB"
-msgstr ""
+msgstr "64 KiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:3
msgid "128 KiB"
-msgstr ""
+msgstr "128 KiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:4
msgid "256 KiB"
-msgstr ""
+msgstr "256 KiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:5
msgid "512 KiB"
-msgstr ""
+msgstr "512 KiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:6
msgid "1 MiB"
-msgstr ""
+msgstr "1 MiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:7
msgid "2 MiB"
-msgstr ""
+msgstr "2 MiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:8
msgid "4 MiB"
-msgstr ""
+msgstr "4 MiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:9
msgid "8 MiB"
-msgstr ""
+msgstr "8 MiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:10
msgid "16 MiB"
-msgstr ""
+msgstr "16 MiB"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:11
msgid "Create Torrent"
@@ -4320,7 +4365,7 @@ msgstr "Save .torrent as"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:1
msgid "_Open Download Folder"
-msgstr ""
+msgstr "_Open Download Folder"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:2
msgid "_Pause"
@@ -4357,7 +4402,7 @@ msgstr "_Force Re-check"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:11
msgid "_Move Download Folder"
-msgstr ""
+msgstr "_Move Download Folder"
#: deluge/ui/gtk3/glade/other_dialog.ui.h:3
msgid "label"
@@ -4385,7 +4430,7 @@ msgstr "Upload _Slot Limit"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:5
msgid "Stop seed at _ratio"
-msgstr ""
+msgstr "Stop seed at _ratio"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:6
msgid "_Auto Managed"
@@ -4393,11 +4438,11 @@ msgstr "_Auto Managed"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:7
msgid "_Super Seeding"
-msgstr ""
+msgstr "_Super Seeding"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:8
msgid "_Change Ownership"
-msgstr ""
+msgstr "_Change Ownership"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:1
#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:26
@@ -4406,7 +4451,7 @@ msgstr "Add Tracker"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:4
msgid "Add Trackers"
-msgstr ""
+msgstr "Add Trackers"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:1
msgid "Add URL"
@@ -4414,7 +4459,7 @@ msgstr "Add URL"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:4
msgid "From URL"
-msgstr ""
+msgstr "From URL"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:5
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:1
@@ -4423,31 +4468,31 @@ msgstr "URL:"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:9
msgid "Deluge Daemons"
-msgstr ""
+msgstr "Deluge Daemons"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:10
msgid "Auto-connect to selected Daemon"
-msgstr ""
+msgstr "Auto-connect to selected Daemon"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:11
msgid "Auto-start localhost daemon (if required)"
-msgstr ""
+msgstr "Auto-start localhost daemon (if required)"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:12
msgid "Hide this dialog"
-msgstr ""
+msgstr "Hide this dialogue"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:13
msgid "Startup Options"
-msgstr ""
+msgstr "Startup Options"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:1
msgid "_Open File"
-msgstr ""
+msgstr "_Open File"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:2
msgid "_Show Folder"
-msgstr ""
+msgstr "_Show Folder"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:3
msgid "_Expand All"
@@ -4455,30 +4500,31 @@ msgstr "_Expand All"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:4
msgid "_Skip"
-msgstr ""
+msgstr "_Skip"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:5
msgid "_Low"
-msgstr ""
+msgstr "_Low"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:6
msgid "_Normal"
-msgstr ""
+msgstr "_Normal"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:7
msgid "_High"
-msgstr ""
+msgstr "_High"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
-msgstr ""
+msgstr "Deluge Team"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
+"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4486,14 +4532,23 @@ msgid ""
"Deluge heavily utilises the libtorrent library it has a comprehensive list "
"of the features provided."
msgstr ""
+"Deluge contains the common features to BitTorrent clients such as Protocol "
+"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
+"PMP, Proxy support, Web seeds, global and per-torrent speed limits. As "
+"Deluge heavily utilises the libtorrent library it has a comprehensive list "
+"of the features provided."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
"handles all the BitTorrent activity and is able to run on headless machines "
"with the user-interfaces connecting remotely from any other platform."
msgstr ""
+"Deluge has been designed to run as both a normal standalone desktop "
+"application and as a client-server. In Thinclient mode a Deluge daemon "
+"handles all the BitTorrent activity and is able to run on headless machines "
+"with the user-interfaces connecting remotely from any other platform."
#: deluge/ui/data/share/applications/deluge.desktop.in.h:2
msgid "BitTorrent Client"
@@ -4509,41 +4564,43 @@ msgstr "Download and share files over BitTorrent"
#: deluge/ui/console/console.py:76
msgid "Console Options"
-msgstr ""
+msgstr "Console Options"
#: deluge/ui/console/console.py:78
msgid ""
"These daemon connect options will be used for commands, or if console ui "
"autoconnect is enabled."
msgstr ""
+"These daemon connect options will be used for commands, or if console UI "
+"auto-connect is enabled."
#: deluge/ui/console/console.py:87
msgid "Deluge daemon IP address to connect to (default 127.0.0.1)"
-msgstr ""
+msgstr "Deluge daemon IP address to connect to (default 127.0.0.1)"
#: deluge/ui/console/console.py:96
msgid "Deluge daemon port to connect to (default 58846)"
-msgstr ""
+msgstr "Deluge daemon port to connect to (default 58846)"
#: deluge/ui/console/console.py:104
msgid "Deluge daemon username to use when connecting"
-msgstr ""
+msgstr "Deluge daemon username to use when connecting"
#: deluge/ui/console/console.py:111
msgid "Deluge daemon password to use when connecting"
-msgstr ""
+msgstr "Deluge daemon password to use when connecting"
#: deluge/ui/console/console.py:131
msgid "Console Commands"
-msgstr ""
+msgstr "Console Commands"
#: deluge/ui/console/console.py:132
msgid "Description"
-msgstr ""
+msgstr "Description"
#: deluge/ui/console/console.py:133
msgid "The following console commands are available:"
-msgstr ""
+msgstr "The following console commands are available:"
#: deluge/ui/console/console.py:134
#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:2
@@ -4553,115 +4610,117 @@ msgstr "Command"
#: deluge/ui/console/cmdline/command.py:208
#, python-format
msgid "`%s` alias"
-msgstr ""
+msgstr "`%s` alias"
#: deluge/ui/console/cmdline/commands/manage.py:29
msgid "Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
-msgstr ""
+msgstr "Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
#: deluge/ui/console/cmdline/commands/manage.py:35
msgid "an expression matched against torrent ids and torrent names"
-msgstr ""
+msgstr "an expression matched against torrent ids and torrent names"
#: deluge/ui/console/cmdline/commands/manage.py:43
#: deluge/ui/console/cmdline/commands/config.py:88
msgid "set value for this key"
-msgstr ""
+msgstr "set value for this key"
#: deluge/ui/console/cmdline/commands/manage.py:46
#: deluge/ui/console/cmdline/commands/config.py:91
msgid "Value to set"
-msgstr ""
+msgstr "Value to set"
#: deluge/ui/console/cmdline/commands/manage.py:53
#: deluge/ui/console/cmdline/commands/config.py:98
msgid "one or more keys separated by space"
-msgstr ""
+msgstr "one or more keys separated by space"
#: deluge/ui/console/cmdline/commands/rm.py:33
msgid "Also removes the torrent data"
-msgstr ""
+msgstr "Also removes the torrent data"
#: deluge/ui/console/cmdline/commands/rm.py:40
msgid "List the matching torrents without removing."
-msgstr ""
+msgstr "List the matching torrents without removing."
#: deluge/ui/console/cmdline/commands/rm.py:46
#: deluge/ui/console/cmdline/commands/recheck.py:28
#: deluge/ui/console/cmdline/commands/move.py:31
msgid "One or more torrent ids"
-msgstr ""
+msgstr "One or more torrent IDs"
#: deluge/ui/console/cmdline/commands/rm.py:66
#, python-format
msgid "Confirm with -c to remove the listed torrents (Count: %d)"
-msgstr ""
+msgstr "Confirm with -c to remove the listed torrents (Count: %d)"
#: deluge/ui/console/cmdline/commands/resume.py:22
msgid "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
-msgstr ""
+msgstr "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
#: deluge/ui/console/cmdline/commands/resume.py:29
msgid "One or more torrent ids. Use \"*\" to resume all torrents"
-msgstr ""
+msgstr "One or more torrent IDs. Use \"*\" to resume all torrents"
#: deluge/ui/console/cmdline/commands/pause.py:29
msgid "One or more torrent ids. Use \"*\" to pause all torrents"
-msgstr ""
+msgstr "One or more torrent IDs. Use \"*\" to pause all torrents"
#: deluge/ui/console/cmdline/commands/add.py:38
msgid "Download folder for torrent"
-msgstr ""
+msgstr "Download folder for torrent"
#: deluge/ui/console/cmdline/commands/add.py:44
msgid "Move the completed torrent to this folder"
-msgstr ""
+msgstr "Move the completed torrent to this folder"
#: deluge/ui/console/cmdline/commands/add.py:50
msgid "One or more torrent files, URLs or magnet URIs"
-msgstr ""
+msgstr "One or more torrent files, URLs or magnet URIs"
#: deluge/ui/console/cmdline/commands/plugin.py:29
msgid "Lists available plugins"
-msgstr ""
+msgstr "Lists available plugins"
#: deluge/ui/console/cmdline/commands/plugin.py:37
msgid "Shows enabled plugins"
-msgstr ""
+msgstr "Shows enabled plugins"
#: deluge/ui/console/cmdline/commands/plugin.py:40
msgid "Enables a plugin"
-msgstr ""
+msgstr "Enables a plugin"
#: deluge/ui/console/cmdline/commands/plugin.py:43
msgid "Disables a plugin"
-msgstr ""
+msgstr "Disables a plugin"
#: deluge/ui/console/cmdline/commands/plugin.py:51
msgid "Reload list of available plugins"
-msgstr ""
+msgstr "Reload list of available plugins"
#: deluge/ui/console/cmdline/commands/plugin.py:54
msgid "Install a plugin from an .egg file"
-msgstr ""
+msgstr "Install a plugin from an .egg file"
#: deluge/ui/console/cmdline/commands/status.py:36
msgid ""
"Raw values for upload/download rates (without KiB/s suffix)(useful for "
"scripts that want to do their own parsing)"
msgstr ""
+"Raw values for upload/download rates (without KiB/s suffix)(useful for "
+"scripts that want to do their own parsing)"
#: deluge/ui/console/cmdline/commands/status.py:46
msgid "Do not show torrent status (Improves command speed)"
-msgstr ""
+msgstr "Do not show torrent status (Improves command speed)"
#: deluge/ui/console/cmdline/commands/connect.py:26
msgid "Usage: connect <host[:port]> [<username>] [<password>]"
-msgstr ""
+msgstr "Usage: connect <host[:port]> [<username>] [<password>]"
#: deluge/ui/console/cmdline/commands/connect.py:30
msgid "Daemon host and port"
-msgstr ""
+msgstr "Daemon host and port"
#: deluge/ui/console/cmdline/commands/connect.py:36
#: deluge/ui/console/modes/preferences/preference_panes.py:652
@@ -4671,139 +4730,139 @@ msgstr "Password"
#: deluge/ui/console/cmdline/commands/move.py:34
msgid "The path to move the torrents to"
-msgstr ""
+msgstr "The path to move the torrents to"
#: deluge/ui/console/cmdline/commands/debug.py:26
msgid "The new state"
-msgstr ""
+msgstr "The new state"
#: deluge/ui/console/cmdline/commands/help.py:29
msgid "One or more commands"
-msgstr ""
+msgstr "One or more commands"
#: deluge/ui/console/cmdline/commands/config.py:79
msgid "Usage: config [--set <key> <value>] [<key> [<key>...] ]"
-msgstr ""
+msgstr "Usage: config [--set <key> <value>] [<key> [<key>...] ]"
#: deluge/ui/console/cmdline/commands/info.py:101
msgid "Show more information per torrent."
-msgstr ""
+msgstr "Show more information per torrent."
#: deluge/ui/console/cmdline/commands/info.py:109
msgid "Show more detailed information including files and peers."
-msgstr ""
+msgstr "Show more detailed information including files and peers."
#: deluge/ui/console/cmdline/commands/info.py:116
#, python-format
msgid "Show torrents with state STATE: %s."
-msgstr ""
+msgstr "Show torrents with state STATE: %s."
#: deluge/ui/console/cmdline/commands/info.py:132
msgid "Same as --sort but items are in reverse order."
-msgstr ""
+msgstr "Same as --sort but items are in reverse order."
#: deluge/ui/console/cmdline/commands/info.py:138
msgid "One or more torrent ids. If none is given, list all"
-msgstr ""
+msgstr "One or more Torrent IDs. If none is given, list all"
#: deluge/ui/console/modes/connectionmanager.py:44
msgid "Select Host"
-msgstr ""
+msgstr "Select Host"
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Quit"
-msgstr ""
+msgstr "Quit"
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Delete Host"
-msgstr ""
+msgstr "Delete Host"
#: deluge/ui/console/modes/connectionmanager.py:116
msgid "Add Host (Up & Down arrows to navigate, Esc to cancel)"
-msgstr ""
+msgstr "Add Host (Up & Down arrows to navigate, Esc to cancel)"
#: deluge/ui/console/modes/connectionmanager.py:133
msgid "Error adding host"
-msgstr ""
+msgstr "Error adding host"
#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
msgid "Columns"
-msgstr ""
+msgstr "Columns"
#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
msgid "Width"
-msgstr ""
+msgstr "Width"
#: deluge/ui/console/modes/preferences/preference_panes.py:178
msgid "General options"
-msgstr ""
+msgstr "General options"
#: deluge/ui/console/modes/preferences/preference_panes.py:182
msgid "Ring system bell when a download finishes"
-msgstr ""
+msgstr "Ring system bell when a download finishes"
#: deluge/ui/console/modes/preferences/preference_panes.py:188
msgid "List complete torrents after incomplete regardless of sorting order"
-msgstr ""
+msgstr "List complete torrents after incomplete regardless of sorting order"
#: deluge/ui/console/modes/preferences/preference_panes.py:193
msgid "Move selection when moving torrents in the queue"
-msgstr ""
+msgstr "Move selection when moving torrents in the queue"
#: deluge/ui/console/modes/preferences/preference_panes.py:200
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:67
msgid "Language"
-msgstr ""
+msgstr "Language"
#: deluge/ui/console/modes/preferences/preference_panes.py:202
msgid "Command Line Mode"
-msgstr ""
+msgstr "Command Line Mode"
#: deluge/ui/console/modes/preferences/preference_panes.py:205
msgid "Do not store duplicate input in history"
-msgstr ""
+msgstr "Do not store duplicate input in history"
#: deluge/ui/console/modes/preferences/preference_panes.py:210
msgid "Store and load command line history in command line mode"
-msgstr ""
+msgstr "Store and load command line history in command line mode"
#: deluge/ui/console/modes/preferences/preference_panes.py:216
msgid "Third tab lists all remaining torrents in command line mode"
-msgstr ""
+msgstr "Third tab lists all remaining Torrents in command line mode"
#: deluge/ui/console/modes/preferences/preference_panes.py:221
msgid "Torrents per tab press"
-msgstr ""
+msgstr "Torrents per tab press"
#: deluge/ui/console/modes/preferences/preference_panes.py:234
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:18
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:39
msgid "Folders"
-msgstr ""
+msgstr "Folders"
#: deluge/ui/console/modes/preferences/preference_panes.py:237
msgid "Download To"
-msgstr ""
+msgstr "Download To"
#: deluge/ui/console/modes/preferences/preference_panes.py:254
msgid "Move completed to"
-msgstr ""
+msgstr "Move completed to"
#: deluge/ui/console/modes/preferences/preference_panes.py:269
msgid "Copy of .torrent files to"
-msgstr ""
+msgstr "Copy of .torrent files to"
#: deluge/ui/console/modes/preferences/preference_panes.py:290
msgid "Add Paused"
-msgstr ""
+msgstr "Add Paused"
#: deluge/ui/console/modes/preferences/preference_panes.py:293
msgid "Pre-Allocate disk space"
-msgstr ""
+msgstr "Pre-Allocate disk space"
#: deluge/ui/console/modes/preferences/preference_panes.py:304
msgid "Incomming Ports"
-msgstr ""
+msgstr "Incomming Ports"
#: deluge/ui/console/modes/preferences/preference_panes.py:313
#: deluge/ui/console/modes/preferences/preference_panes.py:337
@@ -4822,17 +4881,19 @@ msgstr "Use Random Ports"
#: deluge/ui/console/modes/preferences/preference_panes.py:352
msgid "Incoming Interface"
-msgstr ""
+msgstr "Incoming Interface"
#: deluge/ui/console/modes/preferences/preference_panes.py:355
msgid "IP address of the interface to listen on (leave empty for default):"
-msgstr ""
+msgstr "IP address of the interface to listen on (leave empty for default):"
#: deluge/ui/console/modes/preferences/preference_panes.py:363
msgid ""
"The network interface name or IP address for outgoing BitTorrent "
"connections. (Leave empty for default.):"
msgstr ""
+"The network interface name or IP address for outgoing BitTorrent "
+"connections. (Leave empty for default.):"
#: deluge/ui/console/modes/preferences/preference_panes.py:382
msgid "Inbound"
@@ -4845,7 +4906,7 @@ msgstr "Outbound"
#: deluge/ui/console/modes/preferences/preference_panes.py:413
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:38
msgid "Global Bandwidth Usage"
-msgstr ""
+msgstr "Global Bandwidth Usage"
#: deluge/ui/console/modes/preferences/preference_panes.py:416
#: deluge/ui/console/modes/preferences/preference_panes.py:469
@@ -4861,12 +4922,12 @@ msgstr "Maximum Upload Slots"
#: deluge/ui/console/modes/preferences/preference_panes.py:430
#: deluge/ui/console/modes/preferences/preference_panes.py:483
msgid "Maximum Download Speed (KiB/s)"
-msgstr ""
+msgstr "Maximum Download Speed (KiB/s)"
#: deluge/ui/console/modes/preferences/preference_panes.py:437
#: deluge/ui/console/modes/preferences/preference_panes.py:490
msgid "Maximum Upload Speed (KiB/s)"
-msgstr ""
+msgstr "Maximum Upload Speed (KiB/s)"
#: deluge/ui/console/modes/preferences/preference_panes.py:444
msgid "Maximum Half-Open Connections"
@@ -4883,19 +4944,19 @@ msgstr "Rate Limit IP Overhead"
#: deluge/ui/console/modes/preferences/preference_panes.py:466
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:148
msgid "Per Torrent Bandwidth Usage"
-msgstr ""
+msgstr "Per Torrent Bandwidth Usage"
#: deluge/ui/console/modes/preferences/preference_panes.py:513
msgid "Yes, please send anonymous statistics."
-msgstr ""
+msgstr "Yes, please send anonymous statistics."
#: deluge/ui/console/modes/preferences/preference_panes.py:531
msgid "Daemon Port"
-msgstr ""
+msgstr "Daemon Port"
#: deluge/ui/console/modes/preferences/preference_panes.py:538
msgid "Allow remote connections"
-msgstr ""
+msgstr "Allow remote connections"
#: deluge/ui/console/modes/preferences/preference_panes.py:561
msgid "Total"
@@ -4907,19 +4968,19 @@ msgstr "Share Ratio"
#: deluge/ui/console/modes/preferences/preference_panes.py:601
msgid "Time Ratio"
-msgstr ""
+msgstr "Time Ratio"
#: deluge/ui/console/modes/preferences/preference_panes.py:609
msgid "Time (m)"
-msgstr ""
+msgstr "Time (m)"
#: deluge/ui/console/modes/preferences/preference_panes.py:633
msgid "Remove torrent (Unchecked pauses torrent)"
-msgstr ""
+msgstr "Remove torrent (Unchecked pauses torrent)"
#: deluge/ui/console/modes/preferences/preference_panes.py:646
msgid "Proxy Settings"
-msgstr ""
+msgstr "Proxy Settings"
#: deluge/ui/console/modes/preferences/preference_panes.py:649
msgid "Type"
@@ -4927,39 +4988,39 @@ msgstr "Type"
#: deluge/ui/console/modes/preferences/preference_panes.py:653
msgid "Hostname"
-msgstr ""
+msgstr "Hostname"
#: deluge/ui/console/modes/preferences/preference_panes.py:673
msgid "Proxy Type Help"
-msgstr ""
+msgstr "Proxy Type Help"
#: deluge/ui/console/modes/preferences/preference_panes.py:697
msgid "Cache Size (16 KiB blocks)"
-msgstr ""
+msgstr "Cache Size (16 KiB blocks)"
#: deluge/ui/console/modes/preferences/preference_panes.py:704
msgid "Cache Expiry (seconds)"
-msgstr ""
+msgstr "Cache Expiry (seconds)"
#: deluge/ui/console/modes/preferences/preference_panes.py:712
msgid "Blocks Written"
-msgstr ""
+msgstr "Blocks Written"
#: deluge/ui/console/modes/preferences/preference_panes.py:716
msgid "Writes"
-msgstr ""
+msgstr "Writes"
#: deluge/ui/console/modes/preferences/preference_panes.py:720
msgid "Write Cache Hit Ratio"
-msgstr ""
+msgstr "Write Cache Hit Ratio"
#: deluge/ui/console/modes/preferences/preference_panes.py:725
msgid "Blocks Read"
-msgstr ""
+msgstr "Blocks Read"
#: deluge/ui/console/modes/preferences/preference_panes.py:729
msgid "Blocks Read hit"
-msgstr ""
+msgstr "Blocks Read hit"
#: deluge/ui/console/modes/preferences/preference_panes.py:732
msgid "Reads"
diff --git a/deluge/i18n/eo.po b/deluge/i18n/eo.po
index c04394b4f..ac3a7b387 100644
--- a/deluge/i18n/eo.po
+++ b/deluge/i18n/eo.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/es.po b/deluge/i18n/es.po
index 8e26e3e68..194249d25 100644
--- a/deluge/i18n/es.po
+++ b/deluge/i18n/es.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4477,16 +4477,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4495,7 +4495,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/et.po b/deluge/i18n/et.po
index 180e6dbf0..556f6996f 100644
--- a/deluge/i18n/et.po
+++ b/deluge/i18n/et.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4442,16 +4442,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4460,7 +4460,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/eu.po b/deluge/i18n/eu.po
index 5e9711b2b..d18d61d87 100644
--- a/deluge/i18n/eu.po
+++ b/deluge/i18n/eu.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4463,16 +4463,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4481,7 +4481,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/fa.po b/deluge/i18n/fa.po
index b821d2a3f..374a69a5a 100644
--- a/deluge/i18n/fa.po
+++ b/deluge/i18n/fa.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4426,16 +4426,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4444,7 +4444,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/fi.po b/deluge/i18n/fi.po
index 9196ca941..c5a722e89 100644
--- a/deluge/i18n/fi.po
+++ b/deluge/i18n/fi.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2020-04-26 10:56+0000\n"
+"PO-Revision-Date: 2022-06-10 17:48+0000\n"
"Last-Translator: Jiri Grönroos <Unknown>\n"
"Language-Team: Finnish <fi@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -120,11 +120,11 @@ msgstr ""
#: deluge/argparserbase.py:200
msgid "Output to specified logfile instead of stdout"
-msgstr ""
+msgstr "Ohjaa tuloste määritettyyn lokitiedostoon stdoutin sijaan"
#: deluge/argparserbase.py:206
msgid "Set the log level (none, error, warning, info, debug)"
-msgstr ""
+msgstr "Aseta lokituksen taso (none, error, warning, info, debug)"
#: deluge/argparserbase.py:215
#, python-format
@@ -166,7 +166,7 @@ msgstr ""
#: deluge/core/daemon_entry.py:25
msgid "Daemon Options"
-msgstr ""
+msgstr "Taustaprosessin valinnat"
#: deluge/core/daemon_entry.py:31
msgid "IP address to listen for UI connections"
@@ -325,7 +325,7 @@ msgstr "Yhteyksiä enintään"
#: deluge/ui/common.py:64 deluge/ui/web/js/deluge-all/add/OptionsTab.js:109
msgid "Max Upload Slots"
-msgstr ""
+msgstr "Jakopaikkoja enintään"
#: deluge/ui/common.py:65 deluge/ui/gtk3/torrentview.py:325
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:136
@@ -336,7 +336,7 @@ msgstr "Vertaiset"
#: deluge/ui/common.py:66 deluge/ui/gtk3/torrentview.py:317
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:128
msgid "Seeds"
-msgstr ""
+msgstr "Jakajat"
#: deluge/ui/common.py:67 deluge/ui/gtk3/torrentview.py:380
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:173
@@ -346,7 +346,7 @@ msgstr "Saat."
#: deluge/ui/common.py:68 deluge/ui/gtk3/torrentview.py:333
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:284
msgid "Seeds:Peers"
-msgstr ""
+msgstr "Jakajia:vertaisia"
#: deluge/ui/common.py:69 deluge/ui/gtk3/listview.py:203
#: deluge/ui/gtk3/torrentview.py:387
@@ -371,15 +371,15 @@ msgstr "Latauskansio"
#: deluge/ui/common.py:75
msgid "Seeding Time"
-msgstr ""
+msgstr "Jakoaika"
#: deluge/ui/common.py:76
msgid "Active Time"
-msgstr ""
+msgstr "Aktiivisuusaika"
#: deluge/ui/common.py:78
msgid "Last Activity"
-msgstr ""
+msgstr "Viimeisin toiminta"
#: deluge/ui/common.py:81
msgid "Finished Time"
@@ -404,7 +404,7 @@ msgstr "Aikaa jäljellä"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:236
msgid "Shared"
-msgstr ""
+msgstr "Jaettu"
#: deluge/ui/common.py:90 deluge/ui/gtk3/glade/main_window.tabs.ui.h:31
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:287
@@ -415,7 +415,7 @@ msgstr "Suosi ensimmäistä/viimeistä"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:14
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:143
msgid "Sequential Download"
-msgstr ""
+msgstr "Peräkkäinen lataus"
#: deluge/ui/common.py:97 deluge/ui/common.py:98
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:35
@@ -427,15 +427,15 @@ msgstr "Automaattisesti hallittu"
#: deluge/ui/common.py:99
msgid "Stop At Ratio"
-msgstr ""
+msgstr "Pysäytä suhteessa"
#: deluge/ui/common.py:100
msgid "Stop Ratio"
-msgstr ""
+msgstr "Pysäytyssuhde"
#: deluge/ui/common.py:101
msgid "Remove At Ratio"
-msgstr ""
+msgstr "Poista suhteessa"
#: deluge/ui/common.py:102 deluge/ui/common.py:108
msgid "Move On Completed"
@@ -443,7 +443,7 @@ msgstr ""
#: deluge/ui/common.py:104
msgid "Move Completed Path"
-msgstr ""
+msgstr "Siirrä valmistuneet -polku"
#: deluge/ui/common.py:112
msgid "Move On Completed Path"
@@ -1614,7 +1614,7 @@ msgstr ""
#: deluge/ui/web/json_api.py:868
msgid "Daemon does not exist"
-msgstr ""
+msgstr "Taustaprosessia ei ole olemassa"
#: deluge/ui/web/json_api.py:875
msgid "Daemon not running"
@@ -1969,15 +1969,15 @@ msgstr "Ei yhdistetty"
#: deluge/ui/gtk3/statusbar.py:175
msgid "Connections (Limit)"
-msgstr ""
+msgstr "Yhteydet (raja)"
#: deluge/ui/gtk3/statusbar.py:182
msgid "Download Speed (Limit)"
-msgstr ""
+msgstr "Latausnopeus (raja)"
#: deluge/ui/gtk3/statusbar.py:189
msgid "Upload Speed (Limit)"
-msgstr ""
+msgstr "Lähetysnopeus (raja)"
#: deluge/ui/gtk3/statusbar.py:196
msgid "Protocol Traffic (Down:Up)"
@@ -2450,11 +2450,11 @@ msgstr ""
#: deluge/ui/gtk3/menubar.py:466
msgid "Set the maximum upload slots"
-msgstr ""
+msgstr "Aseta lähetyspaikkojen enimmäismäärä"
#: deluge/ui/gtk3/menubar.py:471
msgid "Stop Seed At Ratio"
-msgstr ""
+msgstr "Lopeta jakaminen suhteessa"
#: deluge/ui/gtk3/menubar.py:606
msgid "Ownership Change Error"
@@ -2494,7 +2494,7 @@ msgstr "Torrentit jonossa"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:4
msgid "Add Queued Torrents"
-msgstr ""
+msgstr "Lisää jonotetut torrentit"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:5
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:7
@@ -2775,7 +2775,7 @@ msgstr "Socks5"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:10
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:36
msgid "Socks5 Auth"
-msgstr ""
+msgstr "Socks5-todennus"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:11
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:37
@@ -2785,7 +2785,7 @@ msgstr "HTTP"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:12
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:38
msgid "HTTP Auth"
-msgstr ""
+msgstr "HTTP-todennus"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:13
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:39
@@ -2802,11 +2802,11 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:21
msgid "Connect to a Deluge daemon (deluged)"
-msgstr ""
+msgstr "Yhdistä Deluge-taustaprosessiin (deluged)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:22
msgid "Application Mode"
-msgstr ""
+msgstr "Sovellustila"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:46
@@ -2841,11 +2841,11 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:31
msgid "Waiting:"
-msgstr ""
+msgstr "Odottaa:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:32
msgid "Missing:"
-msgstr ""
+msgstr "Puuttuu:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:33
msgid "_Revert"
@@ -2853,7 +2853,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:34
msgid "Revert color to default"
-msgstr ""
+msgstr "Palauta väri oletukseksi"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:35
msgid "Piece Colors"
@@ -2889,7 +2889,7 @@ msgstr "Suojaa ilmoitusalueelle pienennetty Deluge salasanalla"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:44
msgid "System Tray"
-msgstr ""
+msgstr "Ilmoitusalue"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:45
msgid "Notify about new releases"
@@ -2939,7 +2939,7 @@ msgstr "Lataa kansioon:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:54
msgid "Download Folders"
-msgstr ""
+msgstr "Latauskansiot"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:55
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:93
@@ -2975,15 +2975,15 @@ msgstr "Lisää torrentit keskeytettyinä"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:65
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:120
msgid "Pre-allocate disk space"
-msgstr ""
+msgstr "Ennakkovaraa levytila"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:66
msgid "Pre-allocate the disk space for the torrent files"
-msgstr ""
+msgstr "Ennakkovaraa levytila torrent-tiedostoille"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:67
msgid "Add Torrent Options"
-msgstr ""
+msgstr "Lisää torrent -valinnat"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:68
msgid "Always show"
@@ -2995,15 +2995,15 @@ msgstr "Tuo valintaikkuna eteen"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:70
msgid "Add Torrents Dialog"
-msgstr ""
+msgstr "Lisää torrentti -ikkuna"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:71
msgid "Connection Attempts per Second:"
-msgstr ""
+msgstr "Yhteysyrityksiä per sekunti:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:72
msgid "Half-Open Connections:"
-msgstr ""
+msgstr "Puoliksi avoimet yhteydet:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:73
msgid "The maximum number of connections allowed. Set -1 for unlimited."
@@ -3028,7 +3028,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:32
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:5
msgid "Upload Slots:"
-msgstr ""
+msgstr "Lähetyspaikkoja:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:77
msgid "The maximum download speed for all torrents. Set -1 for unlimited."
@@ -3073,7 +3073,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:85
msgid "Global Bandwidth Limits"
-msgstr ""
+msgstr "Yleiset kaistanleveyden rajoitukset"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:86
msgid "The maximum upload slots per torrent. Set -1 for unlimited."
@@ -3095,19 +3095,19 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:90
msgid "Per-Torrent Bandwidth Limits"
-msgstr ""
+msgstr "Torrent-kohtaiset kaistanleveyden rajoitukset"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:91
#: deluge/ui/console/modes/preferences/preference_panes.py:556
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:42
msgid "Queue to top"
-msgstr ""
+msgstr "Jonota ylimmäksi"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:92
#: deluge/ui/console/modes/preferences/preference_panes.py:554
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:30
msgid "New Torrents"
-msgstr ""
+msgstr "Uudet torrentit"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:93
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:85
@@ -3117,7 +3117,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:94
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:59
msgid "Total:"
-msgstr ""
+msgstr "Yhteensä:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:95
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:102
@@ -3143,7 +3143,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:558
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:50
msgid "Active Torrents"
-msgstr ""
+msgstr "Aktiiviset torrentit"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:100
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:7
@@ -3161,7 +3161,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:102
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:157
msgid "Time (m):"
-msgstr ""
+msgstr "Aika (min):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:103
#: deluge/ui/console/modes/preferences/preference_panes.py:590
@@ -3177,7 +3177,7 @@ msgstr "Keskeytä torrent"
#: deluge/ui/console/modes/preferences/preference_panes.py:627
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:173
msgid "Share Ratio Reached"
-msgstr ""
+msgstr "Jakosuhde saavutettu"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:107
msgid ""
@@ -3223,7 +3223,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:359
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:101
msgid "Outgoing Interface"
-msgstr ""
+msgstr "Lähtevä sovitin"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:118
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:11
@@ -3240,7 +3240,7 @@ msgstr "Päättyen:"
#: deluge/ui/console/modes/preferences/preference_panes.py:328
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:120
msgid "Outgoing Ports"
-msgstr ""
+msgstr "Lähtevät portit"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:121
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:59
@@ -3318,7 +3318,7 @@ msgstr "Vertaisen TOS-tavu:"
#: deluge/ui/console/modes/preferences/preference_panes.py:372
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:181
msgid "Network Extras"
-msgstr ""
+msgstr "Verkon lisäasetukset"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:137
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:4
@@ -3368,7 +3368,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:147
msgid "Force Proxy Use"
-msgstr ""
+msgstr "Pakota välityspalvelimen käyttö"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:148
#: deluge/ui/console/modes/preferences/preference_panes.py:671
@@ -3386,7 +3386,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:669
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:120
msgid "Force Proxy"
-msgstr ""
+msgstr "Pakota välityspalvelin"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:151
msgid "Cache Size (16 KiB blocks):"
@@ -3449,7 +3449,7 @@ msgstr "Osumasuhde kirjoitetulle välimuistille:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:161
#: deluge/ui/console/modes/preferences/preference_panes.py:709
msgid "Write"
-msgstr ""
+msgstr "Kirjoitettu"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:162
msgid ""
@@ -3491,7 +3491,7 @@ msgstr "Luettu:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:170
#: deluge/ui/console/modes/preferences/preference_panes.py:723
msgid "Read"
-msgstr ""
+msgstr "Luettu"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:171
msgid ""
@@ -3554,11 +3554,11 @@ msgstr "GeoIP-tietokanta"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:183
msgid "Associate with Deluge"
-msgstr ""
+msgstr "Liitä Delugeen"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:184
msgid "Magnet Links"
-msgstr ""
+msgstr "Magneettilinkit"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:185
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:37
@@ -3629,11 +3629,11 @@ msgstr "_Asenna"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:202
msgid "_Find More..."
-msgstr ""
+msgstr "_Löydä enemmän..."
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:4
msgid "Remove the selected torrent(s)?"
-msgstr ""
+msgstr "Poistetaanko valitut torrentit?"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:5
msgid "Include downloaded files"
@@ -3641,7 +3641,7 @@ msgstr "Sisällytä ladatut tiedostot"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:6
msgid "(This is permanent!)"
-msgstr ""
+msgstr "(Tätä ei voi perua!)"
#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:1
msgid "Add Peer"
@@ -3691,7 +3691,7 @@ msgstr "Näytä piilotetut tiedostot"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:11
msgid "Set new key"
-msgstr ""
+msgstr "Aseta uusi avain"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:12
msgid "Press this key to set new key accelerators to trigger auto-complete"
@@ -3829,7 +3829,7 @@ msgstr "Lisää Infohash"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
msgid "From Infohash"
-msgstr ""
+msgstr "Infohashista"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
msgid "Infohash:"
@@ -3892,13 +3892,13 @@ msgstr "Älä näytä tätä ilmoitusta tulevaisuudessa"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:26
#: deluge/ui/web/render/tab_status.html:9
msgid "Down Speed:"
-msgstr ""
+msgstr "Latausnopeus:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:2
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:28
#: deluge/ui/web/render/tab_status.html:10
msgid "Up Speed:"
-msgstr ""
+msgstr "Lähetysnopeus:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:3
#: deluge/ui/web/render/tab_status.html:2
@@ -3913,7 +3913,7 @@ msgstr "Lähetetty:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:5
#: deluge/ui/web/render/tab_status.html:16
msgid "Seeds:"
-msgstr ""
+msgstr "Jakajia:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:6
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:10
@@ -3924,7 +3924,7 @@ msgstr "Vertaiset:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:8
#: deluge/ui/web/render/tab_status.html:18
msgid "Availability:"
-msgstr ""
+msgstr "Saatavuus:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:9
#: deluge/ui/web/render/tab_status.html:25
@@ -3933,7 +3933,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:10
msgid "ETA Time:"
-msgstr ""
+msgstr "Arvioitu valmistumisaika:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:11
#: deluge/ui/web/render/tab_status.html:13
@@ -3943,7 +3943,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:12
#: deluge/ui/web/render/tab_status.html:23
msgid "Active Time:"
-msgstr ""
+msgstr "Aktiivisuusaika:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:13
#: deluge/ui/web/render/tab_status.html:20
@@ -3953,7 +3953,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:14
#: deluge/ui/web/render/tab_status.html:24
msgid "Seeding Time:"
-msgstr ""
+msgstr "Jakoaika:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:16
#: deluge/ui/web/render/tab_status.html:12
@@ -4024,7 +4024,7 @@ msgstr "Poista, kun jakosuhde on"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:44
msgid "Bandwidth Limits"
-msgstr ""
+msgstr "Kaistanleveyden rajoitukset"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:46
msgid "Current Tracker:"
@@ -4037,7 +4037,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:48
#: deluge/ui/web/render/tab_status.html:6
msgid "Tracker Status:"
-msgstr ""
+msgstr "Seurantapalvelimen tila:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:49
#: deluge/ui/web/render/tab_status.html:5
@@ -4095,16 +4095,16 @@ msgstr "Suosi ensimmäisiä / viimeisiä osia"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:46
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:152
msgid "Skip File Hash Check"
-msgstr ""
+msgstr "Ohita tiedoston tiivisteen tarkistus"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:170
msgid "Preallocate Disk Space"
-msgstr ""
+msgstr "Ennakkovaraa levytila"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:24
msgid "Preallocate the disk space for the torrent files"
-msgstr ""
+msgstr "Ennakkovaraa levytila torrent-tiedostoille"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:25
msgid "Maximum torrent download speed"
@@ -4116,7 +4116,7 @@ msgstr ""
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:29
msgid "Maximum torrent connections"
-msgstr ""
+msgstr "Torrent-yhteyksien enimmäismäärä"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:31
msgid "Maximum torrent upload slots"
@@ -4391,7 +4391,7 @@ msgstr "Lisää verkko-osoite"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:4
msgid "From URL"
-msgstr ""
+msgstr "Verkko-osoitteesta"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:5
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:1
@@ -4400,7 +4400,7 @@ msgstr "Osoite:"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:9
msgid "Deluge Daemons"
-msgstr ""
+msgstr "Deluge-taustaprosessit"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:10
msgid "Auto-connect to selected Daemon"
@@ -4446,16 +4446,16 @@ msgstr "_Normaali"
msgid "_High"
msgstr "_Korkea"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Deluge-tiimi"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4464,7 +4464,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -4612,7 +4612,7 @@ msgstr "Ottaa liitännäisen käyttöön"
#: deluge/ui/console/cmdline/commands/plugin.py:43
msgid "Disables a plugin"
-msgstr ""
+msgstr "Poistaa liitännäisen käytöstä"
#: deluge/ui/console/cmdline/commands/plugin.py:51
msgid "Reload list of available plugins"
@@ -4620,7 +4620,7 @@ msgstr ""
#: deluge/ui/console/cmdline/commands/plugin.py:54
msgid "Install a plugin from an .egg file"
-msgstr ""
+msgstr "Asenna liitännäinen .egg-tiedostosta"
#: deluge/ui/console/cmdline/commands/status.py:36
msgid ""
@@ -4656,7 +4656,7 @@ msgstr ""
#: deluge/ui/console/cmdline/commands/help.py:29
msgid "One or more commands"
-msgstr ""
+msgstr "Yksi tai useampi komento"
#: deluge/ui/console/cmdline/commands/config.py:79
msgid "Usage: config [--set <key> <value>] [<key> [<key>...] ]"
@@ -4764,7 +4764,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:254
msgid "Move completed to"
-msgstr ""
+msgstr "Siirrä valmistuneet sijaintiin"
#: deluge/ui/console/modes/preferences/preference_panes.py:269
msgid "Copy of .torrent files to"
@@ -4776,7 +4776,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:293
msgid "Pre-Allocate disk space"
-msgstr ""
+msgstr "Ennakkovaraa levytila"
#: deluge/ui/console/modes/preferences/preference_panes.py:304
msgid "Incomming Ports"
@@ -4822,7 +4822,7 @@ msgstr "Ulos"
#: deluge/ui/console/modes/preferences/preference_panes.py:413
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:38
msgid "Global Bandwidth Usage"
-msgstr ""
+msgstr "Yleinen kaistankäyttö"
#: deluge/ui/console/modes/preferences/preference_panes.py:416
#: deluge/ui/console/modes/preferences/preference_panes.py:469
@@ -4860,7 +4860,7 @@ msgstr "Nopeusraja IP:n yläpuolella"
#: deluge/ui/console/modes/preferences/preference_panes.py:466
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:148
msgid "Per Torrent Bandwidth Usage"
-msgstr ""
+msgstr "Torrent-kohtainen kaistankäyttö"
#: deluge/ui/console/modes/preferences/preference_panes.py:513
msgid "Yes, please send anonymous statistics."
@@ -4868,7 +4868,7 @@ msgstr "Kyllä, lähetä anonyymeja tilastoja."
#: deluge/ui/console/modes/preferences/preference_panes.py:531
msgid "Daemon Port"
-msgstr ""
+msgstr "Taustaprosessin portti"
#: deluge/ui/console/modes/preferences/preference_panes.py:538
msgid "Allow remote connections"
@@ -4884,11 +4884,11 @@ msgstr "Jakosuhde"
#: deluge/ui/console/modes/preferences/preference_panes.py:601
msgid "Time Ratio"
-msgstr ""
+msgstr "Aikasuhde"
#: deluge/ui/console/modes/preferences/preference_panes.py:609
msgid "Time (m)"
-msgstr ""
+msgstr "Aika (min)"
#: deluge/ui/console/modes/preferences/preference_panes.py:633
msgid "Remove torrent (Unchecked pauses torrent)"
@@ -4912,11 +4912,11 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:697
msgid "Cache Size (16 KiB blocks)"
-msgstr ""
+msgstr "Välimuistin koko (16 KiB:in lohkoissa)"
#: deluge/ui/console/modes/preferences/preference_panes.py:704
msgid "Cache Expiry (seconds)"
-msgstr ""
+msgstr "Välimuistin vanheneminen (sekuntia)"
#: deluge/ui/console/modes/preferences/preference_panes.py:712
msgid "Blocks Written"
@@ -4981,7 +4981,7 @@ msgstr ""
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:118
#, python-format
msgid "The IP address \"%s\" is badly formed"
-msgstr ""
+msgstr "IP-osoite \"%s\" on väärin muodostettu"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:21
msgid "Emule IP list (GZip)"
@@ -5253,7 +5253,7 @@ msgstr "Pää"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:31
msgid "The user selected here will be the owner of the torrent."
-msgstr ""
+msgstr "Tässä valittu käyttäjä tulee olemaan torrentin omistaja."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:32
msgid "<b>Owner</b>"
@@ -5306,7 +5306,7 @@ msgstr "<b>Tarkkaile hakemistoja:</b>"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:60
msgid "minutes"
-msgstr ""
+msgstr "minuuttia"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:62
msgid "1 minute"
@@ -5322,7 +5322,7 @@ msgstr "sekuntia"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:1
msgid "Stats"
-msgstr ""
+msgstr "Tilastot"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:2
msgid "Resolution"
@@ -5330,15 +5330,15 @@ msgstr ""
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:5
msgid "Seeds/Peers"
-msgstr ""
+msgstr "Jakajat/vertaiset"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:1
msgid "Download color:"
-msgstr ""
+msgstr "Latausväri:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:2
msgid "Upload color:"
-msgstr ""
+msgstr "Lähetysväri:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:3
msgid "<b>Connections Graph</b>"
@@ -5350,11 +5350,11 @@ msgstr "<b>Kaistanleveyden kuvaaja</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:5
msgid "DHT nodes:"
-msgstr ""
+msgstr "DHT-solmut:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:6
msgid "Cached DHT nodes:"
-msgstr ""
+msgstr "Välimuistissa olevat DHT-solmut:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:7
msgid "DHT torrents:"
@@ -5362,7 +5362,7 @@ msgstr "DHT-torrentit:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:9
msgid "<b>Seeds / Peers</b>"
-msgstr ""
+msgstr "<b>Jakajat / vertaiset</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:11
msgid "<b>Graph Colors</b>"
@@ -5462,7 +5462,7 @@ msgstr "Käytä jonoasetuksia:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:17
msgid "Apply folder settings:"
-msgstr ""
+msgstr "Toteuta kansioasetukset:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:19
msgid "<i>(1 line per tracker)</i>"
@@ -5664,7 +5664,7 @@ msgstr "Ajastin"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:289
msgid "<b>Schedule</b>"
-msgstr ""
+msgstr "<b>Aikataulu</b>"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:301
msgid "Download Limit:"
@@ -5867,7 +5867,7 @@ msgstr "Yhdistä"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:197
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:379
msgid "Stop Daemon"
-msgstr ""
+msgstr "Pysäytä taustaprosessi"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:185
msgid "Disconnect"
@@ -5875,7 +5875,7 @@ msgstr "Katkaise yhteys"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:204
msgid "Start Daemon"
-msgstr ""
+msgstr "Käynnistä taustaprosessi"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:322
msgid "Change Default Password"
@@ -5953,7 +5953,7 @@ msgstr "Tila:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:242
msgid "Move Completed:"
-msgstr ""
+msgstr "Siirrä valmistuneet:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:272
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:116
@@ -6040,11 +6040,11 @@ msgstr "Lähetyspaikkojen enimmäismäärä:"
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:43
msgid "Cache Size (16 KiB Blocks):"
-msgstr ""
+msgstr "Välimuistin koko (16 KiB:in lohkoissa):"
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:132
msgid "Force Use of Proxy"
-msgstr ""
+msgstr "Pakota välityspalvelimen käyttö"
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:116
msgid "Find More"
@@ -6088,7 +6088,7 @@ msgstr "Palvelin"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:140
msgid "Session Timeout:"
-msgstr ""
+msgstr "Istunnon aikakatkaisu:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:165
msgid "Enable SSL (paths relative to Deluge config folder)"
@@ -6173,15 +6173,15 @@ msgstr "Torrent ei ole kelvollinen"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:50
msgid "Move Completed Folder"
-msgstr ""
+msgstr "Siirrä valmistuneet -kansio"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:85
msgid "Max Down Speed"
-msgstr ""
+msgstr "Latausnopeus enintään"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:93
msgid "Max Up Speed"
-msgstr ""
+msgstr "Lähetysnopeus enintään"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:125
msgid "Add In Paused State"
diff --git a/deluge/i18n/fo.po b/deluge/i18n/fo.po
index 86205febd..33fff5bf9 100644
--- a/deluge/i18n/fo.po
+++ b/deluge/i18n/fo.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/fr.po b/deluge/i18n/fr.po
index 49619fe3d..06e17a78b 100644
--- a/deluge/i18n/fr.po
+++ b/deluge/i18n/fr.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4519,16 +4519,16 @@ msgstr "_Normal"
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "L'équipe de Deluge"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr "Deluge est un client BitTorrent multiplate-forme léger et libre."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4537,7 +4537,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -6307,5 +6307,5 @@ msgstr "Temps restant estimé :"
msgid "Date Added:"
msgstr ""
-#~ msgid "<b>Languge</b>"
+#~ msgid "<b>Language</b>"
#~ msgstr "<b>Langue</b>"
diff --git a/deluge/i18n/fy.po b/deluge/i18n/fy.po
index d4703ce83..aa217c641 100644
--- a/deluge/i18n/fy.po
+++ b/deluge/i18n/fy.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ga.po b/deluge/i18n/ga.po
index e66a80735..6d4ae4351 100644
--- a/deluge/i18n/ga.po
+++ b/deluge/i18n/ga.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/gl.po b/deluge/i18n/gl.po
index 30d58442f..15e6b372a 100644
--- a/deluge/i18n/gl.po
+++ b/deluge/i18n/gl.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
"X-Poedit-Language: Galician\n"
#: deluge/common.py:411
@@ -4481,16 +4481,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4499,7 +4499,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/he.po b/deluge/i18n/he.po
index e91035460..7900c3d4a 100644
--- a/deluge/i18n/he.po
+++ b/deluge/i18n/he.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4426,16 +4426,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4444,7 +4444,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/hi.po b/deluge/i18n/hi.po
index b1400315c..f3dae8a5a 100644
--- a/deluge/i18n/hi.po
+++ b/deluge/i18n/hi.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-06-06 10:57+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2023-02-17 20:51+0000\n"
+"Last-Translator: Hemish <Unknown>\n"
"Language-Team: Hindi <hi@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -198,7 +198,7 @@ msgstr "सभी"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:534
#: deluge/ui/web/js/deluge-all/UI.js:19
msgid "Active"
-msgstr ""
+msgstr "सक्रिय"
#: deluge/ui/common.py:39 deluge/ui/web/js/deluge-all/UI.js:20
msgid "Allocating"
@@ -223,11 +223,11 @@ msgstr "सीड किया जा रहा है"
#: deluge/ui/common.py:43 deluge/ui/web/js/deluge-all/UI.js:24
msgid "Paused"
-msgstr "ठहराया हुआ"
+msgstr "ठहरा हुआ"
#: deluge/ui/common.py:44 deluge/ui/web/js/deluge-all/UI.js:26
msgid "Queued"
-msgstr "क़तार-बद्ध"
+msgstr "पंक्तिबद्ध"
#: deluge/ui/common.py:45 deluge/ui/common.py:122
#: deluge/ui/gtk3/statusbar.py:396 deluge/ui/gtk3/filtertreeview.py:131
@@ -288,7 +288,7 @@ msgstr "अपलोडेड"
#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
msgid "Remaining"
-msgstr ""
+msgstr "बाकी"
#: deluge/ui/common.py:58 deluge/ui/gtk3/torrentview.py:373
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:165
@@ -393,7 +393,7 @@ msgstr ""
#: deluge/ui/common.py:86 deluge/ui/gtk3/torrentview.py:394
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:197
msgid "Completed"
-msgstr ""
+msgstr "पूर्ण"
#: deluge/ui/common.py:87 deluge/ui/gtk3/torrentview.py:366
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:158
@@ -404,7 +404,7 @@ msgstr "इ टी ए (E T A)"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:236
msgid "Shared"
-msgstr ""
+msgstr "साझा किए गए"
#: deluge/ui/common.py:90 deluge/ui/gtk3/glade/main_window.tabs.ui.h:31
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:287
@@ -455,7 +455,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/FilterPanel.js:32
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:221
msgid "Owner"
-msgstr ""
+msgstr "मालिक"
#: deluge/ui/common.py:116
msgid "Pieces"
@@ -539,7 +539,7 @@ msgstr "प्रॉक्सी"
#: deluge/ui/console/modes/preferences/preferences.py:97
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:18
msgid "Cache"
-msgstr ""
+msgstr "कैश"
#: deluge/ui/common.py:136 deluge/ui/gtk3/glade/preferences_dialog.ui.h:190
#: deluge/ui/console/modes/preferences/preference_panes.py:499
@@ -567,22 +567,22 @@ msgstr "प्लग-इन्स"
#: deluge/ui/common.py:150 deluge/ui/web/js/deluge-all/Deluge.js:154
#: deluge/ui/web/js/deluge-all/Menus.js:365
msgid "Skip"
-msgstr ""
+msgstr "अभी नहीं"
#: deluge/ui/common.py:151 deluge/ui/web/js/deluge-all/Deluge.js:155
#: deluge/ui/web/js/deluge-all/Menus.js:371
msgid "Low"
-msgstr ""
+msgstr "कम"
#: deluge/ui/common.py:152 deluge/ui/web/js/deluge-all/Deluge.js:156
#: deluge/ui/web/js/deluge-all/Menus.js:377
msgid "Normal"
-msgstr ""
+msgstr "सामान्य"
#: deluge/ui/common.py:153 deluge/ui/web/js/deluge-all/Deluge.js:157
#: deluge/ui/web/js/deluge-all/Menus.js:383
msgid "High"
-msgstr ""
+msgstr "उच्च"
#: deluge/ui/client.py:681
msgid ""
@@ -592,7 +592,7 @@ msgstr ""
#: deluge/ui/countries.py:10
msgid "Afghanistan"
-msgstr ""
+msgstr "अफ़ग़ानिस्तान"
#: deluge/ui/countries.py:11
msgid "Aland Islands"
@@ -644,15 +644,15 @@ msgstr ""
#: deluge/ui/countries.py:23
msgid "Australia"
-msgstr ""
+msgstr "ऑस्ट्रेलिया"
#: deluge/ui/countries.py:24
msgid "Austria"
-msgstr ""
+msgstr "ऑस्ट्रिया"
#: deluge/ui/countries.py:25
msgid "Azerbaijan"
-msgstr ""
+msgstr "अज़रबैजान"
#: deluge/ui/countries.py:26
msgid "Bahamas"
@@ -664,7 +664,7 @@ msgstr ""
#: deluge/ui/countries.py:28
msgid "Bangladesh"
-msgstr ""
+msgstr "बांग्लादेश"
#: deluge/ui/countries.py:29
msgid "Barbados"
@@ -672,11 +672,11 @@ msgstr ""
#: deluge/ui/countries.py:30
msgid "Belarus"
-msgstr ""
+msgstr "बेलारूस"
#: deluge/ui/countries.py:31
msgid "Belgium"
-msgstr ""
+msgstr "बेल्जियम"
#: deluge/ui/countries.py:32
msgid "Belize"
@@ -1887,7 +1887,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:65
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:74
msgid "Username:"
-msgstr "उपयोगकर्ता का नाम:"
+msgstr "उपयोगक्ता का नाम:"
#: deluge/ui/gtk3/dialogs.py:217 deluge/ui/gtk3/dialogs.py:310
#: deluge/ui/gtk3/dialogs.py:437
@@ -2264,7 +2264,7 @@ msgstr "स्तर"
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:67
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:71
msgid "Enabled"
-msgstr "सक्रिय किया"
+msgstr "सक्रिय"
#: deluge/ui/gtk3/preferences.py:162
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:87
@@ -3223,7 +3223,7 @@ msgstr ""
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:11
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:155
msgid "From:"
-msgstr "द्वारा:"
+msgstr "प्रेषक:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:119
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:165
@@ -3403,7 +3403,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:29
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:30
msgid "Settings"
-msgstr ""
+msgstr "सेटिंग्स"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:155
msgid ""
@@ -4429,16 +4429,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4447,7 +4447,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -5023,7 +5023,7 @@ msgstr "चालु होने पर ब्लॉकलिस्ट आय
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:6
#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:4
msgid "<b>Settings</b>"
-msgstr "<b>सेटिंग</b>"
+msgstr "<b>सेटिंग्स</b>"
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:7
msgid "Download the blocklist file if necessary and import the file."
@@ -5126,7 +5126,7 @@ msgstr ""
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:507
msgid "Path"
-msgstr ""
+msgstr "पाथ"
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:125
msgid "Watch folder does not exist."
@@ -5135,7 +5135,7 @@ msgstr ""
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:128
#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:443
msgid "Path does not exist."
-msgstr ""
+msgstr "पाथ मौजूद नहीं है"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:1
msgid "Watch Folder Properties"
@@ -5372,7 +5372,7 @@ msgstr ""
#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:3
msgid "Listening port:"
-msgstr ""
+msgstr "सुनने का पोर्ट:"
#: deluge/plugins/Label/deluge_label/core.py:184
msgid "Invalid label, valid characters:[a-z0-9_-]"
@@ -5495,7 +5495,7 @@ msgstr ""
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:194
msgid "pygame is not installed"
-msgstr ""
+msgstr "pygame स्थापित नहीं है"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:206
#, python-format
@@ -5573,7 +5573,7 @@ msgstr ""
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:3
msgid "Sound enabled"
-msgstr ""
+msgstr "ध्वनि सक्षम हैं"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:4
msgid "<b>UI Notifications</b>"
@@ -5724,7 +5724,7 @@ msgstr "स्थानान्तर"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:54
msgid "Browse"
-msgstr ""
+msgstr "ब्राउज़ करें"
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:17
msgid "Edit Connection"
@@ -5757,7 +5757,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/Deluge.js:158
msgid "Mixed"
-msgstr ""
+msgstr "मिश्रित"
#: deluge/ui/web/js/deluge-all/Statusbar.js:87
msgid "Set Maximum Connections"
@@ -5906,7 +5906,7 @@ msgstr "बलपूर्वक पुनः जांच"
#: deluge/ui/web/js/deluge-all/Menus.js:359
msgid "Expand All"
-msgstr ""
+msgstr "सभी फैलाएँ"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:13
msgid "Details"
@@ -5914,11 +5914,11 @@ msgstr "विवरण"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:28
msgid "Comment:"
-msgstr ""
+msgstr "टिप्पणीः"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:29
msgid "Status:"
-msgstr ""
+msgstr "स्थिति:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:242
msgid "Move Completed:"
@@ -5935,7 +5935,7 @@ msgstr "निजी"
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:39
msgid "Loading"
-msgstr ""
+msgstr "लोड हो रहा है"
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:118
msgid "True"
@@ -5962,7 +5962,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:33
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:109
msgid "Install"
-msgstr ""
+msgstr "स्थापित करें"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:45
msgid "Select an egg"
@@ -6041,11 +6041,11 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:110
msgid "Old:"
-msgstr ""
+msgstr "पुराना:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:114
msgid "New:"
-msgstr ""
+msgstr "नया:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:118
msgid "Confirm:"
@@ -6081,11 +6081,11 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:210
msgid "Refresh"
-msgstr ""
+msgstr "ताज़ा करें"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:244
msgid "Invalid Password"
-msgstr ""
+msgstr "अवैध पासवर्ड"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:245
msgid "Your passwords don't match!"
@@ -6114,7 +6114,7 @@ msgstr "यूआरएल"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:45
msgid "Cookies"
-msgstr ""
+msgstr "कुकी"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:99
msgid "Failed to download torrent"
diff --git a/deluge/i18n/hr.po b/deluge/i18n/hr.po
index 92bf1fb5e..0b4b9a89d 100644
--- a/deluge/i18n/hr.po
+++ b/deluge/i18n/hr.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
"Language: hr\n"
#: deluge/common.py:411
@@ -4482,16 +4482,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4500,7 +4500,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -6245,5 +6245,5 @@ msgstr ""
msgid "Date Added:"
msgstr ""
-#~ msgid "<b>Languge</b>"
+#~ msgid "<b>Language</b>"
#~ msgstr "<b>Jezik</b>"
diff --git a/deluge/i18n/hu.po b/deluge/i18n/hu.po
index 1e83f2b9e..f4acc752d 100644
--- a/deluge/i18n/hu.po
+++ b/deluge/i18n/hu.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4427,16 +4427,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4445,7 +4445,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/id.po b/deluge/i18n/id.po
index fcacd264e..22af8f21f 100644
--- a/deluge/i18n/id.po
+++ b/deluge/i18n/id.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/is.po b/deluge/i18n/is.po
index ae38149cc..5b2cbf362 100644
--- a/deluge/i18n/is.po
+++ b/deluge/i18n/is.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4432,16 +4432,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4450,7 +4450,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/it.po b/deluge/i18n/it.po
index eecf8239f..d571b2b71 100644
--- a/deluge/i18n/it.po
+++ b/deluge/i18n/it.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4458,16 +4458,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4476,7 +4476,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/iu.po b/deluge/i18n/iu.po
index f95359d1a..e331044c2 100644
--- a/deluge/i18n/iu.po
+++ b/deluge/i18n/iu.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ja.po b/deluge/i18n/ja.po
index f1bcc6bd1..576dd21ea 100644
--- a/deluge/i18n/ja.po
+++ b/deluge/i18n/ja.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4427,16 +4427,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4445,7 +4445,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ka.po b/deluge/i18n/ka.po
index 9df9554aa..f3fd634a2 100644
--- a/deluge/i18n/ka.po
+++ b/deluge/i18n/ka.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4473,16 +4473,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4491,7 +4491,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/kk.po b/deluge/i18n/kk.po
index 88e2f6b8a..03531f5c0 100644
--- a/deluge/i18n/kk.po
+++ b/deluge/i18n/kk.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4446,16 +4446,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4464,7 +4464,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/km.po b/deluge/i18n/km.po
index 17831d8ff..c5c28fa54 100644
--- a/deluge/i18n/km.po
+++ b/deluge/i18n/km.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/kn.po b/deluge/i18n/kn.po
index 4ed7db089..63fc239c2 100644
--- a/deluge/i18n/kn.po
+++ b/deluge/i18n/kn.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ko.po b/deluge/i18n/ko.po
index 3e706d3f8..566627211 100644
--- a/deluge/i18n/ko.po
+++ b/deluge/i18n/ko.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4426,16 +4426,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4444,7 +4444,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ku.po b/deluge/i18n/ku.po
index e8686bff5..e07b61ee8 100644
--- a/deluge/i18n/ku.po
+++ b/deluge/i18n/ku.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ky.po b/deluge/i18n/ky.po
index 012591470..9391ed855 100644
--- a/deluge/i18n/ky.po
+++ b/deluge/i18n/ky.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/la.po b/deluge/i18n/la.po
index 55e3a6f1b..a4248affd 100644
--- a/deluge/i18n/la.po
+++ b/deluge/i18n/la.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/languages.py b/deluge/i18n/languages.py
index 49dc53026..5673c7116 100644
--- a/deluge/i18n/languages.py
+++ b/deluge/i18n/languages.py
@@ -1,10 +1,7 @@
-# -*- coding: utf-8 -*-
#
# 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'
diff --git a/deluge/i18n/lb.po b/deluge/i18n/lb.po
index 88a0c7d33..21b4fe160 100644
--- a/deluge/i18n/lb.po
+++ b/deluge/i18n/lb.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/lt.po b/deluge/i18n/lt.po
index 42755937a..24dd8605e 100644
--- a/deluge/i18n/lt.po
+++ b/deluge/i18n/lt.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4467,16 +4467,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4485,7 +4485,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/lv.po b/deluge/i18n/lv.po
index 127525018..da35853cf 100644
--- a/deluge/i18n/lv.po
+++ b/deluge/i18n/lv.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-06-06 10:57+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2022-08-03 16:14+0000\n"
+"Last-Translator: Coool <Unknown>\n"
"Language-Team: Latvian <lv@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -125,6 +125,8 @@ msgstr ""
#: deluge/argparserbase.py:206
msgid "Set the log level (none, error, warning, info, debug)"
msgstr ""
+"Iestatīt žurnāla līmeni (nežurnalēt, kļūda, brīdinājums, informācija, "
+"atkļūdošana)"
#: deluge/argparserbase.py:215
#, python-format
@@ -4448,16 +4450,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4466,7 +4468,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/mk.po b/deluge/i18n/mk.po
index ce6b73519..c6609827d 100644
--- a/deluge/i18n/mk.po
+++ b/deluge/i18n/mk.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4449,16 +4449,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4467,7 +4467,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ml.po b/deluge/i18n/ml.po
index 6ac429eff..a41fc6574 100644
--- a/deluge/i18n/ml.po
+++ b/deluge/i18n/ml.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/mo.po b/deluge/i18n/mo.po
new file mode 100644
index 000000000..1699872b1
--- /dev/null
+++ b/deluge/i18n/mo.po
@@ -0,0 +1,6164 @@
+# Moldavian translation for deluge
+# Copyright (c) 2023 Rosetta Contributors and Canonical Ltd 2023
+# This file is distributed under the same license as the deluge package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: deluge\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2019-11-12 14:55+0000\n"
+"PO-Revision-Date: 2023-03-07 14:10+0000\n"
+"Last-Translator: Vlad Nenea <Unknown>\n"
+"Language-Team: Moldavian <mo@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: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
+
+#: deluge/common.py:411
+msgid "B"
+msgstr ""
+
+#: deluge/common.py:412
+msgid "KiB"
+msgstr ""
+
+#: deluge/common.py:413
+msgid "MiB"
+msgstr ""
+
+#: deluge/common.py:414
+msgid "GiB"
+msgstr ""
+
+#: deluge/common.py:415
+msgid "TiB"
+msgstr ""
+
+#: deluge/common.py:416
+msgid "K"
+msgstr ""
+
+#: deluge/common.py:417
+msgid "M"
+msgstr ""
+
+#: deluge/common.py:418
+msgid "G"
+msgstr ""
+
+#: deluge/common.py:419
+msgid "T"
+msgstr ""
+
+#: 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
+#: deluge/ui/gtk3/tab_data_funcs.py:37 deluge/ui/gtk3/systemtray.py:237
+#: deluge/ui/gtk3/systemtray.py:241 deluge/ui/gtk3/systemtray.py:264
+#: deluge/ui/gtk3/systemtray.py:274 deluge/ui/gtk3/systemtray.py:442
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:40
+msgid "K/s"
+msgstr ""
+
+#: 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
+#: deluge/ui/console/widgets/statusbars.py:114
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:36
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:4
+#: deluge/ui/web/js/deluge-all/Statusbar.js:146
+#: deluge/ui/web/js/deluge-all/Statusbar.js:205
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:94
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:121
+msgid "KiB/s"
+msgstr ""
+
+#: deluge/common.py:521
+msgid "M/s"
+msgstr ""
+
+#: deluge/common.py:521
+msgid "MiB/s"
+msgstr ""
+
+#: deluge/common.py:527
+msgid "G/s"
+msgstr ""
+
+#: deluge/common.py:527
+msgid "GiB/s"
+msgstr ""
+
+#: deluge/common.py:533
+msgid "T/s"
+msgstr ""
+
+#: deluge/common.py:533
+msgid "TiB/s"
+msgstr ""
+
+#: deluge/argparserbase.py:172
+msgid "Common Options"
+msgstr ""
+
+#: deluge/argparserbase.py:175
+msgid "Print this help message"
+msgstr ""
+
+#: deluge/argparserbase.py:182
+msgid "Print version information"
+msgstr ""
+
+#: deluge/argparserbase.py:194
+msgid "Set the config directory path"
+msgstr ""
+
+#: deluge/argparserbase.py:200
+msgid "Output to specified logfile instead of stdout"
+msgstr ""
+
+#: deluge/argparserbase.py:206
+msgid "Set the log level (none, error, warning, info, debug)"
+msgstr ""
+
+#: deluge/argparserbase.py:215
+#, python-format
+msgid ""
+"Enable logfile rotation, with optional maximum logfile size, default: "
+"%(const)s (Logfile rotation count is 5)"
+msgstr ""
+
+#: deluge/argparserbase.py:223
+msgid "Quieten logging output (Same as `--loglevel none`)"
+msgstr ""
+
+#: deluge/argparserbase.py:231
+#, python-format
+msgid ""
+"Profile %(prog)s with cProfile. Outputs to stdout unless a filename is "
+"specified"
+msgstr ""
+
+#: deluge/argparserbase.py:351
+msgid "Process Control Options"
+msgstr ""
+
+#: deluge/argparserbase.py:357
+msgid "Pidfile to store the process id"
+msgstr ""
+
+#: deluge/argparserbase.py:365
+msgid "Do not daemonize (fork) this process"
+msgstr ""
+
+#: deluge/argparserbase.py:379
+msgid "Change to this user on startup (Requires root)"
+msgstr ""
+
+#: deluge/argparserbase.py:386
+msgid "Change to this group on startup (Requires root)"
+msgstr ""
+
+#: deluge/core/daemon_entry.py:25
+msgid "Daemon Options"
+msgstr ""
+
+#: deluge/core/daemon_entry.py:31
+msgid "IP address to listen for UI connections"
+msgstr ""
+
+#: deluge/core/daemon_entry.py:39
+msgid "Port to listen for UI connections on"
+msgstr ""
+
+#: deluge/core/daemon_entry.py:47
+msgid "IP address to listen for BitTorrent connections"
+msgstr ""
+
+#: deluge/core/daemon_entry.py:56
+msgid ""
+"The network interface name or IP address for outgoing BitTorrent connections."
+msgstr ""
+
+#: deluge/core/daemon_entry.py:63
+msgid "Config keys to be unmodified by `set_config` RPC"
+msgstr ""
+
+#: deluge/ui/common.py:37 deluge/ui/gtk3/filtertreeview.py:130
+#: deluge/ui/web/js/deluge-all/UI.js:18
+msgid "All"
+msgstr ""
+
+#: deluge/ui/common.py:38 deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:490
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:534
+#: deluge/ui/web/js/deluge-all/UI.js:19
+msgid "Active"
+msgstr ""
+
+#: deluge/ui/common.py:39 deluge/ui/web/js/deluge-all/UI.js:20
+msgid "Allocating"
+msgstr ""
+
+#: deluge/ui/common.py:40 deluge/ui/web/js/deluge-all/UI.js:21
+#: deluge/ui/web/js/deluge-all/UI.js:25
+msgid "Checking"
+msgstr ""
+
+#: deluge/ui/common.py:41
+#: deluge/ui/console/modes/preferences/preference_panes.py:568
+#: deluge/ui/web/js/deluge-all/UI.js:22
+msgid "Downloading"
+msgstr ""
+
+#: deluge/ui/common.py:42
+#: deluge/ui/console/modes/preferences/preference_panes.py:575
+#: deluge/ui/web/js/deluge-all/UI.js:23
+msgid "Seeding"
+msgstr ""
+
+#: deluge/ui/common.py:43 deluge/ui/web/js/deluge-all/UI.js:24
+msgid "Paused"
+msgstr ""
+
+#: deluge/ui/common.py:44 deluge/ui/web/js/deluge-all/UI.js:26
+msgid "Queued"
+msgstr ""
+
+#: deluge/ui/common.py:45 deluge/ui/common.py:122
+#: 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
+#: deluge/ui/web/js/deluge-all/Statusbar.js:351
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:358
+#: 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/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 ""
+
+#: deluge/ui/common.py:50 deluge/ui/gtk3/listview.py:793
+#: deluge/ui/gtk3/torrentview.py:180 deluge/ui/gtk3/torrentview.py:276
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:107
+msgid "Name"
+msgstr ""
+
+#: deluge/ui/common.py:51 deluge/ui/common.py:53
+#: deluge/ui/gtk3/files_tab.py:126 deluge/ui/gtk3/torrentview.py:310
+#: deluge/ui/gtk3/peers_tab.py:120
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:121
+#: deluge/ui/web/js/deluge-all/details/PeersTab.js:80
+#: deluge/ui/web/js/deluge-all/details/FilesTab.js:34
+msgid "Progress"
+msgstr ""
+
+#: deluge/ui/common.py:52 deluge/ui/web/js/deluge-all/Sidebar.js:12
+msgid "State"
+msgstr ""
+
+#: deluge/ui/common.py:54 deluge/ui/gtk3/createtorrentdialog.py:72
+#: 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
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:114
+#: deluge/ui/web/js/deluge-all/details/FilesTab.js:23
+#: deluge/ui/web/js/deluge-all/add/FilesTab.js:33
+msgid "Size"
+msgstr ""
+
+#: deluge/ui/common.py:55 deluge/ui/gtk3/torrentview.py:289
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:244
+msgid "Downloaded"
+msgstr ""
+
+#: deluge/ui/common.py:56 deluge/ui/gtk3/torrentview.py:296
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:252
+msgid "Uploaded"
+msgstr ""
+
+#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
+msgid "Remaining"
+msgstr ""
+
+#: deluge/ui/common.py:58 deluge/ui/gtk3/torrentview.py:373
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:165
+msgid "Ratio"
+msgstr ""
+
+#: deluge/ui/common.py:59 deluge/ui/gtk3/torrentview.py:340
+#: deluge/ui/gtk3/peers_tab.py:133
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:144
+#: deluge/ui/web/js/deluge-all/details/PeersTab.js:87
+msgid "Down Speed"
+msgstr ""
+
+#: deluge/ui/common.py:60 deluge/ui/gtk3/torrentview.py:346
+#: deluge/ui/gtk3/peers_tab.py:146
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:151
+#: deluge/ui/web/js/deluge-all/details/PeersTab.js:94
+msgid "Up Speed"
+msgstr ""
+
+#: deluge/ui/common.py:61 deluge/ui/gtk3/torrentview.py:352
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:268
+msgid "Down Limit"
+msgstr ""
+
+#: deluge/ui/common.py:62 deluge/ui/gtk3/torrentview.py:359
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:276
+msgid "Up Limit"
+msgstr ""
+
+#: deluge/ui/common.py:63 deluge/ui/web/js/deluge-all/add/OptionsTab.js:101
+msgid "Max Connections"
+msgstr ""
+
+#: deluge/ui/common.py:64 deluge/ui/web/js/deluge-all/add/OptionsTab.js:109
+msgid "Max Upload Slots"
+msgstr ""
+
+#: deluge/ui/common.py:65 deluge/ui/gtk3/torrentview.py:325
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:136
+#: deluge/ui/web/js/deluge-all/details/PeersTab.js:46
+msgid "Peers"
+msgstr ""
+
+#: deluge/ui/common.py:66 deluge/ui/gtk3/torrentview.py:317
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:128
+msgid "Seeds"
+msgstr ""
+
+#: deluge/ui/common.py:67 deluge/ui/gtk3/torrentview.py:380
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:173
+msgid "Avail"
+msgstr ""
+
+#: deluge/ui/common.py:68 deluge/ui/gtk3/torrentview.py:333
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:284
+msgid "Seeds:Peers"
+msgstr ""
+
+#: deluge/ui/common.py:69 deluge/ui/gtk3/listview.py:203
+#: deluge/ui/gtk3/torrentview.py:387
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:181
+msgid "Added"
+msgstr ""
+
+#: deluge/ui/common.py:70 deluge/ui/gtk3/createtorrentdialog.py:88
+#: deluge/ui/gtk3/edittrackersdialog.py:127 deluge/ui/gtk3/torrentview.py:408
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:57
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:205
+msgid "Tracker"
+msgstr ""
+
+#: deluge/ui/common.py:72 deluge/ui/gtk3/torrentview.py:414
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:10
+#: deluge/ui/web/js/deluge-all/MoveStorage.js:48
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:213
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:31
+msgid "Download Folder"
+msgstr ""
+
+#: deluge/ui/common.py:75
+msgid "Seeding Time"
+msgstr ""
+
+#: deluge/ui/common.py:76
+msgid "Active Time"
+msgstr ""
+
+#: deluge/ui/common.py:78
+msgid "Last Activity"
+msgstr ""
+
+#: deluge/ui/common.py:81
+msgid "Finished Time"
+msgstr ""
+
+#: deluge/ui/common.py:83 deluge/ui/gtk3/torrentview.py:401
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:189
+msgid "Complete Seen"
+msgstr ""
+
+#: deluge/ui/common.py:86 deluge/ui/gtk3/torrentview.py:394
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:197
+msgid "Completed"
+msgstr ""
+
+#: deluge/ui/common.py:87 deluge/ui/gtk3/torrentview.py:366
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:158
+msgid "ETA"
+msgstr ""
+
+#: deluge/ui/common.py:88 deluge/ui/gtk3/torrentview.py:418
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:236
+msgid "Shared"
+msgstr ""
+
+#: deluge/ui/common.py:90 deluge/ui/gtk3/glade/main_window.tabs.ui.h:31
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:287
+msgid "Prioritize First/Last"
+msgstr ""
+
+#: deluge/ui/common.py:94 deluge/ui/gtk3/glade/main_window.tabs.ui.h:32
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:14
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:143
+msgid "Sequential Download"
+msgstr ""
+
+#: deluge/ui/common.py:97 deluge/ui/common.py:98
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:35
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:11
+#: deluge/ui/web/js/deluge-all/Menus.js:253
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:195
+msgid "Auto Managed"
+msgstr ""
+
+#: deluge/ui/common.py:99
+msgid "Stop At Ratio"
+msgstr ""
+
+#: deluge/ui/common.py:100
+msgid "Stop Ratio"
+msgstr ""
+
+#: deluge/ui/common.py:101
+msgid "Remove At Ratio"
+msgstr ""
+
+#: deluge/ui/common.py:102 deluge/ui/common.py:108
+msgid "Move On Completed"
+msgstr ""
+
+#: deluge/ui/common.py:104
+msgid "Move Completed Path"
+msgstr ""
+
+#: deluge/ui/common.py:112
+msgid "Move On Completed Path"
+msgstr ""
+
+#: 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
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:221
+msgid "Owner"
+msgstr ""
+
+#: deluge/ui/common.py:116
+msgid "Pieces"
+msgstr ""
+
+#: deluge/ui/common.py:117
+msgid "Seed Rank"
+msgstr ""
+
+#: deluge/ui/common.py:118 deluge/ui/gtk3/glade/main_window.tabs.ui.h:33
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:22
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:294
+msgid "Super Seeding"
+msgstr ""
+
+#: deluge/ui/common.py:123 deluge/ui/web/js/deluge-all/details/StatusTab.js:122
+msgid "Warning"
+msgstr ""
+
+#: deluge/ui/common.py:124 deluge/ui/web/js/deluge-all/details/StatusTab.js:123
+msgid "Announce OK"
+msgstr ""
+
+#: deluge/ui/common.py:125 deluge/ui/web/js/deluge-all/details/StatusTab.js:124
+msgid "Announce Sent"
+msgstr ""
+
+#: deluge/ui/common.py:129
+#: deluge/ui/console/modes/preferences/preference_panes.py:174
+#: deluge/ui/console/modes/preferences/preferences.py:89
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:18
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:31
+msgid "Interface"
+msgstr ""
+
+#: deluge/ui/common.py:130
+#: deluge/ui/console/modes/preferences/preference_panes.py:230
+#: deluge/ui/console/modes/preferences/preferences.py:90
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:21
+msgid "Downloads"
+msgstr ""
+
+#: deluge/ui/common.py:131 deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:33
+#: deluge/ui/console/modes/preferences/preference_panes.py:409
+#: deluge/ui/console/modes/preferences/preferences.py:92
+#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:3
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:68
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:21
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:73
+msgid "Bandwidth"
+msgstr ""
+
+#: deluge/ui/common.py:132
+#: deluge/ui/console/modes/preferences/preference_panes.py:550
+#: deluge/ui/console/modes/preferences/preferences.py:95
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:15
+#: deluge/ui/web/js/deluge-all/Menus.js:277
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:176
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:18
+msgid "Queue"
+msgstr ""
+
+#: deluge/ui/common.py:133
+#: deluge/ui/console/modes/preferences/preference_panes.py:300
+#: deluge/ui/console/modes/preferences/preferences.py:91
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:28
+msgid "Network"
+msgstr ""
+
+#: deluge/ui/common.py:134 deluge/ui/gtk3/glade/preferences_dialog.ui.h:146
+#: deluge/ui/console/modes/preferences/preference_panes.py:640
+#: deluge/ui/console/modes/preferences/preference_panes.py:647
+#: deluge/ui/console/modes/preferences/preferences.py:96
+#: deluge/ui/web/js/deluge-all/preferences/ProxyPage.js:21
+#: deluge/ui/web/js/deluge-all/preferences/ProxyPage.js:35
+msgid "Proxy"
+msgstr ""
+
+#: deluge/ui/common.py:135
+#: deluge/ui/console/modes/preferences/preference_panes.py:685
+#: deluge/ui/console/modes/preferences/preferences.py:97
+#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:18
+msgid "Cache"
+msgstr ""
+
+#: deluge/ui/common.py:136 deluge/ui/gtk3/glade/preferences_dialog.ui.h:190
+#: deluge/ui/console/modes/preferences/preference_panes.py:499
+#: deluge/ui/console/modes/preferences/preferences.py:93
+#: deluge/ui/web/js/deluge-all/Statusbar.js:80
+#: deluge/ui/web/js/deluge-all/Statusbar.js:139
+#: deluge/ui/web/js/deluge-all/Statusbar.js:198
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:21
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:69
+msgid "Other"
+msgstr ""
+
+#: deluge/ui/common.py:137
+#: deluge/ui/console/modes/preferences/preference_panes.py:524
+#: deluge/ui/console/modes/preferences/preferences.py:94
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:18
+msgid "Daemon"
+msgstr ""
+
+#: deluge/ui/common.py:138
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:18
+msgid "Plugins"
+msgstr ""
+
+#: deluge/ui/common.py:150 deluge/ui/web/js/deluge-all/Deluge.js:154
+#: deluge/ui/web/js/deluge-all/Menus.js:365
+msgid "Skip"
+msgstr ""
+
+#: deluge/ui/common.py:151 deluge/ui/web/js/deluge-all/Deluge.js:155
+#: deluge/ui/web/js/deluge-all/Menus.js:371
+msgid "Low"
+msgstr ""
+
+#: deluge/ui/common.py:152 deluge/ui/web/js/deluge-all/Deluge.js:156
+#: deluge/ui/web/js/deluge-all/Menus.js:377
+msgid "Normal"
+msgstr ""
+
+#: deluge/ui/common.py:153 deluge/ui/web/js/deluge-all/Deluge.js:157
+#: deluge/ui/web/js/deluge-all/Menus.js:383
+msgid "High"
+msgstr ""
+
+#: deluge/ui/client.py:681
+msgid ""
+"Deluge cannot find the `deluged` executable, check that the deluged package "
+"is installed, or added to your PATH."
+msgstr ""
+
+#: deluge/ui/countries.py:10
+msgid "Afghanistan"
+msgstr ""
+
+#: deluge/ui/countries.py:11
+msgid "Aland Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:12
+msgid "Albania"
+msgstr ""
+
+#: deluge/ui/countries.py:13
+msgid "Algeria"
+msgstr ""
+
+#: deluge/ui/countries.py:14
+msgid "American Samoa"
+msgstr ""
+
+#: deluge/ui/countries.py:15
+msgid "Andorra"
+msgstr ""
+
+#: deluge/ui/countries.py:16
+msgid "Angola"
+msgstr ""
+
+#: deluge/ui/countries.py:17
+msgid "Anguilla"
+msgstr ""
+
+#: deluge/ui/countries.py:18
+msgid "Antarctica"
+msgstr ""
+
+#: deluge/ui/countries.py:19
+msgid "Antigua and Barbuda"
+msgstr ""
+
+#: deluge/ui/countries.py:20
+msgid "Argentina"
+msgstr ""
+
+#: deluge/ui/countries.py:21
+msgid "Armenia"
+msgstr ""
+
+#: deluge/ui/countries.py:22
+msgid "Aruba"
+msgstr ""
+
+#: deluge/ui/countries.py:23
+msgid "Australia"
+msgstr ""
+
+#: deluge/ui/countries.py:24
+msgid "Austria"
+msgstr ""
+
+#: deluge/ui/countries.py:25
+msgid "Azerbaijan"
+msgstr ""
+
+#: deluge/ui/countries.py:26
+msgid "Bahamas"
+msgstr ""
+
+#: deluge/ui/countries.py:27
+msgid "Bahrain"
+msgstr ""
+
+#: deluge/ui/countries.py:28
+msgid "Bangladesh"
+msgstr ""
+
+#: deluge/ui/countries.py:29
+msgid "Barbados"
+msgstr ""
+
+#: deluge/ui/countries.py:30
+msgid "Belarus"
+msgstr ""
+
+#: deluge/ui/countries.py:31
+msgid "Belgium"
+msgstr ""
+
+#: deluge/ui/countries.py:32
+msgid "Belize"
+msgstr ""
+
+#: deluge/ui/countries.py:33
+msgid "Benin"
+msgstr ""
+
+#: deluge/ui/countries.py:34
+msgid "Bermuda"
+msgstr ""
+
+#: deluge/ui/countries.py:35
+msgid "Bhutan"
+msgstr ""
+
+#: deluge/ui/countries.py:36
+msgid "Bolivia"
+msgstr ""
+
+#: deluge/ui/countries.py:37
+msgid "Bosnia and Herzegovina"
+msgstr ""
+
+#: deluge/ui/countries.py:38
+msgid "Botswana"
+msgstr ""
+
+#: deluge/ui/countries.py:39
+msgid "Bouvet Island"
+msgstr ""
+
+#: deluge/ui/countries.py:40
+msgid "Brazil"
+msgstr ""
+
+#: deluge/ui/countries.py:41
+msgid "British Indian Ocean Territory"
+msgstr ""
+
+#: deluge/ui/countries.py:42
+msgid "Brunei Darussalam"
+msgstr ""
+
+#: deluge/ui/countries.py:43
+msgid "Bulgaria"
+msgstr ""
+
+#: deluge/ui/countries.py:44
+msgid "Burkina Faso"
+msgstr ""
+
+#: deluge/ui/countries.py:45
+msgid "Burundi"
+msgstr ""
+
+#: deluge/ui/countries.py:46
+msgid "Cambodia"
+msgstr ""
+
+#: deluge/ui/countries.py:47
+msgid "Cameroon"
+msgstr ""
+
+#: deluge/ui/countries.py:48
+msgid "Canada"
+msgstr ""
+
+#: deluge/ui/countries.py:49
+msgid "Cape Verde"
+msgstr ""
+
+#: deluge/ui/countries.py:50
+msgid "Cayman Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:51
+msgid "Central African Republic"
+msgstr ""
+
+#: deluge/ui/countries.py:52
+msgid "Chad"
+msgstr ""
+
+#: deluge/ui/countries.py:53
+msgid "Chile"
+msgstr ""
+
+#: deluge/ui/countries.py:54
+msgid "China"
+msgstr ""
+
+#: deluge/ui/countries.py:55
+msgid "Christmas Island"
+msgstr ""
+
+#: deluge/ui/countries.py:56
+msgid "Cocos (Keeling) Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:57
+msgid "Colombia"
+msgstr ""
+
+#: deluge/ui/countries.py:58
+msgid "Comoros"
+msgstr ""
+
+#: deluge/ui/countries.py:59
+msgid "Congo"
+msgstr ""
+
+#: deluge/ui/countries.py:60
+msgid "Congo, The Democratic Republic of the"
+msgstr ""
+
+#: deluge/ui/countries.py:61
+msgid "Cook Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:62
+msgid "Costa Rica"
+msgstr ""
+
+#: deluge/ui/countries.py:63
+msgid "Cote d'Ivoire"
+msgstr ""
+
+#: deluge/ui/countries.py:64
+msgid "Croatia"
+msgstr ""
+
+#: deluge/ui/countries.py:65
+msgid "Cuba"
+msgstr ""
+
+#: deluge/ui/countries.py:66
+msgid "Cyprus"
+msgstr ""
+
+#: deluge/ui/countries.py:67
+msgid "Czech Republic"
+msgstr ""
+
+#: deluge/ui/countries.py:68
+msgid "Denmark"
+msgstr ""
+
+#: deluge/ui/countries.py:69
+msgid "Djibouti"
+msgstr ""
+
+#: deluge/ui/countries.py:70
+msgid "Dominica"
+msgstr ""
+
+#: deluge/ui/countries.py:71
+msgid "Dominican Republic"
+msgstr ""
+
+#: deluge/ui/countries.py:72
+msgid "Ecuador"
+msgstr ""
+
+#: deluge/ui/countries.py:73
+msgid "Egypt"
+msgstr ""
+
+#: deluge/ui/countries.py:74
+msgid "El Salvador"
+msgstr ""
+
+#: deluge/ui/countries.py:75
+msgid "Equatorial Guinea"
+msgstr ""
+
+#: deluge/ui/countries.py:76
+msgid "Eritrea"
+msgstr ""
+
+#: deluge/ui/countries.py:77
+msgid "Estonia"
+msgstr ""
+
+#: deluge/ui/countries.py:78
+msgid "Ethiopia"
+msgstr ""
+
+#: deluge/ui/countries.py:79
+msgid "Falkland Islands (Malvinas)"
+msgstr ""
+
+#: deluge/ui/countries.py:80
+msgid "Faroe Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:81
+msgid "Fiji"
+msgstr ""
+
+#: deluge/ui/countries.py:82
+msgid "Finland"
+msgstr ""
+
+#: deluge/ui/countries.py:83
+msgid "France"
+msgstr ""
+
+#: deluge/ui/countries.py:84
+msgid "French Guiana"
+msgstr ""
+
+#: deluge/ui/countries.py:85
+msgid "French Polynesia"
+msgstr ""
+
+#: deluge/ui/countries.py:86
+msgid "French Southern Territories"
+msgstr ""
+
+#: deluge/ui/countries.py:87
+msgid "Gabon"
+msgstr ""
+
+#: deluge/ui/countries.py:88
+msgid "Gambia"
+msgstr ""
+
+#: deluge/ui/countries.py:89
+msgid "Georgia"
+msgstr ""
+
+#: deluge/ui/countries.py:90
+msgid "Germany"
+msgstr ""
+
+#: deluge/ui/countries.py:91
+msgid "Ghana"
+msgstr ""
+
+#: deluge/ui/countries.py:92
+msgid "Gibraltar"
+msgstr ""
+
+#: deluge/ui/countries.py:93
+msgid "Greece"
+msgstr ""
+
+#: deluge/ui/countries.py:94
+msgid "Greenland"
+msgstr ""
+
+#: deluge/ui/countries.py:95
+msgid "Grenada"
+msgstr ""
+
+#: deluge/ui/countries.py:96
+msgid "Guadeloupe"
+msgstr ""
+
+#: deluge/ui/countries.py:97
+msgid "Guam"
+msgstr ""
+
+#: deluge/ui/countries.py:98
+msgid "Guatemala"
+msgstr ""
+
+#: deluge/ui/countries.py:99
+msgid "Guernsey"
+msgstr ""
+
+#: deluge/ui/countries.py:100
+msgid "Guinea"
+msgstr ""
+
+#: deluge/ui/countries.py:101
+msgid "Guinea-Bissau"
+msgstr ""
+
+#: deluge/ui/countries.py:102
+msgid "Guyana"
+msgstr ""
+
+#: deluge/ui/countries.py:103
+msgid "Haiti"
+msgstr ""
+
+#: deluge/ui/countries.py:104
+msgid "Heard Island and McDonald Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:105
+msgid "Holy See (Vatican City State)"
+msgstr ""
+
+#: deluge/ui/countries.py:106
+msgid "Honduras"
+msgstr ""
+
+#: deluge/ui/countries.py:107
+msgid "Hong Kong"
+msgstr ""
+
+#: deluge/ui/countries.py:108
+msgid "Hungary"
+msgstr ""
+
+#: deluge/ui/countries.py:109
+msgid "Iceland"
+msgstr ""
+
+#: deluge/ui/countries.py:110
+msgid "India"
+msgstr ""
+
+#: deluge/ui/countries.py:111
+msgid "Indonesia"
+msgstr ""
+
+#: deluge/ui/countries.py:112
+msgid "Iran, Islamic Republic of"
+msgstr ""
+
+#: deluge/ui/countries.py:113
+msgid "Iraq"
+msgstr ""
+
+#: deluge/ui/countries.py:114
+msgid "Ireland"
+msgstr ""
+
+#: deluge/ui/countries.py:115
+msgid "Isle of Man"
+msgstr ""
+
+#: deluge/ui/countries.py:116
+msgid "Israel"
+msgstr ""
+
+#: deluge/ui/countries.py:117
+msgid "Italy"
+msgstr ""
+
+#: deluge/ui/countries.py:118
+msgid "Jamaica"
+msgstr ""
+
+#: deluge/ui/countries.py:119
+msgid "Japan"
+msgstr ""
+
+#: deluge/ui/countries.py:120
+msgid "Jersey"
+msgstr ""
+
+#: deluge/ui/countries.py:121
+msgid "Jordan"
+msgstr ""
+
+#: deluge/ui/countries.py:122
+msgid "Kazakhstan"
+msgstr ""
+
+#: deluge/ui/countries.py:123
+msgid "Kenya"
+msgstr ""
+
+#: deluge/ui/countries.py:124
+msgid "Kiribati"
+msgstr ""
+
+#: deluge/ui/countries.py:125
+msgid "Korea, Democratic People's Republic of"
+msgstr ""
+
+#: deluge/ui/countries.py:126
+msgid "Korea, Republic of"
+msgstr ""
+
+#: deluge/ui/countries.py:127
+msgid "Kuwait"
+msgstr ""
+
+#: deluge/ui/countries.py:128
+msgid "Kyrgyzstan"
+msgstr ""
+
+#: deluge/ui/countries.py:129
+msgid "Lao People's Democratic Republic"
+msgstr ""
+
+#: deluge/ui/countries.py:130
+msgid "Latvia"
+msgstr ""
+
+#: deluge/ui/countries.py:131
+msgid "Lebanon"
+msgstr ""
+
+#: deluge/ui/countries.py:132
+msgid "Lesotho"
+msgstr ""
+
+#: deluge/ui/countries.py:133
+msgid "Liberia"
+msgstr ""
+
+#: deluge/ui/countries.py:134
+msgid "Libyan Arab Jamahiriya"
+msgstr ""
+
+#: deluge/ui/countries.py:135
+msgid "Liechtenstein"
+msgstr ""
+
+#: deluge/ui/countries.py:136
+msgid "Lithuania"
+msgstr ""
+
+#: deluge/ui/countries.py:137
+msgid "Luxembourg"
+msgstr ""
+
+#: deluge/ui/countries.py:138
+msgid "Macao"
+msgstr ""
+
+#: deluge/ui/countries.py:139
+msgid "Macedonia, The Former Yugoslav Republic of"
+msgstr ""
+
+#: deluge/ui/countries.py:140
+msgid "Madagascar"
+msgstr ""
+
+#: deluge/ui/countries.py:141
+msgid "Malawi"
+msgstr ""
+
+#: deluge/ui/countries.py:142
+msgid "Malaysia"
+msgstr ""
+
+#: deluge/ui/countries.py:143
+msgid "Maldives"
+msgstr ""
+
+#: deluge/ui/countries.py:144
+msgid "Mali"
+msgstr ""
+
+#: deluge/ui/countries.py:145
+msgid "Malta"
+msgstr ""
+
+#: deluge/ui/countries.py:146
+msgid "Marshall Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:147
+msgid "Martinique"
+msgstr ""
+
+#: deluge/ui/countries.py:148
+msgid "Mauritania"
+msgstr ""
+
+#: deluge/ui/countries.py:149
+msgid "Mauritius"
+msgstr ""
+
+#: deluge/ui/countries.py:150
+msgid "Mayotte"
+msgstr ""
+
+#: deluge/ui/countries.py:151
+msgid "Mexico"
+msgstr ""
+
+#: deluge/ui/countries.py:152
+msgid "Micronesia, Federated States of"
+msgstr ""
+
+#: deluge/ui/countries.py:153
+msgid "Moldova"
+msgstr ""
+
+#: deluge/ui/countries.py:154
+msgid "Monaco"
+msgstr ""
+
+#: deluge/ui/countries.py:155
+msgid "Mongolia"
+msgstr ""
+
+#: deluge/ui/countries.py:156
+msgid "Montenegro"
+msgstr ""
+
+#: deluge/ui/countries.py:157
+msgid "Montserrat"
+msgstr ""
+
+#: deluge/ui/countries.py:158
+msgid "Morocco"
+msgstr ""
+
+#: deluge/ui/countries.py:159
+msgid "Mozambique"
+msgstr ""
+
+#: deluge/ui/countries.py:160
+msgid "Myanmar"
+msgstr ""
+
+#: deluge/ui/countries.py:161
+msgid "Namibia"
+msgstr ""
+
+#: deluge/ui/countries.py:162
+msgid "Nauru"
+msgstr ""
+
+#: deluge/ui/countries.py:163
+msgid "Nepal"
+msgstr ""
+
+#: deluge/ui/countries.py:164
+msgid "Netherlands"
+msgstr ""
+
+#: deluge/ui/countries.py:165
+msgid "Netherlands Antilles"
+msgstr ""
+
+#: deluge/ui/countries.py:166
+msgid "New Caledonia"
+msgstr ""
+
+#: deluge/ui/countries.py:167
+msgid "New Zealand"
+msgstr ""
+
+#: deluge/ui/countries.py:168
+msgid "Nicaragua"
+msgstr ""
+
+#: deluge/ui/countries.py:169
+msgid "Niger"
+msgstr ""
+
+#: deluge/ui/countries.py:170
+msgid "Nigeria"
+msgstr ""
+
+#: deluge/ui/countries.py:171
+msgid "Niue"
+msgstr ""
+
+#: deluge/ui/countries.py:172
+msgid "Norfolk Island"
+msgstr ""
+
+#: deluge/ui/countries.py:173
+msgid "Northern Mariana Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:174
+msgid "Norway"
+msgstr ""
+
+#: deluge/ui/countries.py:175
+msgid "Oman"
+msgstr ""
+
+#: deluge/ui/countries.py:176
+msgid "Pakistan"
+msgstr ""
+
+#: deluge/ui/countries.py:177
+msgid "Palau"
+msgstr ""
+
+#: deluge/ui/countries.py:178
+msgid "Palestinian Territory, Occupied"
+msgstr ""
+
+#: deluge/ui/countries.py:179
+msgid "Panama"
+msgstr ""
+
+#: deluge/ui/countries.py:180
+msgid "Papua New Guinea"
+msgstr ""
+
+#: deluge/ui/countries.py:181
+msgid "Paraguay"
+msgstr ""
+
+#: deluge/ui/countries.py:182
+msgid "Peru"
+msgstr ""
+
+#: deluge/ui/countries.py:183
+msgid "Philippines"
+msgstr ""
+
+#: deluge/ui/countries.py:184
+msgid "Pitcairn"
+msgstr ""
+
+#: deluge/ui/countries.py:185
+msgid "Poland"
+msgstr ""
+
+#: deluge/ui/countries.py:186
+msgid "Portugal"
+msgstr ""
+
+#: deluge/ui/countries.py:187
+msgid "Puerto Rico"
+msgstr ""
+
+#: deluge/ui/countries.py:188
+msgid "Qatar"
+msgstr ""
+
+#: deluge/ui/countries.py:189
+msgid "Reunion"
+msgstr ""
+
+#: deluge/ui/countries.py:190
+msgid "Romania"
+msgstr ""
+
+#: deluge/ui/countries.py:191
+msgid "Russian Federation"
+msgstr ""
+
+#: deluge/ui/countries.py:192
+msgid "Rwanda"
+msgstr ""
+
+#: deluge/ui/countries.py:193
+msgid "Saint Barthelemy"
+msgstr ""
+
+#: deluge/ui/countries.py:194
+msgid "Saint Helena"
+msgstr ""
+
+#: deluge/ui/countries.py:195
+msgid "Saint Kitts and Nevis"
+msgstr ""
+
+#: deluge/ui/countries.py:196
+msgid "Saint Lucia"
+msgstr ""
+
+#: deluge/ui/countries.py:197
+msgid "Saint Martin"
+msgstr ""
+
+#: deluge/ui/countries.py:198
+msgid "Saint Pierre and Miquelon"
+msgstr ""
+
+#: deluge/ui/countries.py:199
+msgid "Saint Vincent and the Grenadines"
+msgstr ""
+
+#: deluge/ui/countries.py:200
+msgid "Samoa"
+msgstr ""
+
+#: deluge/ui/countries.py:201
+msgid "San Marino"
+msgstr ""
+
+#: deluge/ui/countries.py:202
+msgid "Sao Tome and Principe"
+msgstr ""
+
+#: deluge/ui/countries.py:203
+msgid "Saudi Arabia"
+msgstr ""
+
+#: deluge/ui/countries.py:204
+msgid "Senegal"
+msgstr ""
+
+#: deluge/ui/countries.py:205
+msgid "Serbia"
+msgstr ""
+
+#: deluge/ui/countries.py:206
+msgid "Seychelles"
+msgstr ""
+
+#: deluge/ui/countries.py:207
+msgid "Sierra Leone"
+msgstr ""
+
+#: deluge/ui/countries.py:208
+msgid "Singapore"
+msgstr ""
+
+#: deluge/ui/countries.py:209
+msgid "Slovakia"
+msgstr ""
+
+#: deluge/ui/countries.py:210
+msgid "Slovenia"
+msgstr ""
+
+#: deluge/ui/countries.py:211
+msgid "Solomon Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:212
+msgid "Somalia"
+msgstr ""
+
+#: deluge/ui/countries.py:213
+msgid "South Africa"
+msgstr ""
+
+#: deluge/ui/countries.py:214
+msgid "South Georgia and the South Sandwich Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:215
+msgid "Spain"
+msgstr ""
+
+#: deluge/ui/countries.py:216
+msgid "Sri Lanka"
+msgstr ""
+
+#: deluge/ui/countries.py:217
+msgid "Sudan"
+msgstr ""
+
+#: deluge/ui/countries.py:218
+msgid "Suriname"
+msgstr ""
+
+#: deluge/ui/countries.py:219
+msgid "Svalbard and Jan Mayen"
+msgstr ""
+
+#: deluge/ui/countries.py:220
+msgid "Swaziland"
+msgstr ""
+
+#: deluge/ui/countries.py:221
+msgid "Sweden"
+msgstr ""
+
+#: deluge/ui/countries.py:222
+msgid "Switzerland"
+msgstr ""
+
+#: deluge/ui/countries.py:223
+msgid "Syrian Arab Republic"
+msgstr ""
+
+#: deluge/ui/countries.py:224
+msgid "Taiwan"
+msgstr ""
+
+#: deluge/ui/countries.py:225
+msgid "Tajikistan"
+msgstr ""
+
+#: deluge/ui/countries.py:226
+msgid "Tanzania, United Republic of"
+msgstr ""
+
+#: deluge/ui/countries.py:227
+msgid "Thailand"
+msgstr ""
+
+#: deluge/ui/countries.py:228
+msgid "Timor-Leste"
+msgstr ""
+
+#: deluge/ui/countries.py:229
+msgid "Togo"
+msgstr ""
+
+#: deluge/ui/countries.py:230
+msgid "Tokelau"
+msgstr ""
+
+#: deluge/ui/countries.py:231
+msgid "Tonga"
+msgstr ""
+
+#: deluge/ui/countries.py:232
+msgid "Trinidad and Tobago"
+msgstr ""
+
+#: deluge/ui/countries.py:233
+msgid "Tunisia"
+msgstr ""
+
+#: deluge/ui/countries.py:234
+msgid "Turkey"
+msgstr ""
+
+#: deluge/ui/countries.py:235
+msgid "Turkmenistan"
+msgstr ""
+
+#: deluge/ui/countries.py:236
+msgid "Turks and Caicos Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:237
+msgid "Tuvalu"
+msgstr ""
+
+#: deluge/ui/countries.py:238
+msgid "Uganda"
+msgstr ""
+
+#: deluge/ui/countries.py:239
+msgid "Ukraine"
+msgstr ""
+
+#: deluge/ui/countries.py:240
+msgid "United Arab Emirates"
+msgstr ""
+
+#: deluge/ui/countries.py:241
+msgid "United Kingdom"
+msgstr ""
+
+#: deluge/ui/countries.py:242
+msgid "United States"
+msgstr ""
+
+#: deluge/ui/countries.py:243
+msgid "United States Minor Outlying Islands"
+msgstr ""
+
+#: deluge/ui/countries.py:244
+msgid "Uruguay"
+msgstr ""
+
+#: deluge/ui/countries.py:245
+msgid "Uzbekistan"
+msgstr ""
+
+#: deluge/ui/countries.py:246
+msgid "Vanuatu"
+msgstr ""
+
+#: deluge/ui/countries.py:247
+msgid "Venezuela"
+msgstr ""
+
+#: deluge/ui/countries.py:248
+msgid "Viet Nam"
+msgstr ""
+
+#: deluge/ui/countries.py:249
+msgid "Virgin Islands, British"
+msgstr ""
+
+#: deluge/ui/countries.py:250
+msgid "Virgin Islands, U.S."
+msgstr ""
+
+#: deluge/ui/countries.py:251
+msgid "Wallis and Futuna"
+msgstr ""
+
+#: deluge/ui/countries.py:252
+msgid "Western Sahara"
+msgstr ""
+
+#: deluge/ui/countries.py:253
+msgid "Yemen"
+msgstr ""
+
+#: deluge/ui/countries.py:254
+msgid "Zambia"
+msgstr ""
+
+#: deluge/ui/countries.py:255
+msgid "Zimbabwe"
+msgstr ""
+
+#: deluge/ui/ui_entry.py:51
+msgid "UI Options"
+msgstr ""
+
+#: deluge/ui/ui_entry.py:57
+msgid "Set the default UI to be run, when no UI is specified"
+msgstr ""
+
+#: deluge/ui/ui_entry.py:91
+msgid ""
+"Alternative UI to launch, with optional ui args \n"
+" (default UI: *)"
+msgstr ""
+
+#: deluge/ui/web/web.py:32
+msgid "Web Server Options"
+msgstr ""
+
+#: deluge/ui/web/web.py:38
+msgid "IP address for web server to listen on"
+msgstr ""
+
+#: deluge/ui/web/web.py:46
+msgid "Port for web server to listen on"
+msgstr ""
+
+#: deluge/ui/web/web.py:53
+msgid "Set the base path that the ui is running on"
+msgstr ""
+
+#: deluge/ui/web/web.py:56
+msgid "Force the web server to use SSL"
+msgstr ""
+
+#: deluge/ui/web/web.py:61
+msgid "Force the web server to disable SSL"
+msgstr ""
+
+#: deluge/ui/web/json_api.py:868
+msgid "Daemon does not exist"
+msgstr ""
+
+#: deluge/ui/web/json_api.py:875
+msgid "Daemon not running"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:62
+#: 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"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:85
+#: deluge/ui/gtk3/edittrackersdialog.py:124
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:52
+msgid "Tier"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:128
+msgid "Choose a file"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:132
+#: deluge/ui/gtk3/createtorrentdialog.py:169
+#: deluge/ui/gtk3/createtorrentdialog.py:258
+#: 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
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:15
+#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:2
+#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:2
+#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:2
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:2
+#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:2
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:2
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:2
+#: deluge/ui/gtk3/glade/edit_trackers.edit.ui.h:2
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:2
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:29
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:2
+#: deluge/ui/gtk3/glade/other_dialog.ui.h:1
+#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:2
+#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:2
+msgid "_Cancel"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:134
+#: deluge/ui/gtk3/createtorrentdialog.py:171
+#: deluge/ui/gtk3/addtorrentdialog.py:700 deluge/ui/gtk3/preferences.py:1160
+msgid "_Open"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:165
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:28
+msgid "Choose a folder"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:254
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:4
+msgid "Save .torrent file"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:260
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:3
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:30
+msgid "_Save"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:271
+#: deluge/ui/gtk3/addtorrentdialog.py:712
+msgid "Torrent files"
+msgstr ""
+
+#: deluge/ui/gtk3/createtorrentdialog.py:275
+#: deluge/ui/gtk3/addtorrentdialog.py:716
+msgid "All files"
+msgstr ""
+
+#: deluge/ui/gtk3/mainwindow.py:192
+msgid "Enter your password to show Deluge..."
+msgstr ""
+
+#: deluge/ui/gtk3/mainwindow.py:251
+msgid "Enter your password to Quit Deluge..."
+msgstr ""
+
+#: deluge/ui/gtk3/mainwindow.py:343
+#, python-brace-format
+msgid "D: {download_rate} U: {upload_rate} - Deluge"
+msgstr ""
+
+#: 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
+#: deluge/ui/web/js/deluge-all/Toolbar.js:23
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:91
+msgid "Deluge"
+msgstr ""
+
+#: 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 ""
+
+#: deluge/ui/gtk3/aboutdialog.py:40
+#, python-format
+msgid "Copyright %(year_start)s-%(year_end)s Deluge Team"
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:44
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:52
+msgid ""
+"A peer-to-peer file sharing program\n"
+"utilizing the BitTorrent protocol."
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:46
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:55
+msgid "Client:"
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:52
+msgid "Current Developers:"
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:61
+msgid "Past Developers or Contributors:"
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:795
+msgid ""
+"This program is free software; you can redistribute it and/or modify it "
+"under the terms of the GNU General Public License as published by the Free "
+"Software Foundation; either version 3 of the License, or (at your option) "
+"any later version. \n"
+"\n"
+"This program is distributed in the hope that it will be useful, but WITHOUT "
+"ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or "
+"FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for "
+"more details. \n"
+"\n"
+"You should have received a copy of the GNU General Public License along with "
+"this program; if not, see <http://www.gnu.org/licenses>. \n"
+"\n"
+"In addition, as a special exception, the copyright holders give permission "
+"to link the code of portions of this program with the OpenSSL library. You "
+"must obey the GNU General Public License in all respects for all of the code "
+"used other than OpenSSL. \n"
+"\n"
+"If you modify file(s) with this exception, you may extend this exception to "
+"your version of the file(s), but you are not obligated to do so. If you do "
+"not wish to do so, delete this exception statement from your version. If you "
+"delete this exception statement from all source files in the program, then "
+"also delete it here."
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:829
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:65
+msgid "Server:"
+msgstr ""
+
+#: deluge/ui/gtk3/aboutdialog.py:833
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:41
+msgid "libtorrent:"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:102 deluge/ui/gtk3/queuedtorrents.py:51
+msgid "Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:232
+#, python-format
+msgid "Add Torrents (%d)"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:238
+msgid "Duplicate torrent(s)"
+msgstr ""
+
+#: 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:255
+msgid "Invalid File"
+msgstr ""
+
+#: 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:296
+msgid "Unable to download files for this magnet"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:694
+msgid "Choose a .torrent file"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:777
+msgid "Invalid URL"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:778
+msgid "is not a valid URL."
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:784
+msgid "Downloading..."
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:819
+msgid "Download Failed"
+msgstr ""
+
+#: deluge/ui/gtk3/addtorrentdialog.py:820
+msgid "Failed to download:"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:110
+msgid "_No"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:110
+msgid "_Yes"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:132 deluge/ui/gtk3/dialogs.py:156
+#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:2
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:2
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:2
+msgid "_Close"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:179
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:195
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:38
+msgid "Details:"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:200
+msgid "Authenticate"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:203 deluge/ui/gtk3/connectionmanager.py:211
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:3
+msgid "C_onnect"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:209 deluge/ui/gtk3/dialogs.py:281
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:139
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:6
+#: deluge/ui/console/modes/connectionmanager.py:123
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:7
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:65
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:65
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:74
+msgid "Username:"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:217 deluge/ui/gtk3/dialogs.py:310
+#: deluge/ui/gtk3/dialogs.py:437
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:43
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:7
+#: deluge/ui/console/modes/connectionmanager.py:124
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:8
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:72
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:72
+#: deluge/ui/web/js/deluge-all/LoginWindow.js:47
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:83
+msgid "Password:"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:257
+msgid "Edit Account"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:258
+msgid "Edit existing account"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:263 deluge/ui/gtk3/dialogs.py:364
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:16
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:38
+msgid "_Apply"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:270
+msgid "New Account"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:271
+msgid "Create a new account"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:273 deluge/ui/gtk3/glade/queuedtorrents.ui.h:3
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:191
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:3
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:5
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:20
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:4
+msgid "_Add"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:289
+msgid "Authentication Level:"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:423
+msgid "Password Protected"
+msgstr ""
+
+#: deluge/ui/gtk3/dialogs.py:429
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:17
+#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:3
+#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:3
+#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:3
+#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:3
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:3
+#: deluge/ui/gtk3/glade/edit_trackers.edit.ui.h:3
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:3
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:3
+#: deluge/ui/gtk3/glade/other_dialog.ui.h:2
+#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:3
+#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:3
+msgid "_OK"
+msgstr ""
+
+#: deluge/ui/gtk3/common.py:155 deluge/ui/gtk3/menubar.py:83
+msgid "Other..."
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:155 deluge/ui/web/js/deluge-all/Statusbar.js:18
+msgid "Not Connected"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:175
+msgid "Connections (Limit)"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:182
+msgid "Download Speed (Limit)"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:189
+msgid "Upload Speed (Limit)"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:196
+msgid "Protocol Traffic (Down:Up)"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:201 deluge/ui/web/js/deluge-all/Statusbar.js:234
+msgid "DHT Nodes"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:207
+msgid "Free Disk Space"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:212 deluge/ui/web/js/deluge-all/Statusbar.js:226
+msgid "External IP Address"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:409
+#, python-format
+msgid "<b>IP</b> <small>%s</small>"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:408
+#: deluge/ui/console/widgets/statusbars.py:121
+#: deluge/ui/web/js/deluge-all/Statusbar.js:358
+msgid "n/a"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:220
+msgid "<b><small>Port Issue</small></b>"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:222
+msgid "No incoming connections, check port forwarding"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:475 deluge/ui/gtk3/systemtray.py:394
+#: deluge/ui/gtk3/menubar.py:447
+msgid "Download Speed Limit"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:476 deluge/ui/gtk3/systemtray.py:395
+#: deluge/ui/gtk3/menubar.py:448
+msgid "Set the maximum download speed"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:482 deluge/ui/gtk3/systemtray.py:409
+#: deluge/ui/gtk3/menubar.py:453
+msgid "Upload Speed Limit"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:483 deluge/ui/gtk3/systemtray.py:410
+#: deluge/ui/gtk3/menubar.py:454
+msgid "Set the maximum upload speed"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:489 deluge/ui/gtk3/menubar.py:459
+msgid "Incoming Connections"
+msgstr ""
+
+#: deluge/ui/gtk3/statusbar.py:490 deluge/ui/gtk3/menubar.py:460
+msgid "Set the maximum incoming connections"
+msgstr ""
+
+#: deluge/ui/gtk3/tab_data_funcs.py:28
+#, python-brace-format
+msgid "{state} {percent}%"
+msgstr ""
+
+#: deluge/ui/gtk3/tab_data_funcs.py:30
+#, python-brace-format
+msgid "{state}: {err_msg}"
+msgstr ""
+
+#: deluge/ui/gtk3/tab_data_funcs.py:42
+#: deluge/ui/gtk3/torrentview_data_funcs.py:284
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:74
+msgid "Never"
+msgstr ""
+
+#: deluge/ui/gtk3/tab_data_funcs.py:96
+msgid "Yes"
+msgstr ""
+
+#: deluge/ui/gtk3/tab_data_funcs.py:96
+msgid "No"
+msgstr ""
+
+#: deluge/ui/gtk3/files_tab.py:140
+#: deluge/ui/web/js/deluge-all/details/FilesTab.js:48
+msgid "Priority"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:142
+msgid "_All"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:143
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:15
+msgid "_Status"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:144
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:26
+msgid "_Details"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:145
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:27
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:9
+msgid "Fi_les"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:146
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:28
+msgid "_Peers"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:147
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:45
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:36
+msgid "_Options"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentdetails.py:148
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:52
+msgid "_Trackers"
+msgstr ""
+
+#: deluge/ui/gtk3/systemtray.py:184
+msgid "Not Connected..."
+msgstr ""
+
+#: deluge/ui/gtk3/systemtray.py:235 deluge/ui/gtk3/systemtray.py:239
+#: deluge/ui/web/js/deluge-all/Statusbar.js:73
+#: deluge/ui/web/js/deluge-all/Statusbar.js:132
+#: deluge/ui/web/js/deluge-all/Statusbar.js:191
+#: deluge/ui/web/js/deluge-all/Menus.js:109
+#: deluge/ui/web/js/deluge-all/Menus.js:154
+#: deluge/ui/web/js/deluge-all/Menus.js:199
+#: deluge/ui/web/js/deluge-all/Menus.js:244
+msgid "Unlimited"
+msgstr ""
+
+#: deluge/ui/gtk3/systemtray.py:245
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:39
+#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:3
+#: deluge/ui/web/js/deluge-all/Toolbar.js:77
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:85
+#: deluge/ui/web/js/deluge-all/Menus.js:298
+msgid "Down"
+msgstr ""
+
+#: deluge/ui/gtk3/systemtray.py:248
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:37
+#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:2
+#: deluge/ui/web/js/deluge-all/FileBrowser.js:33
+#: deluge/ui/web/js/deluge-all/Toolbar.js:70
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:79
+#: deluge/ui/web/js/deluge-all/Menus.js:291
+msgid "Up"
+msgstr ""
+
+#: 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: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: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:349
+msgid "Continue in Thin Client mode?"
+msgstr ""
+
+#: deluge/ui/gtk3/gtkui.py:350
+msgid "Change User Interface Mode"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:52
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:56
+msgid "Offline"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:53
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:53
+msgid "Online"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:54
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:59
+msgid "Connected"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:110
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:176
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:48
+#: deluge/ui/web/js/deluge-all/details/StatusTab.js:17
+msgid "Status"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:115
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:66
+msgid "Host"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:122
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:73
+msgid "Version"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:219
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:8
+msgid "_Start Daemon"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:250
+msgid "_Stop Daemon"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:255
+msgid "_Disconnect"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:280
+msgid "Unable to start daemon!"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:281
+msgid "Check deluged package is installed and logs for further details"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:332
+msgid "Incompatible Client"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:343
+msgid ""
+"Auto-starting the daemon locally is not enabled. See \"Options\" on the "
+"\"Connection Manager\"."
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:346
+msgid "Failed To Connect"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:403
+msgid "Edit Host"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:428
+msgid "Error Adding Host"
+msgstr ""
+
+#: deluge/ui/gtk3/connectionmanager.py:464
+msgid "Error Updating Host"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:131
+#: deluge/ui/console/cmdline/commands/connect.py:33
+#: deluge/ui/console/modes/preferences/preference_panes.py:651
+msgid "Username"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:135
+#: deluge/ui/console/modes/preferences/preference_panes.py:399
+msgid "Level"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:159
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:2
+#: deluge/ui/console/modes/preferences/preference_panes.py:383
+#: deluge/ui/console/modes/preferences/preference_panes.py:392
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:12
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:46
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:67
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:71
+msgid "Enabled"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:162
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:87
+msgid "Plugin"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:876 deluge/ui/gtk3/preferences.py:886
+msgid "Attention"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:876
+msgid "You must choose a language"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:887
+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 ""
+
+#: deluge/ui/gtk3/preferences.py:943
+#, python-format
+msgid "Do you want to restart to use %s mode?"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1154
+msgid "Select the Plugin"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1170
+msgid "Plugin Eggs"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1297
+msgid "Server Side Error"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1298
+msgid "An error occurred on the server"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1368 deluge/ui/gtk3/preferences.py:1375
+msgid "Error Adding Account"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1369
+msgid "Authentication failed"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1376
+msgid "An error occurred while adding account"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1408
+msgid "Error Updating Account"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1409
+msgid "An error occurred while updating account"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1427
+msgid "Remove Account"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1429
+#, python-format
+msgid ""
+"Are you sure you want to remove the account with the username "
+"\"%(username)s\"?"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1441 deluge/ui/gtk3/preferences.py:1448
+msgid "Error Removing Account"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1442
+msgid "Auhentication failed"
+msgstr ""
+
+#: deluge/ui/gtk3/preferences.py:1449
+msgid "An error occurred while removing account"
+msgstr ""
+
+#: deluge/ui/gtk3/filtertreeview.py:122
+#: deluge/ui/web/js/deluge-all/FilterPanel.js:28
+msgid "States"
+msgstr ""
+
+#: 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 ""
+
+#: 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 ""
+
+#: deluge/ui/gtk3/filtertreeview.py:137
+msgid "Admin"
+msgstr ""
+
+#: deluge/ui/gtk3/filtertreeview.py:159
+#: deluge/ui/web/js/deluge-all/FilterPanel.js:34
+msgid "Labels"
+msgstr ""
+
+#: deluge/ui/gtk3/filtertreeview.py:204
+#: deluge/plugins/Label/deluge_label/gtkui/submenu.py:28
+msgid "No Label"
+msgstr ""
+
+#: deluge/ui/gtk3/filtertreeview.py:206
+msgid "No Owner"
+msgstr ""
+
+#: deluge/ui/gtk3/new_release_dialog.py:60
+msgid "<i>Client Version</i>"
+msgstr ""
+
+#: deluge/ui/gtk3/queuedtorrents.py:118
+msgid " Torrents Queued"
+msgstr ""
+
+#: deluge/ui/gtk3/queuedtorrents.py:120
+msgid " Torrent Queued"
+msgstr ""
+
+#: deluge/ui/gtk3/torrentview.py:421
+msgid "Torrent is shared between other Deluge users or not."
+msgstr ""
+
+#: deluge/ui/gtk3/removetorrentdialog.py:67
+msgid "Remove the selected torrents?"
+msgstr ""
+
+#: deluge/ui/gtk3/removetorrentdialog.py:68
+#, python-format
+msgid "Total of %s torrents selected"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:79
+msgid "Set Unlimited"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:91 deluge/ui/web/js/deluge-all/Menus.js:259
+msgid "On"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:94 deluge/ui/web/js/deluge-all/Menus.js:265
+msgid "Off"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:101
+msgid "Disable"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:104
+msgid "Enable..."
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:465
+msgid "Peer Upload Slots"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:466
+msgid "Set the maximum upload slots"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:471
+msgid "Stop Seed At Ratio"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:606
+msgid "Ownership Change Error"
+msgstr ""
+
+#: deluge/ui/gtk3/menubar.py:607
+msgid "There was an error while trying changing ownership."
+msgstr ""
+
+#: deluge/ui/gtk3/peers_tab.py:91
+#: deluge/ui/web/js/deluge-all/details/PeersTab.js:66
+msgid "Address"
+msgstr ""
+
+#: deluge/ui/gtk3/peers_tab.py:107
+#: deluge/ui/web/js/deluge-all/details/PeersTab.js:73
+msgid "Client"
+msgstr ""
+
+#: deluge/ui/gtk3/__init__.py:29
+msgid "GTK Options"
+msgstr ""
+
+#: deluge/ui/gtk3/__init__.py:36
+msgid ""
+"Add one or more torrent files, torrent URLs or magnet URIs to a currently "
+"running Deluge GTK instance"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.progress.ui.h:1
+msgid "Creating Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:1
+msgid "Queued Torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:4
+msgid "Add Queued Torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:5
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:7
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:7
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:21
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:6
+msgid "_Remove"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:6
+msgid "_Clear"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:7
+msgid "Automatically add torrents on connect"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:1
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:4
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:12
+msgid "_File"
+msgstr "_Фишиер"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:2
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:2
+msgid "_Add Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:3
+msgid "_Create Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:4
+msgid "Quit & _Shutdown Daemon"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:5
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:8
+msgid "_Quit"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:6
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:192
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:6
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:5
+msgid "_Edit"
+msgstr "_Редактаре"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:7
+msgid "_Preferences"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:8
+msgid "_Connection Manager"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:9
+msgid "_Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:10
+msgid "_View"
+msgstr "_Аспект"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:11
+msgid "_Toolbar"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:12
+msgid "_Sidebar"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:13
+msgid "Status_bar"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:14
+msgid "T_abs"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:15
+msgid "_Columns"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:16
+msgid "_Find ..."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:17
+msgid "S_idebar"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:18
+msgid "Show _Zero Hits"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:19
+msgid "Show _Trackers"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:20
+msgid "Show _Owners"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:21
+msgid "_Help"
+msgstr "_Информацие"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:22
+msgid "_Homepage"
+msgstr "_Паӂинэ де интернет"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:23
+msgid "_FAQ"
+msgstr "_Ынтребэрь фреквенте"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:24
+msgid "Frequently Asked Questions"
+msgstr "Ынтребэрь фреквенте"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:25
+msgid "_Community"
+msgstr "_Комунитате"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:26
+msgid "_About"
+msgstr "_Деспре програм"
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:27
+msgid "Add torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:28
+msgid "Add Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:29
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:221
+msgid "Remove torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:30
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:105
+#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:1
+#: deluge/ui/web/js/deluge-all/RemoveWindow.js:16
+#: deluge/ui/web/js/deluge-all/RemoveWindow.js:34
+#: deluge/ui/web/js/deluge-all/Menus.js:331
+msgid "Remove Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:31
+msgid ""
+"Filter torrents by name.\n"
+"This will filter torrents for the current selection on the sidebar."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:33
+msgid "Filter"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:34
+msgid "Pause the selected torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:35
+#: deluge/ui/web/js/deluge-all/Toolbar.js:54
+#: deluge/ui/web/js/deluge-all/Menus.js:52
+msgid "Pause"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:36
+msgid "Resume the selected torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:37
+#: deluge/ui/web/js/deluge-all/Toolbar.js:61
+#: deluge/ui/web/js/deluge-all/Menus.js:59
+msgid "Resume"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:38
+msgid "Queue Torrent Up"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:39
+msgid "Queue Up"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:40
+msgid "Queue Torrent Down"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:41
+msgid "Queue Down"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:42
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:14
+#: deluge/ui/web/js/deluge-all/Toolbar.js:84
+#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:24
+msgid "Preferences"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:43
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:1
+#: deluge/ui/web/js/deluge-all/Toolbar.js:91
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:21
+msgid "Connection Manager"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:44
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:2
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:111
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:32
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:32
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:32
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:211
+#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:86
+msgid "Close"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:45
+msgid "Filter:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:46
+msgid "Clear the search"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.ui.h:47
+msgid "_Match Case"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:1
+#: deluge/ui/console/modes/preferences/preference_panes.py:383
+#: deluge/ui/console/modes/preferences/preference_panes.py:392
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:45
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:66
+msgid "Forced"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:3
+#: deluge/ui/console/modes/preferences/preference_panes.py:383
+#: deluge/ui/console/modes/preferences/preference_panes.py:392
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:47
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:68
+msgid "Disabled"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:4
+#: deluge/ui/console/modes/preferences/preference_panes.py:400
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:87
+msgid "Handshake"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:5
+#: deluge/ui/console/modes/preferences/preference_panes.py:400
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:88
+msgid "Full Stream"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:6
+#: deluge/ui/console/modes/preferences/preference_panes.py:400
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:89
+msgid "Either"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:8
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:34
+msgid "Socks4"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:9
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:35
+msgid "Socks5"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:10
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:36
+msgid "Socks5 Auth"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:11
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:37
+msgid "HTTP"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:12
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:38
+msgid "HTTP Auth"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:13
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:39
+msgid "I2P"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:19
+msgid "The standalone self-contained application"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:20
+msgid "Thin Client"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:21
+msgid "Connect to a Deluge daemon (deluged)"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:22
+msgid "Application Mode"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:23
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:46
+msgid "Show session speed in titlebar"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:24
+msgid "Focus window when adding torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:25
+msgid ""
+"The pieces bar\n"
+"will increase bandwidth use between client\n"
+"and daemon (does not apply in Standalone mode)."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:28
+msgid "Show a pieces bar in Status tab"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:29
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:25
+#: deluge/ui/web/render/tab_status.html:27
+msgid "Completed:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:30
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:72
+msgid "Downloading:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:31
+msgid "Waiting:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:32
+msgid "Missing:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:33
+msgid "_Revert"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:34
+msgid "Revert color to default"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:35
+msgid "Piece Colors"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:36
+msgid "Main Window"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:37
+msgid "Enable system tray icon"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:38
+msgid "App Indicator"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:39
+msgid "Systray"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:40
+msgid "Minimize to tray on close"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:41
+msgid "Start in tray"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:42
+msgid "Password protect system tray"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:44
+msgid "System Tray"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:45
+msgid "Notify about new releases"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:46
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:38
+msgid "Updates"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:47
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:235
+msgid "System Default"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:48
+msgid "<b>Language</b>"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:49
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:16
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:61
+msgid "Move completed to:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:50
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:15
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:70
+msgid "Copy of .torrent files to:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:51
+#: deluge/ui/console/modes/preferences/preference_panes.py:275
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:19
+msgid "Delete copy of torrent file on remove"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:52
+msgid ""
+"Delete the copy of the torrent file created when the torrent is removed"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:53
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:53
+msgid "Download to:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:54
+msgid "Download Folders"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:55
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:93
+msgid "Prioritize first and last pieces of torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:56
+msgid "Prioritize first and last pieces of files in torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:57
+#: deluge/ui/console/modes/preferences/preference_panes.py:287
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:102
+msgid "Sequential download"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:58
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:15
+msgid ""
+"When enabled, the piece picker will pick pieces in\n"
+"sequence instead of rarest first.\n"
+"\n"
+"Enabling sequential download will affect the piece\n"
+"distribution negatively in the swarm. It should be\n"
+"used sparingly."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:64
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:111
+msgid "Add torrents in Paused state"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:65
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:120
+msgid "Pre-allocate disk space"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:66
+msgid "Pre-allocate the disk space for the torrent files"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:67
+msgid "Add Torrent Options"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:68
+msgid "Always show"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:69
+msgid "Bring the dialog to focus"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:70
+msgid "Add Torrents Dialog"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:71
+msgid "Connection Attempts per Second:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:72
+msgid "Half-Open Connections:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:73
+msgid "The maximum number of connections allowed. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:74
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:42
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:30
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:8
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:8
+msgid "Connections:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:75
+msgid "The maximum upload slots for all torrents. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:76
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:43
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:32
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:5
+msgid "Upload Slots:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:77
+msgid "The maximum download speed for all torrents. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:78
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:41
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:7
+msgid "Download Speed:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:79
+msgid "The maximum upload speed for all torrents. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:81
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:39
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:6
+msgid "Upload Speed:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:82
+#: deluge/ui/console/modes/preferences/preference_panes.py:458
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:131
+msgid "Ignore limits on local network"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:83
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:141
+msgid "Rate limit IP overhead"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:84
+msgid ""
+"If checked, the estimated TCP/IP overhead is drained from the rate limiters, "
+"to avoid exceeding the limits with the total traffic"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:85
+msgid "Global Bandwidth Limits"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:86
+msgid "The maximum upload slots per torrent. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:87
+msgid "The maximum number of connections per torrent. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:88
+msgid "The maximum number download speed per torrent. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:89
+msgid "The maximum upload speed per torrent. Set -1 for unlimited."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:90
+msgid "Per-Torrent Bandwidth Limits"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:91
+#: deluge/ui/console/modes/preferences/preference_panes.py:556
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:42
+msgid "Queue to top"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:92
+#: deluge/ui/console/modes/preferences/preference_panes.py:554
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:30
+msgid "New Torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:93
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:85
+msgid "Seeding:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:94
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:59
+msgid "Total:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:95
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:102
+msgid "Ignore slow torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:96
+msgid ""
+"Torrents not transfering any data do not count towards download/seeding "
+"active count."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:97
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:111
+msgid "Prefer seeding torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:98
+msgid "Give preference to seeding torrents over downloading torrents."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:99
+#: deluge/ui/console/modes/preferences/preference_panes.py:558
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:50
+msgid "Active Torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:100
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:7
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:127
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:187
+#: deluge/ui/web/render/tab_status.html:4
+msgid "Share Ratio:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:101
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:142
+msgid "Time Ratio:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:102
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:157
+msgid "Time (m):"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:103
+#: deluge/ui/console/modes/preferences/preference_panes.py:590
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:118
+msgid "Seeding Rotation"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:104
+msgid "Pause Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:106
+#: deluge/ui/console/modes/preferences/preference_panes.py:627
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:173
+msgid "Share Ratio Reached"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:107
+msgid ""
+"The IP address of the interface to listen for incoming bittorrent "
+"connections on. Leave this empty if you want to use the default."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:108
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:38
+msgid "Incoming Address"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:109
+msgid "Random"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:110
+msgid "Uses random ports in range 49152 to 65525"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:111
+msgid "Active Port:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:112
+msgid "Test Active Port"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:113
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:58
+msgid "Incoming Port"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:114
+msgid ""
+"\n"
+"The network interface name or IP address for outgoing BitTorrent "
+"connections. (Leave empty for default.)\n"
+" "
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:117
+#: deluge/ui/console/modes/preferences/preference_panes.py:359
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:101
+msgid "Outgoing Interface"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:118
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:11
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:155
+msgid "From:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:119
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:165
+msgid "To:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:120
+#: deluge/ui/console/modes/preferences/preference_panes.py:328
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:120
+msgid "Outgoing Ports"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:121
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:59
+msgid "Outgoing:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:122
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:38
+msgid "Incoming:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:123
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:80
+msgid "Level:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:124
+#: deluge/ui/console/modes/preferences/preference_panes.py:379
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:18
+msgid "Encryption"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:125
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:194
+msgid "UPnP"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:126
+msgid "Universal Plug and Play"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:127
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:203
+msgid "NAT-PMP"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:128
+msgid "NAT Port Mapping Protocol"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:129
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:213
+msgid "Peer Exchange"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:130
+msgid "Exchanges peers between clients. (Disabling requires restart)"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:131
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:223
+msgid "LSD"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:132
+msgid "Local Service Discovery finds local peers on your network."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:133
+#: deluge/ui/console/widgets/statusbars.py:118
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:232
+msgid "DHT"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:134
+msgid "Distributed hash table may improve the amount of active connections."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:135
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:251
+msgid "Peer TOS Byte:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:136
+#: deluge/ui/console/modes/preferences/preference_panes.py:372
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:181
+msgid "Network Extras"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:137
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:4
+#: deluge/ui/console/modes/connectionmanager.py:121
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:5
+msgid "Hostname:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:138
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:5
+#: deluge/ui/console/modes/connectionmanager.py:122
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:6
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:52
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:52
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:62
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:150
+msgid "Port:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:140
+#: deluge/ui/console/modes/preferences/preference_panes.py:658
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:94
+msgid "Proxy Hostnames"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:141
+msgid ""
+"Hostnames should be attempted to be resolved through\n"
+"the proxy instead of using the local DNS service"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:143
+#: deluge/ui/console/modes/preferences/preference_panes.py:661
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:103
+msgid "Proxy Peers"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:144
+msgid "Proxy peer and web seed connections."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:145
+#: deluge/ui/console/modes/preferences/preference_panes.py:665
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:112
+msgid "Proxy Trackers"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:147
+msgid "Force Proxy Use"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:148
+#: deluge/ui/console/modes/preferences/preference_panes.py:671
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:141
+msgid "Hide Client Identity"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:149
+msgid ""
+"Attempt to hide client identity and only use proxy for incoming connections."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:150
+#: deluge/ui/console/modes/preferences/preference_panes.py:668
+#: deluge/ui/console/modes/preferences/preference_panes.py:669
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:120
+msgid "Force Proxy"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:151
+msgid "Cache Size (16 KiB blocks):"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:152
+msgid ""
+"The number of seconds from the last cached write to a piece in the write "
+"cache, to when it's forcefully flushed to disk. Default is 60 seconds."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:153
+#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:53
+msgid "Cache Expiry (seconds):"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:154
+#: deluge/ui/console/modes/preferences/preference_panes.py:694
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:14
+#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:29
+#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:30
+msgid "Settings"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:155
+msgid ""
+"The total number of 16 KiB blocks written to disk since this session was "
+"started."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:156
+msgid "Blocks Written:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:157
+msgid ""
+"The total number of write operations performed since this session was "
+"started."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:158
+msgid "Writes:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:159
+msgid ""
+"The ratio (blocks_written - writes) / blocks_written represents the number "
+"of saved write operations per total write operations, i.e. a kind of cache "
+"hit ratio for the write cache."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:160
+msgid "Write Cache Hit Ratio:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:161
+#: deluge/ui/console/modes/preferences/preference_panes.py:709
+msgid "Write"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:162
+msgid ""
+"The number of blocks that were requested from the bittorrent engine (from "
+"peers), that were served from disk or cache."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:163
+msgid "Blocks Read:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:164
+msgid "The number of blocks that were served from cache."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:165
+msgid "Blocks Read Hit:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:166
+msgid "The cache hit ratio for the read cache."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:167
+msgid "Read Cache Hit Ratio:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:168
+msgid ""
+"The total number of read operations performed since this session was started."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:169
+msgid "Reads:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:170
+#: deluge/ui/console/modes/preferences/preference_panes.py:723
+msgid "Read"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:171
+msgid ""
+"The number of 16 KiB blocks currently in the disk cache. This includes both "
+"read and write cache."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:172
+msgid "Cache Size:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:173
+msgid "Read Cache Size:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:175
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:7
+msgid "_Refresh"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:177
+msgid ""
+"Help us improve Deluge by sending us your Python version, PyGTK version, OS "
+"and processor types. Absolutely no other information is sent."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:178
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:77
+msgid "Yes, please send anonymous statistics"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:179
+#: deluge/ui/console/modes/preferences/preference_panes.py:503
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:57
+msgid "System Information"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:180
+msgid "Location:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:181
+msgid ""
+"If Deluge cannot find the database file at this location it will fallback to "
+"using DNS to resolve the peer's country."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:182
+#: deluge/ui/console/modes/preferences/preference_panes.py:516
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:85
+msgid "GeoIP Database"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:183
+msgid "Associate with Deluge"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:184
+msgid "Magnet Links"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:185
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:37
+msgid "Daemon port:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:186
+#: deluge/ui/console/modes/preferences/preference_panes.py:655
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:30
+msgid "Port"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:187
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:61
+msgid "Allow Remote Connections"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:188
+#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:4
+#: deluge/ui/web/js/deluge-all/Statusbar.js:39
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:50
+msgid "Connections"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:189
+#: deluge/ui/console/modes/preferences/preference_panes.py:543
+#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:80
+msgid "Periodically check the website for new releases"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:193
+msgid "_Delete"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:194
+msgid "Accounts"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:196
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:29
+msgid "Version:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:197
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:16
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:26
+msgid "Author:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:198
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:35
+msgid "Homepage:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:199
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:32
+msgid "Author Email:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:200
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:18
+msgid "Info"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:201
+msgid "_Install"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:202
+msgid "_Find More..."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:4
+msgid "Remove the selected torrent(s)?"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:5
+msgid "Include downloaded files"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:6
+msgid "(This is permanent!)"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:1
+msgid "Add Peer"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:4
+msgid "hostname:port"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:1
+msgid "Properties"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:3
+msgid "Max drop down rows"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:4
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:2
+#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:5
+msgid "<b>General</b>"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:5
+msgid "Show path entry"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:6
+msgid "Show file chooser"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:7
+msgid "Show folder name"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:8
+msgid "Path Chooser Type"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:9
+msgid "Enable autocomplete"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:10
+msgid "Show hidden files"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:11
+msgid "Set new key"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:12
+msgid "Press this key to set new key accelerators to trigger auto-complete"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:13
+msgid "Autocomplete"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:14
+msgid "Save path"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:15
+msgid "Ctrl+S"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:16
+msgid "Ctrl+E"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:17
+msgid "Ctrl+R"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:18
+msgid "Ctrl+H"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:19
+msgid "Ctrl+D"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:22
+msgid "Toggle hidden files"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:23
+msgid "Default path"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:24
+msgid "Shortcuts"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:25
+msgid "Select a Directory"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:26
+msgid "Saved paths"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:27
+msgid "column"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:29
+#: deluge/ui/console/modes/preferences/preferences.py:145
+#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:42
+#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:33
+#: deluge/ui/web/js/deluge-all/RemoveWindow.js:32
+#: deluge/ui/web/js/deluge-all/MoveStorage.js:36
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:34
+#: deluge/ui/web/js/deluge-all/OtherLimitWindow.js:51
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:64
+msgid "Cancel"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:30
+msgid "Open"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:31
+#: deluge/ui/web/js/deluge-all/Toolbar.js:39
+#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:43
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:33
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:92
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:94
+#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:27
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:65
+msgid "Add"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:32
+msgid "Add the current entry value to the list"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:33
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:98
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:33
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:102
+msgid "Edit"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:34
+msgid "Edit the selected entry"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:35
+#: deluge/ui/web/js/deluge-all/Toolbar.js:46
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:104
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:110
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:156
+msgid "Remove"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:36
+msgid "Remove the selected entry"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:38
+msgid "Move the selected entry up"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:40
+msgid "Move the selected entry down"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:41
+msgid "Default"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:42
+msgid "No default path set"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:43
+msgid "Open properties dialog"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:1
+msgid "Add Infohash"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
+msgid "From Infohash"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
+msgid "Infohash:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:6
+#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:5
+#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:53
+msgid "Trackers:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:1
+#: deluge/ui/console/modes/connectionmanager.py:51
+msgid "Add Host"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:1
+#: deluge/ui/web/js/deluge-all/MoveStorage.js:16
+#: deluge/ui/web/js/deluge-all/Menus.js:346
+msgid "Move Download Folder"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:4
+msgid "Move the torrent(s) download folder."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:5
+msgid "Destination:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:1
+msgid "New Release"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:3
+msgid "_Goto Website"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:4
+msgid "New Release Available!"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:5
+msgid "Available Version:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:6
+msgid "Server Version"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:7
+msgid "Current Version:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:8
+msgid "Do not show this dialog in the future"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:1
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:26
+#: deluge/ui/web/render/tab_status.html:9
+msgid "Down Speed:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:2
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:28
+#: deluge/ui/web/render/tab_status.html:10
+msgid "Up Speed:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:3
+#: deluge/ui/web/render/tab_status.html:2
+msgid "Downloaded:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:4
+#: deluge/ui/web/render/tab_status.html:3
+msgid "Uploaded:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:5
+#: deluge/ui/web/render/tab_status.html:16
+msgid "Seeds:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:6
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:10
+#: deluge/ui/web/render/tab_status.html:17
+msgid "Peers:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:8
+#: deluge/ui/web/render/tab_status.html:18
+msgid "Availability:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:9
+#: deluge/ui/web/render/tab_status.html:25
+msgid "Seed Rank:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:10
+msgid "ETA Time:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:11
+#: deluge/ui/web/render/tab_status.html:13
+msgid "Last Transfer:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:12
+#: deluge/ui/web/render/tab_status.html:23
+msgid "Active Time:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:13
+#: deluge/ui/web/render/tab_status.html:20
+msgid "Complete Seen:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:14
+#: deluge/ui/web/render/tab_status.html:24
+msgid "Seeding Time:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:16
+#: deluge/ui/web/render/tab_status.html:12
+msgid "Pieces:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:17
+#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:3
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:23
+msgid "Name:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:18
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:25
+msgid "Download Folder:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:19
+msgid "Added:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:20
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:26
+msgid "Total Size:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:21
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:27
+msgid "Total Files:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:22
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:24
+msgid "Hash:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:23
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:31
+msgid "Created By:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:24
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:17
+msgid "Comments:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:29
+msgid "Owner:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:34
+msgid "Move completed:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:36
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:39
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:12
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:205
+msgid "Stop seed at ratio:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:37
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:40
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:13
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:233
+msgid "Remove at ratio"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:44
+msgid "Bandwidth Limits"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:46
+msgid "Current Tracker:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:47
+msgid "Total Trackers:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:48
+#: deluge/ui/web/render/tab_status.html:6
+msgid "Tracker Status:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:49
+#: deluge/ui/web/render/tab_status.html:5
+msgid "Next Announce:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:50
+msgid "Private Torrent:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:51
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:8
+msgid "_Edit Trackers"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:1
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:44
+#: deluge/ui/web/js/deluge-all/Menus.js:284
+msgid "Top"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:4
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:45
+#: deluge/ui/web/js/deluge-all/Menus.js:305
+msgid "Bottom"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:1
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:50
+msgid "Add Torrents"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:5
+msgid "_URL"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:6
+msgid "Info_hash"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:11
+msgid "Move Complete Folder"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:12
+msgid "Add In _Paused State"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:13
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:134
+msgid "Prioritize First/Last Pieces"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:21
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:46
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:152
+msgid "Skip File Hash Check"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:23
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:170
+msgid "Preallocate Disk Space"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:24
+msgid "Preallocate the disk space for the torrent files"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:25
+msgid "Maximum torrent download speed"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:27
+msgid "Maximum torrent upload speed"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:29
+msgid "Maximum torrent connections"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:31
+msgid "Maximum torrent upload slots"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:34
+msgid "Apply To All"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:35
+msgid "Revert To Defaults"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:1
+msgid "_Show Deluge"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:3
+msgid "_Pause Session"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:4
+msgid "_Resume Session"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:5
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:1
+msgid "_Download Speed Limit"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:6
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:2
+msgid "_Upload Speed Limit"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/tray_menu.ui.h:7
+msgid "Quit & Shutdown Daemon"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:1
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:17
+#: deluge/ui/web/js/deluge-all/Menus.js:323
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:318
+msgid "Edit Trackers"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:4
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:19
+msgid "_Up"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.ui.h:8
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:22
+msgid "_Down"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:1
+msgid "_Add Peer"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:2
+msgid "Add a peer by its IP"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.edit.ui.h:1
+#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:17
+msgid "Edit Tracker"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.edit.ui.h:4
+#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:44
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:30
+msgid "Tracker:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:1
+msgid "Enter Remote Path"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:4
+msgid "Remote Path"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:5
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:5
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:94
+msgid "Path:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:1
+msgid "32 KiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:2
+msgid "64 KiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:3
+msgid "128 KiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:4
+msgid "256 KiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:5
+msgid "512 KiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:6
+msgid "1 MiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:7
+msgid "2 MiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:8
+msgid "4 MiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:9
+msgid "8 MiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:10
+msgid "16 MiB"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:11
+msgid "Create Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:13
+msgid "Fol_der"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:14
+msgid "_Remote Path"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:15
+#: deluge/ui/web/js/deluge-all/details/FilesTab.js:12
+#: deluge/ui/web/js/deluge-all/details/FilesTab.js:73
+#: deluge/ui/web/js/deluge-all/add/FilesTab.js:18
+msgid "Files"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:24
+msgid "Webseeds"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:25
+msgid "Piece Size:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:26
+msgid "Set Private Flag"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:27
+msgid "Add this torrent to the session"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:28
+#: deluge/ui/console/modes/preferences/preference_panes.py:279
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:48
+#: deluge/ui/web/js/deluge-all/Menus.js:66
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:26
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:80
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:17
+msgid "Options"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:1
+msgid "Save .torrent as"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:1
+msgid "_Open Download Folder"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:2
+msgid "_Pause"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:3
+msgid "Resu_me"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:4
+#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:4
+msgid "Resume selected torrents."
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:5
+msgid "Opt_ions"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:6
+msgid "_Queue"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:7
+msgid "_Update Tracker"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:9
+msgid "_Remove Torrent"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:10
+msgid "_Force Re-check"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.ui.h:11
+msgid "_Move Download Folder"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/other_dialog.ui.h:3
+msgid "label"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:1
+msgid "_Select All"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:2
+msgid "_Pause All"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:3
+msgid "Resu_me All"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:3
+msgid "_Connection Limit"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:4
+msgid "Upload _Slot Limit"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:5
+msgid "Stop seed at _ratio"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:6
+msgid "_Auto Managed"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:7
+msgid "_Super Seeding"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:8
+msgid "_Change Ownership"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:1
+#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:26
+msgid "Add Tracker"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:4
+msgid "Add Trackers"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:1
+msgid "Add URL"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:4
+msgid "From URL"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:5
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:1
+msgid "URL:"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:9
+msgid "Deluge Daemons"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:10
+msgid "Auto-connect to selected Daemon"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:11
+msgid "Auto-start localhost daemon (if required)"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:12
+msgid "Hide this dialog"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/connection_manager.ui.h:13
+msgid "Startup Options"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:1
+msgid "_Open File"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:2
+msgid "_Show Folder"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:3
+msgid "_Expand All"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:4
+msgid "_Skip"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:5
+msgid "_Low"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:6
+msgid "_Normal"
+msgstr ""
+
+#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:7
+msgid "_High"
+msgstr ""
+
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
+msgid "Deluge Team"
+msgstr ""
+
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
+msgid ""
+"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
+msgstr ""
+
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
+msgid ""
+"Deluge contains the common features to BitTorrent clients such as Protocol "
+"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
+"PMP, Proxy support, Web seeds, global and per-torrent speed limits. As "
+"Deluge heavily utilises the libtorrent library it has a comprehensive list "
+"of the features provided."
+msgstr ""
+
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
+msgid ""
+"Deluge has been designed to run as both a normal standalone desktop "
+"application and as a client-server. In Thinclient mode a Deluge daemon "
+"handles all the BitTorrent activity and is able to run on headless machines "
+"with the user-interfaces connecting remotely from any other platform."
+msgstr ""
+
+#: deluge/ui/data/share/applications/deluge.desktop.in.h:2
+msgid "BitTorrent Client"
+msgstr ""
+
+#: deluge/ui/data/share/applications/deluge.desktop.in.h:3
+msgid "Deluge BitTorrent Client"
+msgstr ""
+
+#: deluge/ui/data/share/applications/deluge.desktop.in.h:4
+msgid "Download and share files over BitTorrent"
+msgstr ""
+
+#: deluge/ui/console/console.py:76
+msgid "Console Options"
+msgstr ""
+
+#: deluge/ui/console/console.py:78
+msgid ""
+"These daemon connect options will be used for commands, or if console ui "
+"autoconnect is enabled."
+msgstr ""
+
+#: deluge/ui/console/console.py:87
+msgid "Deluge daemon IP address to connect to (default 127.0.0.1)"
+msgstr ""
+
+#: deluge/ui/console/console.py:96
+msgid "Deluge daemon port to connect to (default 58846)"
+msgstr ""
+
+#: deluge/ui/console/console.py:104
+msgid "Deluge daemon username to use when connecting"
+msgstr ""
+
+#: deluge/ui/console/console.py:111
+msgid "Deluge daemon password to use when connecting"
+msgstr ""
+
+#: deluge/ui/console/console.py:131
+msgid "Console Commands"
+msgstr ""
+
+#: deluge/ui/console/console.py:132
+msgid "Description"
+msgstr ""
+
+#: deluge/ui/console/console.py:133
+msgid "The following console commands are available:"
+msgstr ""
+
+#: deluge/ui/console/console.py:134
+#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:2
+msgid "Command"
+msgstr ""
+
+#: deluge/ui/console/cmdline/command.py:208
+#, python-format
+msgid "`%s` alias"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/manage.py:29
+msgid "Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/manage.py:35
+msgid "an expression matched against torrent ids and torrent names"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/manage.py:43
+#: deluge/ui/console/cmdline/commands/config.py:88
+msgid "set value for this key"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/manage.py:46
+#: deluge/ui/console/cmdline/commands/config.py:91
+msgid "Value to set"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/manage.py:53
+#: deluge/ui/console/cmdline/commands/config.py:98
+msgid "one or more keys separated by space"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/rm.py:33
+msgid "Also removes the torrent data"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/rm.py:40
+msgid "List the matching torrents without removing."
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/rm.py:46
+#: deluge/ui/console/cmdline/commands/recheck.py:28
+#: deluge/ui/console/cmdline/commands/move.py:31
+msgid "One or more torrent ids"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/rm.py:66
+#, python-format
+msgid "Confirm with -c to remove the listed torrents (Count: %d)"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/resume.py:22
+msgid "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/resume.py:29
+msgid "One or more torrent ids. Use \"*\" to resume all torrents"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/pause.py:29
+msgid "One or more torrent ids. Use \"*\" to pause all torrents"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/add.py:38
+msgid "Download folder for torrent"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/add.py:44
+msgid "Move the completed torrent to this folder"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/add.py:50
+msgid "One or more torrent files, URLs or magnet URIs"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/plugin.py:29
+msgid "Lists available plugins"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/plugin.py:37
+msgid "Shows enabled plugins"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/plugin.py:40
+msgid "Enables a plugin"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/plugin.py:43
+msgid "Disables a plugin"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/plugin.py:51
+msgid "Reload list of available plugins"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/plugin.py:54
+msgid "Install a plugin from an .egg file"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/status.py:36
+msgid ""
+"Raw values for upload/download rates (without KiB/s suffix)(useful for "
+"scripts that want to do their own parsing)"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/status.py:46
+msgid "Do not show torrent status (Improves command speed)"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/connect.py:26
+msgid "Usage: connect <host[:port]> [<username>] [<password>]"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/connect.py:30
+msgid "Daemon host and port"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/connect.py:36
+#: deluge/ui/console/modes/preferences/preference_panes.py:652
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:259
+msgid "Password"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/move.py:34
+msgid "The path to move the torrents to"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/debug.py:26
+msgid "The new state"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/help.py:29
+msgid "One or more commands"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/config.py:79
+msgid "Usage: config [--set <key> <value>] [<key> [<key>...] ]"
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/info.py:101
+msgid "Show more information per torrent."
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/info.py:109
+msgid "Show more detailed information including files and peers."
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/info.py:116
+#, python-format
+msgid "Show torrents with state STATE: %s."
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/info.py:132
+msgid "Same as --sort but items are in reverse order."
+msgstr ""
+
+#: deluge/ui/console/cmdline/commands/info.py:138
+msgid "One or more torrent ids. If none is given, list all"
+msgstr ""
+
+#: deluge/ui/console/modes/connectionmanager.py:44
+msgid "Select Host"
+msgstr ""
+
+#: deluge/ui/console/modes/connectionmanager.py:51
+msgid "Quit"
+msgstr ""
+
+#: deluge/ui/console/modes/connectionmanager.py:51
+msgid "Delete Host"
+msgstr ""
+
+#: deluge/ui/console/modes/connectionmanager.py:116
+msgid "Add Host (Up & Down arrows to navigate, Esc to cancel)"
+msgstr ""
+
+#: deluge/ui/console/modes/connectionmanager.py:133
+msgid "Error adding host"
+msgstr ""
+
+#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
+msgid "Columns"
+msgstr ""
+
+#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
+msgid "Width"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:178
+msgid "General options"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:182
+msgid "Ring system bell when a download finishes"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:188
+msgid "List complete torrents after incomplete regardless of sorting order"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:193
+msgid "Move selection when moving torrents in the queue"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:200
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:67
+msgid "Language"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:202
+msgid "Command Line Mode"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:205
+msgid "Do not store duplicate input in history"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:210
+msgid "Store and load command line history in command line mode"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:216
+msgid "Third tab lists all remaining torrents in command line mode"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:221
+msgid "Torrents per tab press"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:234
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:18
+#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:39
+msgid "Folders"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:237
+msgid "Download To"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:254
+msgid "Move completed to"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:269
+msgid "Copy of .torrent files to"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:290
+msgid "Add Paused"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:293
+msgid "Pre-Allocate disk space"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:304
+msgid "Incomming Ports"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:313
+#: deluge/ui/console/modes/preferences/preference_panes.py:337
+msgid "From"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:321
+#: deluge/ui/console/modes/preferences/preference_panes.py:345
+msgid "To"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:331
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:131
+msgid "Use Random Ports"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:352
+msgid "Incoming Interface"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:355
+msgid "IP address of the interface to listen on (leave empty for default):"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:363
+msgid ""
+"The network interface name or IP address for outgoing BitTorrent "
+"connections. (Leave empty for default.):"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:382
+msgid "Inbound"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:391
+msgid "Outbound"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:413
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:38
+msgid "Global Bandwidth Usage"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:416
+#: deluge/ui/console/modes/preferences/preference_panes.py:469
+msgid "Maximum Connections"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:423
+#: deluge/ui/console/modes/preferences/preference_panes.py:476
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:63
+msgid "Maximum Upload Slots"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:430
+#: deluge/ui/console/modes/preferences/preference_panes.py:483
+msgid "Maximum Download Speed (KiB/s)"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:437
+#: deluge/ui/console/modes/preferences/preference_panes.py:490
+msgid "Maximum Upload Speed (KiB/s)"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:444
+msgid "Maximum Half-Open Connections"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:451
+msgid "Maximum Connection Attempts per Second"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:463
+msgid "Rate Limit IP Overhead"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:466
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:148
+msgid "Per Torrent Bandwidth Usage"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:513
+msgid "Yes, please send anonymous statistics."
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:531
+msgid "Daemon Port"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:538
+msgid "Allow remote connections"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:561
+msgid "Total"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:593
+msgid "Share Ratio"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:601
+msgid "Time Ratio"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:609
+msgid "Time (m)"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:633
+msgid "Remove torrent (Unchecked pauses torrent)"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:646
+msgid "Proxy Settings"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:649
+msgid "Type"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:653
+msgid "Hostname"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:673
+msgid "Proxy Type Help"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:697
+msgid "Cache Size (16 KiB blocks)"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:704
+msgid "Cache Expiry (seconds)"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:712
+msgid "Blocks Written"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:716
+msgid "Writes"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:720
+msgid "Write Cache Hit Ratio"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:725
+msgid "Blocks Read"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:729
+msgid "Blocks Read hit"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:732
+msgid "Reads"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:735
+msgid "Read Cache Hit Ratio"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:741
+msgid "Cache Size"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preference_panes.py:746
+msgid "Read Cache Size"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preferences.py:145
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:333
+#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:87
+msgid "Apply"
+msgstr ""
+
+#: deluge/ui/console/modes/preferences/preferences.py:145
+#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:35
+#: deluge/ui/web/js/deluge-all/OtherLimitWindow.js:52
+#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:88
+msgid "OK"
+msgstr ""
+
+#: deluge/ui/console/widgets/fields.py:1070
+msgid "Select Language"
+msgstr ""
+
+#: deluge/ui/console/widgets/statusbars.py:120
+#, python-format
+msgid "IP {!white,blue!}%s{!status!}"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/common.py:114
+#: deluge/plugins/Blocklist/deluge_blocklist/common.py:116
+#: deluge/plugins/Blocklist/deluge_blocklist/common.py:118
+#, python-format
+msgid "The IP address \"%s\" is badly formed"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:21
+msgid "Emule IP list (GZip)"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:22
+msgid "SafePeer Text (Zipped)"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:23
+msgid "PeerGuardian Text (Uncompressed)"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:24
+msgid "PeerGuardian P2B (GZip)"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:45
+msgid "Blocked IP Ranges /Whitelisted IP Ranges"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:56
+#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:156
+#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:204
+msgid "Blocklist"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:233
+msgid "Bad IP address"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py:40
+msgid "Invalid leader"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py:44
+msgid "Invalid magic code"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py:49
+msgid "Invalid version"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:3
+msgid "Days"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:4
+msgid "Check for new list every:"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:5
+msgid "Import blocklist on startup"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:6
+#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:4
+msgid "<b>Settings</b>"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:7
+msgid "Download the blocklist file if necessary and import the file."
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:8
+msgid "Check Download and Import"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:9
+msgid "Download a new blocklist file and import it."
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:10
+msgid "Force Download and Import"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:11
+msgid "Blocklist is up to date"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:12
+msgid "<b>Options</b>"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:13
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:25
+msgid "Type:"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:14
+msgid "Date:"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:15
+msgid "File Size:"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:16
+msgid "<b>Info</b>"
+msgstr ""
+
+#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:17
+msgid "<b>Whitelist</b>"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/gtkui.py:36
+msgid "Torrent Complete"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/gtkui.py:37
+msgid "Torrent Added"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/gtkui.py:38
+msgid "Torrent Removed"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/gtkui.py:64
+#: deluge/plugins/Execute/deluge_execute/gtkui.py:79
+msgid "Execute"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:1
+msgid "Event"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:3
+msgid "<b>Add Command</b>"
+msgstr ""
+
+#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:4
+msgid "<b>Commands</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:327
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:342
+msgid "Incompatible Option"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:418
+msgid ""
+"\"Watch Folder\" directory and \"Copy of .torrent files to\" directory "
+"cannot be the same!"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:462
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:466
+msgid "AutoAdd"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:495
+msgid "Double-click to toggle"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:503
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:511
+msgid "Double-click to edit"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:507
+msgid "Path"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:125
+msgid "Watch folder does not exist."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:128
+#: deluge/plugins/AutoAdd/deluge_autoadd/core.py:443
+msgid "Path does not exist."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:1
+msgid "Watch Folder Properties"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:2
+msgid ""
+"If a .torrent file is added to this directory,\n"
+"it will be added to the session."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:4
+#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:2
+msgid "Select A Folder"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:5
+msgid "Enable this watch folder"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:6
+msgid "<b>Watch Folder</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:7
+msgid "Delete .torrent after adding"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:8
+msgid ""
+"Once the torrent is added to the session,\n"
+"the .torrent will be deleted."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:10
+msgid "Append extension after adding:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:11
+msgid ""
+"Once the torrent is added to the session,\n"
+"an extension will be appended to the .torrent\n"
+"and it will remain in the same directory."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:14
+msgid ".added"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:16
+msgid ""
+"Once the torrent is added to the session,\n"
+"the .torrent will copied to the chosen directory\n"
+"and deleted from the watch folder."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:20
+msgid ""
+"Once the torrent is deleted from the session,\n"
+"also delete the .torrent file used to add it."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:22
+msgid "<b>Torrent File Action</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:23
+msgid "Set download folder"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:24
+msgid "This folder will be where the torrent data is downloaded to."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:25
+msgid "<b>Download Folder</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:26
+msgid "Set move completed folder"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:27
+msgid "<b>Move Completed</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:28
+msgid "Label: "
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:29
+msgid "<b>Label</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:30
+msgid "Main"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:31
+msgid "The user selected here will be the owner of the torrent."
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:32
+msgid "<b>Owner</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:33
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:103
+msgid "Max Upload Speed:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:34
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:130
+msgid "Max Connections:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:35
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:153
+msgid "Max Upload Slots:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:37
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:77
+msgid "Max Download Speed:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:38
+msgid "<b>Bandwidth</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:41
+#: deluge/ui/web/render/tab_status.html:19
+msgid "Auto Managed:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:42
+msgid "Add Paused:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:43
+msgid "Queue to:"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:47
+msgid "<b>Queue</b>"
+msgstr ""
+
+#: deluge/plugins/AutoAdd/deluge_autoadd/data/config.ui.h:1
+msgid "<b>Watch Folders:</b>"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/gtkui.py:60
+msgid "minutes"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/gtkui.py:62
+msgid "1 minute"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/gtkui.py:64
+msgid "1 second"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/gtkui.py:66
+msgid "seconds"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:1
+msgid "Stats"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:2
+msgid "Resolution"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:5
+msgid "Seeds/Peers"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:1
+msgid "Download color:"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:2
+msgid "Upload color:"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:3
+msgid "<b>Connections Graph</b>"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:4
+msgid "<b>Bandwidth Graph</b>"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:5
+msgid "DHT nodes:"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:6
+msgid "Cached DHT nodes:"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:7
+msgid "DHT torrents:"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:9
+msgid "<b>Seeds / Peers</b>"
+msgstr ""
+
+#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:11
+msgid "<b>Graph Colors</b>"
+msgstr ""
+
+#: deluge/plugins/WebUi/deluge_webui/gtkui.py:35
+#: deluge/plugins/WebUi/deluge_webui/gtkui.py:47
+msgid "WebUi"
+msgstr ""
+
+#: deluge/plugins/WebUi/deluge_webui/gtkui.py:90
+msgid ""
+"The Deluge web interface is not installed, please install the\n"
+"interface and try again"
+msgstr ""
+
+#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:1
+msgid "Enable web interface"
+msgstr ""
+
+#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:2
+msgid "Enable SSL"
+msgstr ""
+
+#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:3
+msgid "Listening port:"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/core.py:184
+msgid "Invalid label, valid characters:[a-z0-9_-]"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/core.py:186
+msgid "Empty Label"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/core.py:187
+msgid "Label already exists"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/core.py:195
+#: deluge/plugins/Label/deluge_label/core.py:285
+#: deluge/plugins/Label/deluge_label/core.py:320
+msgid "Unknown Label"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/core.py:321
+msgid "Unknown Torrent"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:46
+msgid "Label _Options"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:47
+msgid "_Remove Label"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:48
+msgid "_Add Label"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py:177
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:2
+msgid "Label Options"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/gtkui/submenu.py:34
+#: deluge/plugins/Label/deluge_label/gtkui/label_config.py:37
+#: deluge/plugins/Label/deluge_label/gtkui/label_config.py:45
+#: deluge/plugins/Label/deluge_label/gtkui/__init__.py:49
+#: deluge/plugins/Label/deluge_label/gtkui/__init__.py:77
+msgid "Label"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:1
+msgid "tracker1.org"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:3
+msgid "<b>Label Options</b>"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:9
+msgid "Apply per torrent max settings:"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:10
+msgid "Maximum"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:14
+msgid "Apply Queue settings:"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:17
+msgid "Apply folder settings:"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:19
+msgid "<i>(1 line per tracker)</i>"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:20
+msgid "Automatically apply label:"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:1
+msgid "Add Label"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:2
+msgid "<b>Add Label</b>"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_pref.ui.h:1
+msgid "<i>Use the sidebar to add,edit and remove labels. </i>\n"
+msgstr ""
+
+#: deluge/plugins/Label/deluge_label/data/label_pref.ui.h:3
+msgid "<b>Labels</b>"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:171
+msgid "Notification Blink shown"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:175
+msgid "Popup notification is not enabled."
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:177
+msgid "libnotify is not installed"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:185
+msgid "Failed to popup notification"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:188
+msgid "Notification popup shown"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:192
+msgid "Sound notification not enabled"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:194
+msgid "pygame is not installed"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:206
+#, python-format
+msgid "Sound notification failed %s"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:210
+msgid "Sound notification Success"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:234
+msgid "Finished Torrent"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:238
+#, python-format
+msgid ""
+"The torrent \"%(name)s\" including %(num_files)i file(s) has finished "
+"downloading."
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:287
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:317
+msgid "Notifications"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:663
+msgid "Choose Sound File"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/core.py:127
+#: deluge/plugins/Notifications/deluge_notifications/core.py:158
+#, python-format
+msgid "There was an error sending the notification email: %s"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/core.py:145
+#, python-format
+msgid "Server did not reply properly to HELO greeting: %s"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/core.py:149
+#, python-format
+msgid "Server refused username/password combination: %s"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/core.py:174
+msgid "Notification email sent."
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/core.py:181
+#, python-format
+msgid "Finished Torrent \"%(name)s\""
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/core.py:184
+#, python-format
+msgid ""
+"This email is to inform you that Deluge has finished downloading "
+"\"%(name)s\", which includes %(num_files)i files.\n"
+"To stop receiving these alerts, simply turn off email notification in "
+"Deluge's preferences.\n"
+"\n"
+"Thank you,\n"
+"Deluge."
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:1
+msgid "Tray icon blinks enabled"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:2
+msgid "Popups enabled"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:3
+msgid "Sound enabled"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:4
+msgid "<b>UI Notifications</b>"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:9
+msgid "<b>Recipients</b>"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:10
+msgid "Server requires TLS/SSL"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:13
+msgid "<b>Email Notifications</b>"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:15
+msgid ""
+"This configuration does not mean that you'll actually receive notifications "
+"for all these events."
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:16
+msgid "Subscriptions"
+msgstr ""
+
+#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:17
+msgid "Sound Customization"
+msgstr ""
+
+#: deluge/plugins/Extractor/deluge_extractor/gtkui.py:42
+#: deluge/plugins/Extractor/deluge_extractor/gtkui.py:53
+msgid "Extractor"
+msgstr ""
+
+#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:1
+msgid "Extract to:"
+msgstr ""
+
+#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:3
+msgid "Create torrent name sub-folder"
+msgstr ""
+
+#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:4
+msgid ""
+"This option will create a sub-folder using the torrent's name within the "
+"selected extract folder and put the extracted files there."
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:196
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:359
+msgid "Scheduler"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:289
+msgid "<b>Schedule</b>"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:301
+msgid "Download Limit:"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:310
+msgid "Upload Limit:"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:319
+msgid "Active Torrents:"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:328
+msgid "Active Downloading:"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:337
+msgid "Active Seeding:"
+msgstr ""
+
+#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:350
+msgid "<b>Slow Settings</b>"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/FileBrowser.js:13
+msgid "File Browser"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/FileBrowser.js:25
+msgid "Back"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/FileBrowser.js:29
+msgid "Forward"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/FileBrowser.js:37
+msgid "Home"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Toolbar.js:32
+msgid "Create"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Toolbar.js:100
+msgid "Help"
+msgstr "Информацие"
+
+#: deluge/ui/web/js/deluge-all/Toolbar.js:108
+msgid "Logout"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/EditTrackerWindow.js:34
+msgid "Save"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:19
+msgid "About Deluge"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/AboutWindow.js:102
+msgid "Copyright 2007-2018 Deluge Team"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/RemoveWindow.js:33
+msgid "Remove With Data"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:17
+msgid "Add Connection"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:44
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:44
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:53
+msgid "Host:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:96
+#, python-brace-format
+msgid "Unable to add host: {0}"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/MoveStorage.js:37
+msgid "Move"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/MoveStorage.js:54
+msgid "Browse"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:17
+msgid "Edit Connection"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:115
+msgid "Unable to edit host"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/LoginWindow.js:22
+#: deluge/ui/web/js/deluge-all/LoginWindow.js:31
+msgid "Login"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/LoginWindow.js:108
+msgid "Login Failed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/LoginWindow.js:109
+msgid "You entered an incorrect password"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:228
+msgid "Public"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/TorrentGrid.js:292
+msgid "Last Transfer"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Deluge.js:158
+msgid "Mixed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:87
+msgid "Set Maximum Connections"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:97
+msgid "Download Speed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:102
+#: deluge/ui/web/js/deluge-all/Statusbar.js:161
+#: deluge/ui/web/js/deluge-all/Menus.js:79
+#: deluge/ui/web/js/deluge-all/Menus.js:124
+msgid "5 KiB/s"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:108
+#: deluge/ui/web/js/deluge-all/Statusbar.js:167
+#: deluge/ui/web/js/deluge-all/Menus.js:85
+#: deluge/ui/web/js/deluge-all/Menus.js:130
+msgid "10 KiB/s"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:114
+#: deluge/ui/web/js/deluge-all/Statusbar.js:173
+#: deluge/ui/web/js/deluge-all/Menus.js:91
+#: deluge/ui/web/js/deluge-all/Menus.js:136
+msgid "30 KiB/s"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:120
+#: deluge/ui/web/js/deluge-all/Statusbar.js:179
+#: deluge/ui/web/js/deluge-all/Menus.js:97
+#: deluge/ui/web/js/deluge-all/Menus.js:142
+msgid "80 KiB/s"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:126
+#: deluge/ui/web/js/deluge-all/Statusbar.js:185
+#: deluge/ui/web/js/deluge-all/Menus.js:103
+#: deluge/ui/web/js/deluge-all/Menus.js:148
+msgid "300 KiB/s"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:145
+msgid "Set Maximum Download Speed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:156
+msgid "Upload Speed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:204
+msgid "Set Maximum Upload Speed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:215
+msgid "Protocol Traffic Download/Upload"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:242
+msgid "Freespace in download folder"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Statusbar.js:357
+#, python-brace-format
+msgid "<b>IP</b> {0}"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:33
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:187
+msgid "Connect"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:120
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:197
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:379
+msgid "Stop Daemon"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:185
+msgid "Disconnect"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:204
+msgid "Start Daemon"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:322
+msgid "Change Default Password"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/ConnectionManager.js:324
+msgid ""
+"We recommend changing the default password.<br><br>Would you like to change "
+"it now?"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Sidebar.js:13
+msgid "Tracker Host"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Sidebar.js:33
+msgid "Filters"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/UI.js:142
+msgid "Connection restored"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/UI.js:153
+msgid "Lost Connection"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/UI.js:154
+msgid "The connection to the webserver has been lost!"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/UI.js:160
+msgid "Lost connection to webserver"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:72
+msgid "D/L Speed Limit"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:117
+msgid "U/L Speed Limit"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:162
+msgid "Connection Limit"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:207
+msgid "Upload Slot Limit"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:316
+msgid "Update Tracker"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:339
+msgid "Force Recheck"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/Menus.js:359
+msgid "Expand All"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:13
+msgid "Details"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:28
+msgid "Comment:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:29
+msgid "Status:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:242
+msgid "Move Completed:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:272
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:116
+msgid "General"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:279
+msgid "Private"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/StatusTab.js:39
+msgid "Loading"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/details/StatusTab.js:118
+msgid "True"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:50
+msgid "Be alerted about new releases"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:67
+msgid ""
+"Help us improve Deluge by sending us your Python version, PyGTK version, OS "
+"and processor types. Absolutely no other information is sent."
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:215
+msgid "Pause torrent"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:17
+msgid "Install Plugin"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:33
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:109
+msgid "Install"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:45
+msgid "Select an egg"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:46
+msgid "Plugin Egg"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:49
+msgid "Browse..."
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:59
+msgid "Uploading your plugin..."
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:52
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:162
+msgid "Maximum Connections:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:74
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:184
+msgid "Maximum Download Speed (KiB/s):"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:85
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:195
+msgid "Maximum Upload Speed (KiB/s):"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:96
+msgid "Maximum Half-Open Connections:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:107
+msgid "Maximum Connection Attempts per Second:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:173
+msgid "Maximum Upload Slots:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:43
+msgid "Cache Size (16 KiB Blocks):"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:132
+msgid "Force Use of Proxy"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:116
+msgid "Find More"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:69
+msgid "Use Random Port"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:241
+msgid "Type Of Service"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:53
+msgid "Show filters with zero torrents"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:60
+msgid "Allow the use of multiple filters at once"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:94
+msgid "WebUI Password"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:110
+msgid "Old:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:114
+msgid "New:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:118
+msgid "Confirm:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:124
+msgid "Server"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:140
+msgid "Session Timeout:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:165
+msgid "Enable SSL (paths relative to Deluge config folder)"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:177
+msgid "Private Key:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:187
+msgid "Certificate:"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:205
+msgid "WebUI Language Changed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:207
+msgid "Do you want to refresh the page now to use the new language?"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:210
+msgid "Refresh"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:244
+msgid "Invalid Password"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:245
+msgid "Your passwords don't match!"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:260
+msgid "Your old password was incorrect!"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:269
+msgid "Change Successful"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:270
+msgid "Your password was successfully changed!"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:13
+msgid "Add from Url"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:37
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:143
+msgid "Url"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:45
+msgid "Cookies"
+msgstr ""
+
+#: 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 ""
+
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:149
+msgid "Infohash"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/AddWindow.js:260
+msgid "Uploading your torrent..."
+msgstr ""
+
+#: 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 ""
+
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:50
+msgid "Move Completed Folder"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:85
+msgid "Max Down Speed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:93
+msgid "Max Up Speed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:125
+msgid "Add In Paused State"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:161
+msgid "Super Seed"
+msgstr ""
+
+#: deluge/ui/web/js/deluge-all/add/FilesTab.js:43
+msgid "Download"
+msgstr ""
+
+#: deluge/ui/web/render/tab_status.html:11
+msgid "ETA:"
+msgstr ""
+
+#: deluge/ui/web/render/tab_status.html:26
+msgid "Date Added:"
+msgstr ""
diff --git a/deluge/i18n/ms.po b/deluge/i18n/ms.po
index ff671ad3f..84b905e8c 100644
--- a/deluge/i18n/ms.po
+++ b/deluge/i18n/ms.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4471,16 +4471,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4489,7 +4489,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/nap.po b/deluge/i18n/nap.po
index dea9f4b8d..5c4ca92b9 100644
--- a/deluge/i18n/nap.po
+++ b/deluge/i18n/nap.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/nb.po b/deluge/i18n/nb.po
index 62e9a000f..a21eb7803 100644
--- a/deluge/i18n/nb.po
+++ b/deluge/i18n/nb.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4441,16 +4441,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4459,7 +4459,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/nds.po b/deluge/i18n/nds.po
index 9a4566fde..fd8d34f3e 100644
--- a/deluge/i18n/nds.po
+++ b/deluge/i18n/nds.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/nl.po b/deluge/i18n/nl.po
index 90932895f..85b19f68a 100644
--- a/deluge/i18n/nl.po
+++ b/deluge/i18n/nl.po
@@ -8,18 +8,18 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-06-06 10:57+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2021-12-23 05:43+0000\n"
+"Last-Translator: Sam van Kampen <sam@tehsvk.net>\n"
"Language-Team: Dutch <nl@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
-msgstr ""
+msgstr "B"
#: deluge/common.py:412
msgid "KiB"
@@ -35,7 +35,7 @@ msgstr "GiB"
#: deluge/common.py:415
msgid "TiB"
-msgstr ""
+msgstr "TiB"
#: deluge/common.py:416
msgid "K"
@@ -80,7 +80,7 @@ msgstr "KiB/s"
#: deluge/common.py:521
msgid "M/s"
-msgstr ""
+msgstr "M/s"
#: deluge/common.py:521
msgid "MiB/s"
@@ -88,7 +88,7 @@ msgstr "MiB/s"
#: deluge/common.py:527
msgid "G/s"
-msgstr ""
+msgstr "G/s"
#: deluge/common.py:527
msgid "GiB/s"
@@ -96,11 +96,11 @@ msgstr "GiB/s"
#: deluge/common.py:533
msgid "T/s"
-msgstr ""
+msgstr "T/s"
#: deluge/common.py:533
msgid "TiB/s"
-msgstr ""
+msgstr "TiB/s"
#: deluge/argparserbase.py:172
msgid "Common Options"
@@ -108,11 +108,11 @@ msgstr ""
#: deluge/argparserbase.py:175
msgid "Print this help message"
-msgstr ""
+msgstr "Dit Help-bericht afdrukken"
#: deluge/argparserbase.py:182
msgid "Print version information"
-msgstr ""
+msgstr "Versie-informatie afdrukken"
#: deluge/argparserbase.py:194
msgid "Set the config directory path"
@@ -278,12 +278,12 @@ msgstr "Grootte"
#: deluge/ui/common.py:55 deluge/ui/gtk3/torrentview.py:289
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:244
msgid "Downloaded"
-msgstr "Gedownloaded"
+msgstr "Binnengehaald"
#: deluge/ui/common.py:56 deluge/ui/gtk3/torrentview.py:296
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:252
msgid "Uploaded"
-msgstr "Geupload"
+msgstr "Geüpload"
#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
@@ -3901,17 +3901,17 @@ msgstr ""
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:28
#: deluge/ui/web/render/tab_status.html:10
msgid "Up Speed:"
-msgstr ""
+msgstr "Uploadsnelheid:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:3
#: deluge/ui/web/render/tab_status.html:2
msgid "Downloaded:"
-msgstr ""
+msgstr "Binnengehaald:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:4
#: deluge/ui/web/render/tab_status.html:3
msgid "Uploaded:"
-msgstr ""
+msgstr "Geüpload:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:5
#: deluge/ui/web/render/tab_status.html:16
@@ -4449,16 +4449,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4467,7 +4467,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/nn.po b/deluge/i18n/nn.po
index 72c93239c..618123cfa 100644
--- a/deluge/i18n/nn.po
+++ b/deluge/i18n/nn.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4424,16 +4424,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4442,7 +4442,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/oc.po b/deluge/i18n/oc.po
index 609c37239..3d8784808 100644
--- a/deluge/i18n/oc.po
+++ b/deluge/i18n/oc.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/pl.po b/deluge/i18n/pl.po
index 7a269f4ab..509c21862 100644
--- a/deluge/i18n/pl.po
+++ b/deluge/i18n/pl.po
@@ -8,18 +8,18 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-06-06 10:57+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2023-11-06 18:51+0000\n"
+"Last-Translator: Cas <Unknown>\n"
"Language-Team: Polish <pl@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
-msgstr ""
+msgstr "B"
#: deluge/common.py:412
msgid "KiB"
@@ -35,7 +35,7 @@ msgstr "GiB"
#: deluge/common.py:415
msgid "TiB"
-msgstr ""
+msgstr "TiB"
#: deluge/common.py:416
msgid "K"
@@ -51,7 +51,7 @@ msgstr "G"
#: deluge/common.py:419
msgid "T"
-msgstr ""
+msgstr "T"
#: deluge/common.py:515 deluge/ui/gtk3/statusbar.py:442
#: deluge/ui/gtk3/statusbar.py:455 deluge/ui/gtk3/statusbar.py:464
@@ -62,7 +62,7 @@ msgstr ""
#: deluge/ui/gtk3/systemtray.py:274 deluge/ui/gtk3/systemtray.py:442
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:40
msgid "K/s"
-msgstr ""
+msgstr "K/s"
#: deluge/common.py:515 deluge/ui/gtk3/menubar.py:449
#: deluge/ui/gtk3/menubar.py:455
@@ -80,7 +80,7 @@ msgstr "KiB/s"
#: deluge/common.py:521
msgid "M/s"
-msgstr ""
+msgstr "M/s"
#: deluge/common.py:521
msgid "MiB/s"
@@ -88,7 +88,7 @@ msgstr "MiB/s"
#: deluge/common.py:527
msgid "G/s"
-msgstr ""
+msgstr "G/s"
#: deluge/common.py:527
msgid "GiB/s"
@@ -96,11 +96,11 @@ msgstr "GiB/s"
#: deluge/common.py:533
msgid "T/s"
-msgstr ""
+msgstr "T/s"
#: deluge/common.py:533
msgid "TiB/s"
-msgstr ""
+msgstr "TiB/s"
#: deluge/argparserbase.py:172
msgid "Common Options"
@@ -198,36 +198,36 @@ msgstr "Wszystkie"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:534
#: deluge/ui/web/js/deluge-all/UI.js:19
msgid "Active"
-msgstr "Aktywny"
+msgstr "Aktywne"
#: deluge/ui/common.py:39 deluge/ui/web/js/deluge-all/UI.js:20
msgid "Allocating"
-msgstr ""
+msgstr "Alokowane"
#: deluge/ui/common.py:40 deluge/ui/web/js/deluge-all/UI.js:21
#: deluge/ui/web/js/deluge-all/UI.js:25
msgid "Checking"
-msgstr "Sprawdzanie"
+msgstr "Sprawdzane"
#: deluge/ui/common.py:41
#: deluge/ui/console/modes/preferences/preference_panes.py:568
#: deluge/ui/web/js/deluge-all/UI.js:22
msgid "Downloading"
-msgstr "Pobieranie"
+msgstr "Pobierane"
#: deluge/ui/common.py:42
#: deluge/ui/console/modes/preferences/preference_panes.py:575
#: deluge/ui/web/js/deluge-all/UI.js:23
msgid "Seeding"
-msgstr "Wysyłanie"
+msgstr "Wysyłane"
#: deluge/ui/common.py:43 deluge/ui/web/js/deluge-all/UI.js:24
msgid "Paused"
-msgstr "Pauza"
+msgstr "Wstrzymane"
#: deluge/ui/common.py:44 deluge/ui/web/js/deluge-all/UI.js:26
msgid "Queued"
-msgstr "W kolejce"
+msgstr "Zakolejkowane"
#: deluge/ui/common.py:45 deluge/ui/common.py:122
#: deluge/ui/gtk3/statusbar.py:396 deluge/ui/gtk3/filtertreeview.py:131
@@ -243,7 +243,7 @@ msgstr "W kolejce"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:291
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:316
msgid "Error"
-msgstr "Błąd"
+msgstr "Błędne"
#: deluge/ui/common.py:50 deluge/ui/gtk3/listview.py:793
#: deluge/ui/gtk3/torrentview.py:180 deluge/ui/gtk3/torrentview.py:276
@@ -288,26 +288,26 @@ msgstr "Wysłano"
#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
msgid "Remaining"
-msgstr ""
+msgstr "Pozostało"
#: deluge/ui/common.py:58 deluge/ui/gtk3/torrentview.py:373
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:165
msgid "Ratio"
-msgstr "Ratio"
+msgstr "Ułamek"
#: deluge/ui/common.py:59 deluge/ui/gtk3/torrentview.py:340
#: deluge/ui/gtk3/peers_tab.py:133
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:144
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:87
msgid "Down Speed"
-msgstr "Szybk. pobierania"
+msgstr "Szybkość pobierania"
#: deluge/ui/common.py:60 deluge/ui/gtk3/torrentview.py:346
#: deluge/ui/gtk3/peers_tab.py:146
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:151
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:94
msgid "Up Speed"
-msgstr "Szybk. wysyłania"
+msgstr "Szybkość wysyłania"
#: deluge/ui/common.py:61 deluge/ui/gtk3/torrentview.py:352
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:268
@@ -321,11 +321,11 @@ msgstr "Limit Wysyłania"
#: deluge/ui/common.py:63 deluge/ui/web/js/deluge-all/add/OptionsTab.js:101
msgid "Max Connections"
-msgstr ""
+msgstr "Maksymalnie Połączeń"
#: deluge/ui/common.py:64 deluge/ui/web/js/deluge-all/add/OptionsTab.js:109
msgid "Max Upload Slots"
-msgstr ""
+msgstr "Maksymalnie Slotów Wysyłania"
#: deluge/ui/common.py:65 deluge/ui/gtk3/torrentview.py:325
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:136
@@ -336,17 +336,17 @@ msgstr "Uczestnicy"
#: deluge/ui/common.py:66 deluge/ui/gtk3/torrentview.py:317
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:128
msgid "Seeds"
-msgstr ""
+msgstr "Posiadacze"
#: deluge/ui/common.py:67 deluge/ui/gtk3/torrentview.py:380
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:173
msgid "Avail"
-msgstr "Dost."
+msgstr "Dostępność"
#: deluge/ui/common.py:68 deluge/ui/gtk3/torrentview.py:333
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:284
msgid "Seeds:Peers"
-msgstr ""
+msgstr "Posiadaczy:Uczestników"
#: deluge/ui/common.py:69 deluge/ui/gtk3/listview.py:203
#: deluge/ui/gtk3/torrentview.py:387
@@ -367,33 +367,33 @@ msgstr "Tracker"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:213
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:31
msgid "Download Folder"
-msgstr ""
+msgstr "Folder pobrania"
#: deluge/ui/common.py:75
msgid "Seeding Time"
-msgstr ""
+msgstr "Razem wysyłano czasu"
#: deluge/ui/common.py:76
msgid "Active Time"
-msgstr ""
+msgstr "Razem aktywności czasu"
#: deluge/ui/common.py:78
msgid "Last Activity"
-msgstr ""
+msgstr "Ostatnia aktywność"
#: deluge/ui/common.py:81
msgid "Finished Time"
-msgstr ""
+msgstr "Zakończono o"
#: deluge/ui/common.py:83 deluge/ui/gtk3/torrentview.py:401
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:189
msgid "Complete Seen"
-msgstr ""
+msgstr "Ostatnio widziano cały o"
#: deluge/ui/common.py:86 deluge/ui/gtk3/torrentview.py:394
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:197
msgid "Completed"
-msgstr ""
+msgstr "Zakończono o"
#: deluge/ui/common.py:87 deluge/ui/gtk3/torrentview.py:366
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:158
@@ -404,7 +404,7 @@ msgstr "Pozostało"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:236
msgid "Shared"
-msgstr ""
+msgstr "Współdzielony"
#: deluge/ui/common.py:90 deluge/ui/gtk3/glade/main_window.tabs.ui.h:31
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:287
@@ -415,7 +415,7 @@ msgstr "Kolejkuj Pierwszy/Ostatni"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:14
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:143
msgid "Sequential Download"
-msgstr ""
+msgstr "Sekwencyjne pobieranie"
#: deluge/ui/common.py:97 deluge/ui/common.py:98
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:35
@@ -427,19 +427,19 @@ msgstr "Automatycznie zarządzany"
#: deluge/ui/common.py:99
msgid "Stop At Ratio"
-msgstr ""
+msgstr "Zatrzymaj na ułamku"
#: deluge/ui/common.py:100
msgid "Stop Ratio"
-msgstr ""
+msgstr "Ułamek do zatrzymania"
#: deluge/ui/common.py:101
msgid "Remove At Ratio"
-msgstr ""
+msgstr "Usuń na ułamku"
#: deluge/ui/common.py:102 deluge/ui/common.py:108
msgid "Move On Completed"
-msgstr ""
+msgstr "Przenieś po zakończeniu"
#: deluge/ui/common.py:104
msgid "Move Completed Path"
@@ -455,7 +455,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/FilterPanel.js:32
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:221
msgid "Owner"
-msgstr ""
+msgstr "Właściciel"
#: deluge/ui/common.py:116
msgid "Pieces"
@@ -463,13 +463,13 @@ msgstr "Fragmentów"
#: deluge/ui/common.py:117
msgid "Seed Rank"
-msgstr ""
+msgstr "Cyklów seedowania"
#: deluge/ui/common.py:118 deluge/ui/gtk3/glade/main_window.tabs.ui.h:33
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:22
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:294
msgid "Super Seeding"
-msgstr ""
+msgstr "Początkowe seedowanie"
#: deluge/ui/common.py:123 deluge/ui/web/js/deluge-all/details/StatusTab.js:122
msgid "Warning"
@@ -506,7 +506,7 @@ msgstr "Pobierane"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:21
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:73
msgid "Bandwidth"
-msgstr "Łącze"
+msgstr "Przepustowość"
#: deluge/ui/common.py:132
#: deluge/ui/console/modes/preferences/preference_panes.py:550
@@ -532,7 +532,7 @@ msgstr "Sieć"
#: deluge/ui/web/js/deluge-all/preferences/ProxyPage.js:21
#: deluge/ui/web/js/deluge-all/preferences/ProxyPage.js:35
msgid "Proxy"
-msgstr "Proxy"
+msgstr "Pośrednik sieciowy"
#: deluge/ui/common.py:135
#: deluge/ui/console/modes/preferences/preference_panes.py:685
@@ -567,22 +567,22 @@ msgstr "Wtyczki"
#: deluge/ui/common.py:150 deluge/ui/web/js/deluge-all/Deluge.js:154
#: deluge/ui/web/js/deluge-all/Menus.js:365
msgid "Skip"
-msgstr ""
+msgstr "Nie pobieraj"
#: deluge/ui/common.py:151 deluge/ui/web/js/deluge-all/Deluge.js:155
#: deluge/ui/web/js/deluge-all/Menus.js:371
msgid "Low"
-msgstr ""
+msgstr "Niski priorytet"
#: deluge/ui/common.py:152 deluge/ui/web/js/deluge-all/Deluge.js:156
#: deluge/ui/web/js/deluge-all/Menus.js:377
msgid "Normal"
-msgstr ""
+msgstr "Średni priorytet"
#: deluge/ui/common.py:153 deluge/ui/web/js/deluge-all/Deluge.js:157
#: deluge/ui/web/js/deluge-all/Menus.js:383
msgid "High"
-msgstr ""
+msgstr "Wysoki priorytet"
#: deluge/ui/client.py:681
msgid ""
@@ -4441,16 +4441,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4459,7 +4459,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/pms.po b/deluge/i18n/pms.po
index a670047f5..a72d65d86 100644
--- a/deluge/i18n/pms.po
+++ b/deluge/i18n/pms.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/pt.po b/deluge/i18n/pt.po
index 867dcf9fd..27ef213c7 100644
--- a/deluge/i18n/pt.po
+++ b/deluge/i18n/pt.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4464,16 +4464,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4482,7 +4482,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/pt_BR.po b/deluge/i18n/pt_BR.po
index b6a36de09..9850f3e3c 100644
--- a/deluge/i18n/pt_BR.po
+++ b/deluge/i18n/pt_BR.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4477,16 +4477,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4495,7 +4495,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ro.po b/deluge/i18n/ro.po
index 39df0414d..bf47fb10f 100644
--- a/deluge/i18n/ro.po
+++ b/deluge/i18n/ro.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4452,16 +4452,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4470,7 +4470,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/ru.po b/deluge/i18n/ru.po
index 6552c1d91..5142278b3 100644
--- a/deluge/i18n/ru.po
+++ b/deluge/i18n/ru.po
@@ -8,14 +8,16 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-12-03 13:05+0000\n"
+"PO-Revision-Date: 2022-09-09 17:58+0000\n"
"Last-Translator: adem <adem4ik@gmail.com>\n"
-"Language-Team: Russian <ru@li.org>\n"
+"Language-Team: Russian Launchpad Translators <lp-l10n-"
+"ru@lists.launchpad.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
+"Language: ru\n"
#: deluge/common.py:411
msgid "B"
@@ -53,6 +55,7 @@ msgstr "Г"
msgid "T"
msgstr "T"
+# ❗Это сокращение KiB/s, см. ф. fspeed в deluge/common.py
#: 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
@@ -108,11 +111,11 @@ msgstr "Общие параметры"
#: deluge/argparserbase.py:175
msgid "Print this help message"
-msgstr "Показывать это вспомогательное сообщение"
+msgstr "Показать эту справку"
#: deluge/argparserbase.py:182
msgid "Print version information"
-msgstr "Вывести сведения о версии"
+msgstr "Показать информацию о версии"
#: deluge/argparserbase.py:194
msgid "Set the config directory path"
@@ -120,7 +123,7 @@ msgstr "Установить путь каталога настроек"
#: deluge/argparserbase.py:200
msgid "Output to specified logfile instead of stdout"
-msgstr ""
+msgstr "Вывод в указанный файл журнала вместо stdout"
#: deluge/argparserbase.py:206
msgid "Set the log level (none, error, warning, info, debug)"
@@ -134,10 +137,12 @@ msgid ""
"Enable logfile rotation, with optional maximum logfile size, default: "
"%(const)s (Logfile rotation count is 5)"
msgstr ""
+"Включить ротацию журналов событий. Можно указать максимальный размер журнала "
+"(по умолчанию %(const)s). Количество журналов — 5."
#: deluge/argparserbase.py:223
msgid "Quieten logging output (Same as `--loglevel none`)"
-msgstr ""
+msgstr "Отключить журналирование событий (то же, что и `--loglevel none`)"
#: deluge/argparserbase.py:231
#, python-format
@@ -145,6 +150,8 @@ msgid ""
"Profile %(prog)s with cProfile. Outputs to stdout unless a filename is "
"specified"
msgstr ""
+"Профилировать %(prog)s с помощью cProfile. Вывод направляется в стандартный "
+"вывод, если не указано имя файла."
#: deluge/argparserbase.py:351
msgid "Process Control Options"
@@ -156,23 +163,26 @@ msgstr "Pid-файл для хранения ID процесса"
#: deluge/argparserbase.py:365
msgid "Do not daemonize (fork) this process"
-msgstr ""
+msgstr "Работать не в фоновом режиме (не выполнять системный вызов fork)"
#: deluge/argparserbase.py:379
msgid "Change to this user on startup (Requires root)"
-msgstr "Переключиться на этого пользователя при запуске (требует права root)"
+msgstr ""
+"Переключиться на этого пользователя при запуске (требуются права "
+"администратора)"
#: deluge/argparserbase.py:386
msgid "Change to this group on startup (Requires root)"
-msgstr "Переключиться на эту группу при запуске (требует права root)"
+msgstr ""
+"Переключиться на эту группу при запуске (требуются права администратора)"
#: deluge/core/daemon_entry.py:25
msgid "Daemon Options"
-msgstr "Параметры демона"
+msgstr "Параметры службы"
#: deluge/core/daemon_entry.py:31
msgid "IP address to listen for UI connections"
-msgstr "IP адресс, прослушиваемый для соединений с UI"
+msgstr "Прослушиваемый IP-адрес для соединений с интерфейсом"
#: deluge/core/daemon_entry.py:39
msgid "Port to listen for UI connections on"
@@ -180,7 +190,7 @@ msgstr "Порт, прослушиваемый для соединений с UI
#: deluge/core/daemon_entry.py:47
msgid "IP address to listen for BitTorrent connections"
-msgstr "IP адресс, прослушиваемый для соединений BitTorrent"
+msgstr "Прослушиваемый IP-адрес для соединений БитТоррент"
#: deluge/core/daemon_entry.py:56
msgid ""
@@ -191,6 +201,8 @@ msgstr ""
#: deluge/core/daemon_entry.py:63
msgid "Config keys to be unmodified by `set_config` RPC"
msgstr ""
+"Параметры конфигурации, которые не будут изменены при удалённом вызове "
+"`set_config`"
#: deluge/ui/common.py:37 deluge/ui/gtk3/filtertreeview.py:130
#: deluge/ui/web/js/deluge-all/UI.js:18
@@ -261,7 +273,7 @@ msgstr "Название"
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:80
#: deluge/ui/web/js/deluge-all/details/FilesTab.js:34
msgid "Progress"
-msgstr "Состояние"
+msgstr "Прогресс"
#: deluge/ui/common.py:52 deluge/ui/web/js/deluge-all/Sidebar.js:12
msgid "State"
@@ -286,7 +298,7 @@ msgstr "Загружено"
#: deluge/ui/common.py:56 deluge/ui/gtk3/torrentview.py:296
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:252
msgid "Uploaded"
-msgstr "Роздано"
+msgstr "Отдано"
#: deluge/ui/common.py:57 deluge/ui/gtk3/torrentview.py:303
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:260
@@ -310,31 +322,31 @@ msgstr "Загрузка"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:151
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:94
msgid "Up Speed"
-msgstr "Скорость раздачи"
+msgstr "Отдача"
#: deluge/ui/common.py:61 deluge/ui/gtk3/torrentview.py:352
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:268
msgid "Down Limit"
-msgstr "Нижнее ограничение"
+msgstr "Порог загрузки"
#: deluge/ui/common.py:62 deluge/ui/gtk3/torrentview.py:359
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:276
msgid "Up Limit"
-msgstr "Лимит скорости раздачи"
+msgstr "Порог отдачи"
#: deluge/ui/common.py:63 deluge/ui/web/js/deluge-all/add/OptionsTab.js:101
msgid "Max Connections"
-msgstr "Максимальное число соединений"
+msgstr "Максимум соединений"
#: deluge/ui/common.py:64 deluge/ui/web/js/deluge-all/add/OptionsTab.js:109
msgid "Max Upload Slots"
-msgstr "Максимальное число слотов отдачи"
+msgstr "Максимум слотов раздачи"
#: deluge/ui/common.py:65 deluge/ui/gtk3/torrentview.py:325
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:136
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:46
msgid "Peers"
-msgstr "Узлы"
+msgstr "Пиры"
#: deluge/ui/common.py:66 deluge/ui/gtk3/torrentview.py:317
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:128
@@ -401,7 +413,7 @@ msgstr "Завершено"
#: deluge/ui/common.py:87 deluge/ui/gtk3/torrentview.py:366
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:158
msgid "ETA"
-msgstr "Осталось"
+msgstr "Ост. время"
#: deluge/ui/common.py:88 deluge/ui/gtk3/torrentview.py:418
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:30
@@ -442,15 +454,15 @@ msgstr "Удалить при рейтинге"
#: deluge/ui/common.py:102 deluge/ui/common.py:108
msgid "Move On Completed"
-msgstr ""
+msgstr "Перемещение по завершению"
#: deluge/ui/common.py:104
msgid "Move Completed Path"
-msgstr ""
+msgstr "Путь для перемещения завершённых"
#: deluge/ui/common.py:112
msgid "Move On Completed Path"
-msgstr ""
+msgstr "Путь для перемещения завершённых"
#: deluge/ui/common.py:115 deluge/ui/gtk3/filtertreeview.py:135
#: deluge/ui/gtk3/torrentview.py:416
@@ -466,13 +478,13 @@ msgstr "Части"
#: deluge/ui/common.py:117
msgid "Seed Rank"
-msgstr "Ранг сида"
+msgstr "Ранг раздачи"
#: deluge/ui/common.py:118 deluge/ui/gtk3/glade/main_window.tabs.ui.h:33
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:22
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:294
msgid "Super Seeding"
-msgstr "Режим суперсида"
+msgstr "Супер-раздача"
#: deluge/ui/common.py:123 deluge/ui/web/js/deluge-all/details/StatusTab.js:122
msgid "Warning"
@@ -509,7 +521,7 @@ msgstr "Загрузки"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:21
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:73
msgid "Bandwidth"
-msgstr "Ограничения"
+msgstr "Полоса пропускания"
#: deluge/ui/common.py:132
#: deluge/ui/console/modes/preferences/preference_panes.py:550
@@ -519,7 +531,7 @@ msgstr "Ограничения"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:176
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:18
msgid "Queue"
-msgstr "Добавить в очередь"
+msgstr "Очередь"
#: deluge/ui/common.py:133
#: deluge/ui/console/modes/preferences/preference_panes.py:300
@@ -592,6 +604,8 @@ msgid ""
"Deluge cannot find the `deluged` executable, check that the deluged package "
"is installed, or added to your PATH."
msgstr ""
+"Deluge не может найти исполняемый файл `deluged`, проверьте, что пакет "
+"`deluged` установлен или добавьте его в PATH"
#: deluge/ui/countries.py:10
msgid "Afghanistan"
@@ -1067,7 +1081,7 @@ msgstr "Кувейт"
#: deluge/ui/countries.py:128
msgid "Kyrgyzstan"
-msgstr "Кыргызстан"
+msgstr "Киргизия"
#: deluge/ui/countries.py:129
msgid "Lao People's Democratic Republic"
@@ -1459,7 +1473,7 @@ msgstr "Таджикистан"
#: deluge/ui/countries.py:226
msgid "Tanzania, United Republic of"
-msgstr "Объединенная республика Танзания"
+msgstr "Объединённая Республика Танзания"
#: deluge/ui/countries.py:227
msgid "Thailand"
@@ -1523,7 +1537,7 @@ msgstr "Великобритания"
#: deluge/ui/countries.py:242
msgid "United States"
-msgstr "Соединенные Штаты Америки"
+msgstr "Соединённые Штаты Америки"
#: deluge/ui/countries.py:243
msgid "United States Minor Outlying Islands"
@@ -1584,6 +1598,8 @@ msgstr "Параметры интерфейса"
#: deluge/ui/ui_entry.py:57
msgid "Set the default UI to be run, when no UI is specified"
msgstr ""
+"Установить пользовательский интерфейс для запуска по умолчанию, когда "
+"пользовательский интерфейс не указан"
#: deluge/ui/ui_entry.py:91
msgid ""
@@ -1591,7 +1607,7 @@ msgid ""
" (default UI: *)"
msgstr ""
"Альтернативный интерфейс для запуска с необязательными аргументами \n"
-" (стандартный интерфейм: *)"
+" (стандартный интерфейс: *)"
#: deluge/ui/web/web.py:32
msgid "Web Server Options"
@@ -1599,15 +1615,16 @@ msgstr "Параметры веб-сервера"
#: deluge/ui/web/web.py:38
msgid "IP address for web server to listen on"
-msgstr ""
+msgstr "IP-адрес для прослушивания веб-сервером"
#: deluge/ui/web/web.py:46
msgid "Port for web server to listen on"
-msgstr ""
+msgstr "Порт для прослушивания веб-сервером"
#: deluge/ui/web/web.py:53
msgid "Set the base path that the ui is running on"
msgstr ""
+"Установить базовый путь, по которому работает пользовательский интерфейс"
#: deluge/ui/web/web.py:56
msgid "Force the web server to use SSL"
@@ -1619,7 +1636,7 @@ msgstr "Требовать отключение SSL от веб-сервера"
#: deluge/ui/web/json_api.py:868
msgid "Daemon does not exist"
-msgstr "Демон не существует"
+msgstr "Служба не существует"
#: deluge/ui/web/json_api.py:875
msgid "Daemon not running"
@@ -1692,7 +1709,7 @@ msgstr "_Сохранить"
#: deluge/ui/gtk3/createtorrentdialog.py:271
#: deluge/ui/gtk3/addtorrentdialog.py:712
msgid "Torrent files"
-msgstr "Торренты"
+msgstr "Торрент-файлы"
#: deluge/ui/gtk3/createtorrentdialog.py:275
#: deluge/ui/gtk3/addtorrentdialog.py:716
@@ -1724,7 +1741,7 @@ msgstr "Deluge"
#: deluge/ui/gtk3/path_combo_chooser.py:393
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:20
msgid "Edit path"
-msgstr "Редактировать путь"
+msgstr "Изменить путь"
#: deluge/ui/gtk3/path_combo_chooser.py:395
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:21
@@ -1746,8 +1763,8 @@ msgid ""
"A peer-to-peer file sharing program\n"
"utilizing the BitTorrent protocol."
msgstr ""
-"Программа для обмена данными по протоколу p2p\n"
-"с использованием протокола BitTorrent protocol."
+"Программа для обмена файлами\n"
+"по пиринговому протоколу БитТоррент"
#: deluge/ui/gtk3/aboutdialog.py:46
#: deluge/ui/web/js/deluge-all/AboutWindow.js:55
@@ -1760,7 +1777,7 @@ msgstr "Текущие разработчики:"
#: deluge/ui/gtk3/aboutdialog.py:61
msgid "Past Developers or Contributors:"
-msgstr "Прошлые разработчики и контрибуторы:"
+msgstr "Прошлые разработчики и участники:"
#: deluge/ui/gtk3/aboutdialog.py:795
msgid ""
@@ -1789,9 +1806,9 @@ msgid ""
"also delete it here."
msgstr ""
"Эта программа является свободным программным обеспечением; Вы можете "
-"распространять и/или изменять ее согласно правилам лицензии GNU General "
+"распространять и/или изменять её согласно правилам лицензии GNU General "
"Public License, опубликованной Фондом Свободного Программного обеспечения; "
-"как под версийей 3 лицензии, так и (по желанию) под более поздней. \n"
+"как под версией 3 лицензии, так и (по желанию) под более поздней. \n"
"\n"
"Эта программа распространяется в надежде что будет полезной, но БЕЗ КАКИХ-"
"ЛИБО ГАРАНТИЙ; даже без подразумеваемых гарантий КОММЕРЧЕСКОЙ ЦЕННОСТИ или "
@@ -1810,7 +1827,7 @@ msgstr ""
"исключение для вашей версии файла(ов), но вы не обязаны это делать. Если вы "
"не хотите сделать это, удалите это заявление об исключении из вашей версии. "
"Если вы удалите это заявление об исключении из всех исходных файлы "
-"программы, то так же удалите ее."
+"программы, то так же удалите её."
#: deluge/ui/gtk3/aboutdialog.py:829
#: deluge/ui/web/js/deluge-all/AboutWindow.js:65
@@ -1839,7 +1856,7 @@ msgstr "Дублировать торрент(ы)"
#, python-format
msgid ""
"You cannot add the same torrent twice. %d torrents were already added."
-msgstr ""
+msgstr "Вы не можете добавить этот торрент дважды. %d торренты уже добавлены"
#: deluge/ui/gtk3/addtorrentdialog.py:255
msgid "Invalid File"
@@ -1864,7 +1881,7 @@ msgstr "Неверный URL"
#: deluge/ui/gtk3/addtorrentdialog.py:778
msgid "is not a valid URL."
-msgstr "является некорректным URL."
+msgstr "не является верным адресом."
#: deluge/ui/gtk3/addtorrentdialog.py:784
msgid "Downloading..."
@@ -1934,7 +1951,7 @@ msgstr "Пароль:"
#: deluge/ui/gtk3/dialogs.py:257
msgid "Edit Account"
-msgstr "Редактировать учётную запись"
+msgstr "Править учётную запись"
#: deluge/ui/gtk3/dialogs.py:258
msgid "Edit existing account"
@@ -1993,7 +2010,7 @@ msgstr "Прочее..."
#: deluge/ui/gtk3/statusbar.py:155 deluge/ui/web/js/deluge-all/Statusbar.js:18
msgid "Not Connected"
-msgstr "Не подключен"
+msgstr "Не подключён"
#: deluge/ui/gtk3/statusbar.py:175
msgid "Connections (Limit)"
@@ -2009,7 +2026,7 @@ msgstr "Скорость отдачи (порог)"
#: deluge/ui/gtk3/statusbar.py:196
msgid "Protocol Traffic (Down:Up)"
-msgstr ""
+msgstr "Трафик по протоколу (загружено/отдано)"
#: deluge/ui/gtk3/statusbar.py:201 deluge/ui/web/js/deluge-all/Statusbar.js:234
msgid "DHT Nodes"
@@ -2026,7 +2043,7 @@ msgstr "Внешний адрес IP"
#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:409
#, python-format
msgid "<b>IP</b> <small>%s</small>"
-msgstr ""
+msgstr "<b>IP</b> <small>%s</small>"
#: deluge/ui/gtk3/statusbar.py:213 deluge/ui/gtk3/statusbar.py:408
#: deluge/ui/console/widgets/statusbars.py:121
@@ -2036,11 +2053,11 @@ msgstr "н/д"
#: deluge/ui/gtk3/statusbar.py:220
msgid "<b><small>Port Issue</small></b>"
-msgstr ""
+msgstr "<b><small>Проблема с портом</small></b>"
#: deluge/ui/gtk3/statusbar.py:222
msgid "No incoming connections, check port forwarding"
-msgstr ""
+msgstr "Нет входящих соединений, проверьте перенаправление порта"
#: deluge/ui/gtk3/statusbar.py:475 deluge/ui/gtk3/systemtray.py:394
#: deluge/ui/gtk3/menubar.py:447
@@ -2060,7 +2077,7 @@ msgstr "Ограничение скорости отдачи"
#: deluge/ui/gtk3/statusbar.py:483 deluge/ui/gtk3/systemtray.py:410
#: deluge/ui/gtk3/menubar.py:454
msgid "Set the maximum upload speed"
-msgstr "Установить максимальную скорость отдачи"
+msgstr "Установить максимальную скорость ротдачи"
#: deluge/ui/gtk3/statusbar.py:489 deluge/ui/gtk3/menubar.py:459
msgid "Incoming Connections"
@@ -2068,7 +2085,7 @@ msgstr "Входящие соединения"
#: deluge/ui/gtk3/statusbar.py:490 deluge/ui/gtk3/menubar.py:460
msgid "Set the maximum incoming connections"
-msgstr "Установить максимальное число входящих соединений"
+msgstr "Установить максимум входящих соединений"
#: deluge/ui/gtk3/tab_data_funcs.py:28
#, python-brace-format
@@ -2078,13 +2095,13 @@ msgstr "{state} {percent} %"
#: deluge/ui/gtk3/tab_data_funcs.py:30
#, python-brace-format
msgid "{state}: {err_msg}"
-msgstr ""
+msgstr "{state}: {err_msg}"
#: deluge/ui/gtk3/tab_data_funcs.py:42
#: deluge/ui/gtk3/torrentview_data_funcs.py:284
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:74
msgid "Never"
-msgstr ""
+msgstr "Никогда"
#: deluge/ui/gtk3/tab_data_funcs.py:96
msgid "Yes"
@@ -2117,12 +2134,12 @@ msgstr "_Подробности"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:27
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:9
msgid "Fi_les"
-msgstr "Файлы"
+msgstr "Фай_лы"
#: deluge/ui/gtk3/torrentdetails.py:146
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:28
msgid "_Peers"
-msgstr "_Узлы"
+msgstr "_Пиры"
#: deluge/ui/gtk3/torrentdetails.py:147
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:45
@@ -2133,7 +2150,7 @@ msgstr "_Параметры"
#: deluge/ui/gtk3/torrentdetails.py:148
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:52
msgid "_Trackers"
-msgstr ""
+msgstr "_Трекеры"
#: deluge/ui/gtk3/systemtray.py:184
msgid "Not Connected..."
@@ -2174,26 +2191,35 @@ msgid ""
"A Deluge daemon (deluged) is already running.\n"
"To use Standalone mode, stop local daemon and restart Deluge."
msgstr ""
+"Служба Deluge (deluged) уже запущена.\n"
+"Для использования автономного режима, остановите локальную службу и "
+"перезапустите Deluge."
#: 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 ""
+"Доступен только режим тонкого клиента, потому что libtorrent не установлен.\n"
+"Для использования автономного режима, пожалуйста установите пакет libtorrent."
#: 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 ""
+"Доступен только режим тонкого клиента из-за неизвестной ошибки импорта "
+"(Import Error).\n"
+"Для использования автономного режима смотрите подробности об ошибке в "
+"журнале событий."
#: deluge/ui/gtk3/gtkui.py:349
msgid "Continue in Thin Client mode?"
-msgstr ""
+msgstr "Продолжить в режиме тонкого клиента?"
#: deluge/ui/gtk3/gtkui.py:350
msgid "Change User Interface Mode"
-msgstr ""
+msgstr "Изменить режим пользовательского интерфейса"
#: deluge/ui/gtk3/connectionmanager.py:52
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:56
@@ -2208,7 +2234,7 @@ msgstr "В сети"
#: deluge/ui/gtk3/connectionmanager.py:54
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:59
msgid "Connected"
-msgstr "Подключен"
+msgstr "Подключено"
#: deluge/ui/gtk3/connectionmanager.py:110
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:176
@@ -2220,7 +2246,7 @@ msgstr "Статус"
#: deluge/ui/gtk3/connectionmanager.py:115
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:66
msgid "Host"
-msgstr "Сервер"
+msgstr "Хост"
#: deluge/ui/gtk3/connectionmanager.py:122
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:73
@@ -2230,41 +2256,45 @@ msgstr "Версия"
#: deluge/ui/gtk3/connectionmanager.py:219
#: deluge/ui/gtk3/glade/connection_manager.ui.h:8
msgid "_Start Daemon"
-msgstr "_Запустить демон"
+msgstr "З_апустить службу"
#: deluge/ui/gtk3/connectionmanager.py:250
msgid "_Stop Daemon"
-msgstr "_Остановить демон"
+msgstr "О_становить службу"
#: deluge/ui/gtk3/connectionmanager.py:255
msgid "_Disconnect"
-msgstr ""
+msgstr "_Отсоединить"
#: deluge/ui/gtk3/connectionmanager.py:280
msgid "Unable to start daemon!"
-msgstr "Не удается запустить демон."
+msgstr "Не удаётся запустить службу!"
#: deluge/ui/gtk3/connectionmanager.py:281
msgid "Check deluged package is installed and logs for further details"
msgstr ""
+"Проверьте установлен ли пакет deluged и журналы для получения дополнительной "
+"информации"
#: deluge/ui/gtk3/connectionmanager.py:332
msgid "Incompatible Client"
-msgstr ""
+msgstr "Несовместимый клиент"
#: deluge/ui/gtk3/connectionmanager.py:343
msgid ""
"Auto-starting the daemon locally is not enabled. See \"Options\" on the "
"\"Connection Manager\"."
msgstr ""
+"Автозапуск локальной службы отключён. Смотрите «Параметры запуска» в "
+"«Управлении подключениями»."
#: deluge/ui/gtk3/connectionmanager.py:346
msgid "Failed To Connect"
-msgstr ""
+msgstr "Не удалось подключиться"
#: deluge/ui/gtk3/connectionmanager.py:403
msgid "Edit Host"
-msgstr ""
+msgstr "Изменить хост"
#: deluge/ui/gtk3/connectionmanager.py:428
msgid "Error Adding Host"
@@ -2272,7 +2302,7 @@ msgstr "Ошибка при добавлении узла"
#: deluge/ui/gtk3/connectionmanager.py:464
msgid "Error Updating Host"
-msgstr ""
+msgstr "Ошибка обновления хоста"
#: deluge/ui/gtk3/preferences.py:131
#: deluge/ui/console/cmdline/commands/connect.py:33
@@ -2303,33 +2333,35 @@ msgstr "Модуль"
#: deluge/ui/gtk3/preferences.py:876 deluge/ui/gtk3/preferences.py:886
msgid "Attention"
-msgstr ""
+msgstr "Внимание"
#: deluge/ui/gtk3/preferences.py:876
msgid "You must choose a language"
-msgstr ""
+msgstr "Необходимо выбрать язык"
#: deluge/ui/gtk3/preferences.py:887
msgid "You must now restart the deluge UI for the changes to take effect."
msgstr ""
+"Необходимо перезапустить пользовательский интерфейс deluge для того, чтобы "
+"изменения вступили в силу."
#: deluge/ui/gtk3/preferences.py:940
msgid "Thinclient"
-msgstr ""
+msgstr "Тонкий клиент"
#: deluge/ui/gtk3/preferences.py:940
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:18
msgid "Standalone"
-msgstr ""
+msgstr "Автономный"
#: deluge/ui/gtk3/preferences.py:942
msgid "Switching Deluge Client Mode..."
-msgstr ""
+msgstr "Переключение клиентского режима Deluge..."
#: deluge/ui/gtk3/preferences.py:943
#, python-format
msgid "Do you want to restart to use %s mode?"
-msgstr ""
+msgstr "Хотите перезапустить deluge, чтобы использовать режим «%s»?"
#: deluge/ui/gtk3/preferences.py:1154
msgid "Select the Plugin"
@@ -2337,39 +2369,39 @@ msgstr "Выберите модуль"
#: deluge/ui/gtk3/preferences.py:1170
msgid "Plugin Eggs"
-msgstr "Модули Egg"
+msgstr "Дополнение Eggs"
#: deluge/ui/gtk3/preferences.py:1297
msgid "Server Side Error"
-msgstr ""
+msgstr "Ошибка на стороне сервера"
#: deluge/ui/gtk3/preferences.py:1298
msgid "An error occurred on the server"
-msgstr ""
+msgstr "На сервере произошла ошибка"
#: deluge/ui/gtk3/preferences.py:1368 deluge/ui/gtk3/preferences.py:1375
msgid "Error Adding Account"
-msgstr ""
+msgstr "Ошибка при добавлении аккаунта"
#: deluge/ui/gtk3/preferences.py:1369
msgid "Authentication failed"
-msgstr ""
+msgstr "Сбой аутентификации"
#: deluge/ui/gtk3/preferences.py:1376
msgid "An error occurred while adding account"
-msgstr ""
+msgstr "Произошла ошибка при добавлении аккаунта"
#: deluge/ui/gtk3/preferences.py:1408
msgid "Error Updating Account"
-msgstr ""
+msgstr "Ошибка обновления аккаунта"
#: deluge/ui/gtk3/preferences.py:1409
msgid "An error occurred while updating account"
-msgstr ""
+msgstr "Произошла ошибка при обновлении аккаунта"
#: deluge/ui/gtk3/preferences.py:1427
msgid "Remove Account"
-msgstr ""
+msgstr "Удалить аккаунт"
#: deluge/ui/gtk3/preferences.py:1429
#, python-format
@@ -2377,18 +2409,19 @@ msgid ""
"Are you sure you want to remove the account with the username "
"\"%(username)s\"?"
msgstr ""
+"Действительно удалить учётную запись с именем пользователя «%(username)s»?"
#: deluge/ui/gtk3/preferences.py:1441 deluge/ui/gtk3/preferences.py:1448
msgid "Error Removing Account"
-msgstr ""
+msgstr "Ошибка удаления аккаунта"
#: deluge/ui/gtk3/preferences.py:1442
msgid "Auhentication failed"
-msgstr ""
+msgstr "Сбой аутентификации"
#: deluge/ui/gtk3/preferences.py:1449
msgid "An error occurred while removing account"
-msgstr ""
+msgstr "Произошла ошибка при удалении учётной записи"
#: deluge/ui/gtk3/filtertreeview.py:122
#: deluge/ui/web/js/deluge-all/FilterPanel.js:28
@@ -2406,7 +2439,7 @@ msgstr "Трекеры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:7
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:33
msgid "None"
-msgstr "Отсутствует"
+msgstr "Нет"
#: deluge/ui/gtk3/filtertreeview.py:137
msgid "Admin"
@@ -2424,7 +2457,7 @@ msgstr "Без метки"
#: deluge/ui/gtk3/filtertreeview.py:206
msgid "No Owner"
-msgstr ""
+msgstr "Нет владельца"
#: deluge/ui/gtk3/new_release_dialog.py:60
msgid "<i>Client Version</i>"
@@ -2440,16 +2473,16 @@ msgstr " Задание помещено в очередь"
#: deluge/ui/gtk3/torrentview.py:421
msgid "Torrent is shared between other Deluge users or not."
-msgstr ""
+msgstr "Торрент общий для других пользователей Deluge или нет."
#: deluge/ui/gtk3/removetorrentdialog.py:67
msgid "Remove the selected torrents?"
-msgstr ""
+msgstr "Удалить выбранные торренты?"
#: deluge/ui/gtk3/removetorrentdialog.py:68
#, python-format
msgid "Total of %s torrents selected"
-msgstr ""
+msgstr "Выбрано торрентов — %s"
#: deluge/ui/gtk3/menubar.py:79
msgid "Set Unlimited"
@@ -2461,7 +2494,7 @@ msgstr "Вкл."
#: deluge/ui/gtk3/menubar.py:94 deluge/ui/web/js/deluge-all/Menus.js:265
msgid "Off"
-msgstr "Выкл."
+msgstr "Откл."
#: deluge/ui/gtk3/menubar.py:101
msgid "Disable"
@@ -2469,27 +2502,27 @@ msgstr "Отключить"
#: deluge/ui/gtk3/menubar.py:104
msgid "Enable..."
-msgstr ""
+msgstr "Включить..."
#: deluge/ui/gtk3/menubar.py:465
msgid "Peer Upload Slots"
-msgstr ""
+msgstr "Слоты отдачи для пира"
#: deluge/ui/gtk3/menubar.py:466
msgid "Set the maximum upload slots"
-msgstr ""
+msgstr "Установить максимум слотов раздачи"
#: deluge/ui/gtk3/menubar.py:471
msgid "Stop Seed At Ratio"
-msgstr ""
+msgstr "Остановить раздачу при рейтинге"
#: deluge/ui/gtk3/menubar.py:606
msgid "Ownership Change Error"
-msgstr ""
+msgstr "Ошибка смены владельца"
#: deluge/ui/gtk3/menubar.py:607
msgid "There was an error while trying changing ownership."
-msgstr ""
+msgstr "Произошла ошибка при смене владельца"
#: deluge/ui/gtk3/peers_tab.py:91
#: deluge/ui/web/js/deluge-all/details/PeersTab.js:66
@@ -2503,13 +2536,15 @@ msgstr "Клиент"
#: deluge/ui/gtk3/__init__.py:29
msgid "GTK Options"
-msgstr ""
+msgstr "Параметры GTK"
#: deluge/ui/gtk3/__init__.py:36
msgid ""
"Add one or more torrent files, torrent URLs or magnet URIs to a currently "
"running Deluge GTK instance"
msgstr ""
+"Добавить один или несколько торрент-файлов, ссылки на торренты или магнет-"
+"ссылки в запущенный экземпляр Deluge GTK"
#: deluge/ui/gtk3/glade/create_torrent_dialog.progress.ui.h:1
msgid "Creating Torrent"
@@ -2521,7 +2556,7 @@ msgstr "Задания в очереди"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:4
msgid "Add Queued Torrents"
-msgstr ""
+msgstr "Добавить торренты в очередь"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:5
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:7
@@ -2533,7 +2568,7 @@ msgstr "_Удалить"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:6
msgid "_Clear"
-msgstr ""
+msgstr "_Очистить"
#: deluge/ui/gtk3/glade/queuedtorrents.ui.h:7
msgid "Automatically add torrents on connect"
@@ -2556,7 +2591,7 @@ msgstr "_Создать торрент"
#: deluge/ui/gtk3/glade/main_window.ui.h:4
msgid "Quit & _Shutdown Daemon"
-msgstr "Выйти и _выключить демон"
+msgstr "Выйти и _остановить службу"
#: deluge/ui/gtk3/glade/main_window.ui.h:5
#: deluge/ui/gtk3/glade/tray_menu.ui.h:8
@@ -2572,7 +2607,7 @@ msgstr "_Правка"
#: deluge/ui/gtk3/glade/main_window.ui.h:7
msgid "_Preferences"
-msgstr ""
+msgstr "_Параметры"
#: deluge/ui/gtk3/glade/main_window.ui.h:8
msgid "_Connection Manager"
@@ -2580,7 +2615,7 @@ msgstr "Управление _подключениями"
#: deluge/ui/gtk3/glade/main_window.ui.h:9
msgid "_Torrent"
-msgstr "_Задание"
+msgstr "Т_оррент"
#: deluge/ui/gtk3/glade/main_window.ui.h:10
msgid "_View"
@@ -2608,7 +2643,7 @@ msgstr "_Колонки"
#: deluge/ui/gtk3/glade/main_window.ui.h:16
msgid "_Find ..."
-msgstr ""
+msgstr "_Найти..."
#: deluge/ui/gtk3/glade/main_window.ui.h:17
msgid "S_idebar"
@@ -2624,7 +2659,7 @@ msgstr "Показывать _трекеры"
#: deluge/ui/gtk3/glade/main_window.ui.h:20
msgid "Show _Owners"
-msgstr ""
+msgstr "Показывать _владельцев"
#: deluge/ui/gtk3/glade/main_window.ui.h:21
msgid "_Help"
@@ -2636,7 +2671,7 @@ msgstr "_Домашняя страница"
#: deluge/ui/gtk3/glade/main_window.ui.h:23
msgid "_FAQ"
-msgstr "_FAQ"
+msgstr "_Частые вопросы"
#: deluge/ui/gtk3/glade/main_window.ui.h:24
msgid "Frequently Asked Questions"
@@ -2648,7 +2683,7 @@ msgstr "_Сообщество"
#: deluge/ui/gtk3/glade/main_window.ui.h:26
msgid "_About"
-msgstr ""
+msgstr "_О программе"
#: deluge/ui/gtk3/glade/main_window.ui.h:27
msgid "Add torrent"
@@ -2677,14 +2712,16 @@ msgid ""
"Filter torrents by name.\n"
"This will filter torrents for the current selection on the sidebar."
msgstr ""
+"Отфильтровать торренты по имени.\n"
+"Это отфильтрует торренты, выбранные на боковой панели."
#: deluge/ui/gtk3/glade/main_window.ui.h:33
msgid "Filter"
-msgstr ""
+msgstr "Фильтр"
#: deluge/ui/gtk3/glade/main_window.ui.h:34
msgid "Pause the selected torrents"
-msgstr "Приостановить выбранные задания"
+msgstr "Приостановить выбранные торренты"
#: deluge/ui/gtk3/glade/main_window.ui.h:35
#: deluge/ui/web/js/deluge-all/Toolbar.js:54
@@ -2694,29 +2731,29 @@ msgstr "Приостановить"
#: deluge/ui/gtk3/glade/main_window.ui.h:36
msgid "Resume the selected torrents"
-msgstr "Продолжить выбранные задания"
+msgstr "Возобновить выбранные торренты"
#: deluge/ui/gtk3/glade/main_window.ui.h:37
#: deluge/ui/web/js/deluge-all/Toolbar.js:61
#: deluge/ui/web/js/deluge-all/Menus.js:59
msgid "Resume"
-msgstr "Продолжить"
+msgstr "Возобновить"
#: deluge/ui/gtk3/glade/main_window.ui.h:38
msgid "Queue Torrent Up"
-msgstr "На задание вперёд"
+msgstr "Переместить торрент вперёд"
#: deluge/ui/gtk3/glade/main_window.ui.h:39
msgid "Queue Up"
-msgstr "В начало очереди"
+msgstr "К началу очереди"
#: deluge/ui/gtk3/glade/main_window.ui.h:40
msgid "Queue Torrent Down"
-msgstr "На задание назад"
+msgstr "Переместить торрент назад"
#: deluge/ui/gtk3/glade/main_window.ui.h:41
msgid "Queue Down"
-msgstr "В конец очереди"
+msgstr "К концу очереди"
#: deluge/ui/gtk3/glade/main_window.ui.h:42
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:14
@@ -2741,19 +2778,19 @@ msgstr "Управление подключениями"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:211
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:86
msgid "Close"
-msgstr ""
+msgstr "Закрыть"
#: deluge/ui/gtk3/glade/main_window.ui.h:45
msgid "Filter:"
-msgstr ""
+msgstr "Фильтр:"
#: deluge/ui/gtk3/glade/main_window.ui.h:46
msgid "Clear the search"
-msgstr ""
+msgstr "Очистить поиск"
#: deluge/ui/gtk3/glade/main_window.ui.h:47
msgid "_Match Case"
-msgstr ""
+msgstr "У_читывать регистр"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:1
#: deluge/ui/console/modes/preferences/preference_panes.py:383
@@ -2761,7 +2798,7 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:45
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:66
msgid "Forced"
-msgstr "Форсированный"
+msgstr "Принудительно"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:3
#: deluge/ui/console/modes/preferences/preference_panes.py:383
@@ -2769,7 +2806,7 @@ msgstr "Форсированный"
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:47
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:68
msgid "Disabled"
-msgstr "Откл."
+msgstr "Отключено"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:4
#: deluge/ui/console/modes/preferences/preference_panes.py:400
@@ -2781,7 +2818,7 @@ msgstr "Рукопожатие"
#: deluge/ui/console/modes/preferences/preference_panes.py:400
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:88
msgid "Full Stream"
-msgstr ""
+msgstr "Весь поток"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:6
#: deluge/ui/console/modes/preferences/preference_panes.py:400
@@ -2792,17 +2829,17 @@ msgstr "Оба"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:8
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:34
msgid "Socks4"
-msgstr ""
+msgstr "Socks4"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:9
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:35
msgid "Socks5"
-msgstr ""
+msgstr "Socks5"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:10
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:36
msgid "Socks5 Auth"
-msgstr ""
+msgstr "Socks5 Аутентификация"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:11
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:37
@@ -2812,28 +2849,28 @@ msgstr "HTTP"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:12
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:38
msgid "HTTP Auth"
-msgstr ""
+msgstr "HTTP-аутентификация"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:13
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:39
msgid "I2P"
-msgstr ""
+msgstr "I2P"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:19
msgid "The standalone self-contained application"
-msgstr ""
+msgstr "Автономное приложение"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:20
msgid "Thin Client"
-msgstr ""
+msgstr "Тонкий клиент"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:21
msgid "Connect to a Deluge daemon (deluged)"
-msgstr ""
+msgstr "Подключаться к службе Deluge (deluged)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:22
msgid "Application Mode"
-msgstr ""
+msgstr "Режим приложения"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:46
@@ -2850,45 +2887,48 @@ msgid ""
"will increase bandwidth use between client\n"
"and daemon (does not apply in Standalone mode)."
msgstr ""
+"Панель частей\n"
+"увеличит использование канала между клиентом\n"
+"и службой (это не относится к автономному режиму)."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:28
msgid "Show a pieces bar in Status tab"
-msgstr ""
+msgstr "Показывать панель частей на вкладке «Состояние»"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:29
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:25
#: deluge/ui/web/render/tab_status.html:27
msgid "Completed:"
-msgstr ""
+msgstr "Завершён:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:30
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:72
msgid "Downloading:"
-msgstr ""
+msgstr "Загружается:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:31
msgid "Waiting:"
-msgstr ""
+msgstr "Ожидающие:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:32
msgid "Missing:"
-msgstr ""
+msgstr "Отсутствующие:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:33
msgid "_Revert"
-msgstr ""
+msgstr "_Восстановить"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:34
msgid "Revert color to default"
-msgstr ""
+msgstr "Восстановить цвет по умолчанию"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:35
msgid "Piece Colors"
-msgstr ""
+msgstr "Цвета частей"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:36
msgid "Main Window"
-msgstr ""
+msgstr "Главное окно"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:37
msgid "Enable system tray icon"
@@ -2896,11 +2936,11 @@ msgstr "Показывать значок в области уведомлени
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:38
msgid "App Indicator"
-msgstr ""
+msgstr "App Indicator"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:39
msgid "Systray"
-msgstr ""
+msgstr "Системный трей"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:40
msgid "Minimize to tray on close"
@@ -2916,25 +2956,25 @@ msgstr "Защитить паролем значок в области увед
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:44
msgid "System Tray"
-msgstr ""
+msgstr "Системный трей"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:45
msgid "Notify about new releases"
-msgstr ""
+msgstr "Уведомлять о новых выпусках"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:46
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:38
msgid "Updates"
-msgstr ""
+msgstr "Обновления"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:47
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:235
msgid "System Default"
-msgstr ""
+msgstr "Системный по умолчанию"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:48
msgid "<b>Language</b>"
-msgstr ""
+msgstr "<b>Язык</b>"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:49
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:16
@@ -2966,7 +3006,7 @@ msgstr "Загружать в:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:54
msgid "Download Folders"
-msgstr ""
+msgstr "Папки для загрузок"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:55
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:93
@@ -2983,7 +3023,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:287
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:102
msgid "Sequential download"
-msgstr ""
+msgstr "Последовательная загрузка"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:58
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:15
@@ -2995,24 +3035,30 @@ msgid ""
"distribution negatively in the swarm. It should be\n"
"used sparingly."
msgstr ""
+"Когда включено, части будут выбираться\n"
+"последовательно, а не сначала самые редкие.\n"
+"\n"
+"Последовательная загрузка отрицательно влияет\n"
+"на распространение частей в раздаче. Её следует\n"
+"использовать в редких случаях."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:64
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:111
msgid "Add torrents in Paused state"
-msgstr "Добавлять задания приостановленными"
+msgstr "Добавлять торренты в приостановленном состоянии"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:65
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:120
msgid "Pre-allocate disk space"
-msgstr ""
+msgstr "Резервировать место на диске"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:66
msgid "Pre-allocate the disk space for the torrent files"
-msgstr ""
+msgstr "Резервировать место на диске для загружаемых файлов"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:67
msgid "Add Torrent Options"
-msgstr ""
+msgstr "Добавление торрента"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:68
msgid "Always show"
@@ -3024,19 +3070,19 @@ msgstr "Сделать диалоговое окно активным"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:70
msgid "Add Torrents Dialog"
-msgstr ""
+msgstr "Диалоговое окно «Добавить торренты»"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:71
msgid "Connection Attempts per Second:"
-msgstr ""
+msgstr "Попыток соединения в секунду:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:72
msgid "Half-Open Connections:"
-msgstr ""
+msgstr "Полуоткрытые соединения:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:73
msgid "The maximum number of connections allowed. Set -1 for unlimited."
-msgstr "Максимально допустимое число соединений. -1 означает неограниченное."
+msgstr "Максимум допустимых соединений. «-1» — неограниченно."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:74
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:42
@@ -3048,7 +3094,7 @@ msgstr "Соединения:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:75
msgid "The maximum upload slots for all torrents. Set -1 for unlimited."
-msgstr "Лимит числа слотов раздачи. -1 означает без ограничений."
+msgstr "Максимум допустимых соединений отдачи. «-1» — неограниченно."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:76
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:43
@@ -3070,14 +3116,13 @@ msgstr "Скорость загрузки:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:79
msgid "The maximum upload speed for all torrents. Set -1 for unlimited."
-msgstr ""
-"Лимит скорости раздачи для всех загрузок. -1 означает без ограничений."
+msgstr "Максимум скорости отдачи для всех торрентов. «-1» — без ограничений."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:81
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:39
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:6
msgid "Upload Speed:"
-msgstr "Скорость раздачи:"
+msgstr "Скорость отдачи:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:82
#: deluge/ui/console/modes/preferences/preference_panes.py:458
@@ -3100,11 +3145,11 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:85
msgid "Global Bandwidth Limits"
-msgstr ""
+msgstr "Глобальные ограничения пропускной способности"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:86
msgid "The maximum upload slots per torrent. Set -1 for unlimited."
-msgstr "Лимит числа слотов раздачи на загрузку. -1 означает без ограничений."
+msgstr "Максимум слотов отдачи на торрент. «-1» — без ограничений."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:87
msgid "The maximum number of connections per torrent. Set -1 for unlimited."
@@ -3114,62 +3159,66 @@ msgstr ""
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:88
msgid "The maximum number download speed per torrent. Set -1 for unlimited."
msgstr ""
+"Максимальная скорость загрузки для торрента. Установите -1 для "
+"неограниченной."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:89
msgid "The maximum upload speed per torrent. Set -1 for unlimited."
-msgstr "Лимит скорости раздачи на загрузку. -1 означает без ограничений."
+msgstr "Максимум скорости отдачи на торрент. «-1»  — без ограничений."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:90
msgid "Per-Torrent Bandwidth Limits"
-msgstr ""
+msgstr "Ограничения пропускной способности для торрента"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:91
#: deluge/ui/console/modes/preferences/preference_panes.py:556
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:42
msgid "Queue to top"
-msgstr ""
+msgstr "Добавлять в начало очереди"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:92
#: deluge/ui/console/modes/preferences/preference_panes.py:554
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:30
msgid "New Torrents"
-msgstr ""
+msgstr "Новые торренты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:93
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:85
msgid "Seeding:"
-msgstr ""
+msgstr "Раздачи:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:94
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:59
msgid "Total:"
-msgstr ""
+msgstr "Всего:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:95
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:102
msgid "Ignore slow torrents"
-msgstr ""
+msgstr "Игнорировать медленные торренты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:96
msgid ""
"Torrents not transfering any data do not count towards download/seeding "
"active count."
msgstr ""
+"Торренты, не передающие данные, не учитываются в количестве активных "
+"загрузок и раздач."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:97
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:111
msgid "Prefer seeding torrents"
-msgstr ""
+msgstr "Приоритет раздающих торрентов"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:98
msgid "Give preference to seeding torrents over downloading torrents."
-msgstr ""
+msgstr "Предпочесть раздающие торренты вместо скачиваемых."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:99
#: deluge/ui/console/modes/preferences/preference_panes.py:558
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:50
msgid "Active Torrents"
-msgstr ""
+msgstr "Активные торренты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:100
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:7
@@ -3177,44 +3226,46 @@ msgstr ""
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:187
#: deluge/ui/web/render/tab_status.html:4
msgid "Share Ratio:"
-msgstr ""
+msgstr "Рейтинг раздачи:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:101
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:142
msgid "Time Ratio:"
-msgstr ""
+msgstr "Коэффициент времени:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:102
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:157
msgid "Time (m):"
-msgstr ""
+msgstr "Длительность (мин):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:103
#: deluge/ui/console/modes/preferences/preference_panes.py:590
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:118
msgid "Seeding Rotation"
-msgstr ""
+msgstr "Чередование раздачи"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:104
msgid "Pause Torrent"
-msgstr ""
+msgstr "Приостановить торрент"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:106
#: deluge/ui/console/modes/preferences/preference_panes.py:627
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:173
msgid "Share Ratio Reached"
-msgstr ""
+msgstr "Рейтинг раздачи достигнут"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:107
msgid ""
"The IP address of the interface to listen for incoming bittorrent "
"connections on. Leave this empty if you want to use the default."
msgstr ""
+"IP-адрес интерфейса для прослушивания входящих bittorrent соединений. "
+"Оставьте это поле пустым, если хотите использовать значение по умолчанию."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:108
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:38
msgid "Incoming Address"
-msgstr ""
+msgstr "Входящий адрес"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:109
msgid "Random"
@@ -3222,7 +3273,7 @@ msgstr "Случайно"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:110
msgid "Uses random ports in range 49152 to 65525"
-msgstr ""
+msgstr "Использует случайные порты из диапазона от 49152 до 65525"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:111
msgid "Active Port:"
@@ -3235,7 +3286,7 @@ msgstr "Проверить активный порт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:113
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:58
msgid "Incoming Port"
-msgstr ""
+msgstr "Входящий порт"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:114
msgid ""
@@ -3244,12 +3295,16 @@ msgid ""
"connections. (Leave empty for default.)\n"
" "
msgstr ""
+"\n"
+"Имя сетевого интерфейса или IP-адрес для исходящих BitTorrent соединений. "
+"(Оставьте пустым для значений по умолчанию.)\n"
+" "
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:117
#: deluge/ui/console/modes/preferences/preference_panes.py:359
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:101
msgid "Outgoing Interface"
-msgstr ""
+msgstr "Исходящий интерфейс"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:118
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:11
@@ -3266,17 +3321,17 @@ msgstr "По:"
#: deluge/ui/console/modes/preferences/preference_panes.py:328
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:120
msgid "Outgoing Ports"
-msgstr ""
+msgstr "Исходящие порты"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:121
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:59
msgid "Outgoing:"
-msgstr ""
+msgstr "Исходящее:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:122
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:38
msgid "Incoming:"
-msgstr ""
+msgstr "Входящее:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:123
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:80
@@ -3314,7 +3369,7 @@ msgstr "Обмен узлами"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:130
msgid "Exchanges peers between clients. (Disabling requires restart)"
-msgstr ""
+msgstr "Обмен участниками между клиентами. (Отключение требует перезапуска)"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:131
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:223
@@ -3323,7 +3378,9 @@ msgstr "LSD"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:132
msgid "Local Service Discovery finds local peers on your network."
-msgstr "Local Service Discovery находит узлы в местной сети."
+msgstr ""
+"Обнаружение локальных служб (Local Service Discovery) находит участников в "
+"локальной сети."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:133
#: deluge/ui/console/widgets/statusbars.py:118
@@ -3346,7 +3403,7 @@ msgstr "Байт узла:"
#: deluge/ui/console/modes/preferences/preference_panes.py:372
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:181
msgid "Network Extras"
-msgstr ""
+msgstr "Дополнительные сетевые параметры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:137
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:4
@@ -3370,55 +3427,59 @@ msgstr "Порт:"
#: deluge/ui/console/modes/preferences/preference_panes.py:658
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:94
msgid "Proxy Hostnames"
-msgstr ""
+msgstr "Использовать прокси для имён хостов"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:141
msgid ""
"Hostnames should be attempted to be resolved through\n"
"the proxy instead of using the local DNS service"
msgstr ""
+"Имена хостов следует пытаться разрешать через прокси\n"
+"вместо использования локальной службы DNS."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:143
#: deluge/ui/console/modes/preferences/preference_panes.py:661
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:103
msgid "Proxy Peers"
-msgstr ""
+msgstr "Использовать прокси для пиров"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:144
msgid "Proxy peer and web seed connections."
-msgstr ""
+msgstr "Использовать прокси для соединений с узлами и веб-раздачами."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:145
#: deluge/ui/console/modes/preferences/preference_panes.py:665
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:112
msgid "Proxy Trackers"
-msgstr ""
+msgstr "Использовать прокси для трекеров"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:147
msgid "Force Proxy Use"
-msgstr ""
+msgstr "Использовать прокси принудительно"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:148
#: deluge/ui/console/modes/preferences/preference_panes.py:671
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:141
msgid "Hide Client Identity"
-msgstr ""
+msgstr "Скрывать данные клиента"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:149
msgid ""
"Attempt to hide client identity and only use proxy for incoming connections."
msgstr ""
+"Попытаться скрыть данные клиента и использовать прокси только для входящих "
+"соединений."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:150
#: deluge/ui/console/modes/preferences/preference_panes.py:668
#: deluge/ui/console/modes/preferences/preference_panes.py:669
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:120
msgid "Force Proxy"
-msgstr ""
+msgstr "Прокси принудительно"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:151
msgid "Cache Size (16 KiB blocks):"
-msgstr "Размер кэша (в блоках по 16 КБ):"
+msgstr "Размер кэша (блоков по 16 КиБ):"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:152
msgid ""
@@ -3440,14 +3501,14 @@ msgstr "Время жизни кэша (секунд):"
#: deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js:29
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:30
msgid "Settings"
-msgstr "Настройки"
+msgstr "Параметры"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:155
msgid ""
"The total number of 16 KiB blocks written to disk since this session was "
"started."
msgstr ""
-"Количество блоков по 16 КБ, записанных на диск с момента начала сессии."
+"Количество блоков по 16 КиБ, записанных на диск с начала текущего сеанса."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:156
msgid "Blocks Written:"
@@ -3457,9 +3518,7 @@ msgstr "Блоков записано:"
msgid ""
"The total number of write operations performed since this session was "
"started."
-msgstr ""
-"Общее количество выполненных операций записи с момента запуска текущей "
-"сессии."
+msgstr "Общее число операций записи, выполненных с начала текущего сеанса."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:158
msgid "Writes:"
@@ -3471,25 +3530,25 @@ msgid ""
"of saved write operations per total write operations, i.e. a kind of cache "
"hit ratio for the write cache."
msgstr ""
-"Соотношение (блоков_записано - операций_записи) / блоков_записано "
-"представляет отношение количества сохраненных операций записи к их общему "
-"количеству, т.е. эффективность кэша записи."
+"Соотношение «(блоков_записано - операций_записи) / блоков_записано» "
+"представляет собой отношение количества сэкономленных операций записи к их "
+"общему количеству, т. е. коэффициент эффективности кэша записи."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:160
msgid "Write Cache Hit Ratio:"
-msgstr "Процент попаданий в кэш:"
+msgstr "Коэффициент эффективности кэша записи:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:161
#: deluge/ui/console/modes/preferences/preference_panes.py:709
msgid "Write"
-msgstr ""
+msgstr "Запись"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:162
msgid ""
"The number of blocks that were requested from the bittorrent engine (from "
"peers), that were served from disk or cache."
msgstr ""
-"Количество блоков, запрошенных у движка BitTorrent (от узлов), полученных с "
+"Количество блоков, запрошенных у движка BitTorrent (от узлов) и полученных с "
"диска или из кэша."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:163
@@ -3510,13 +3569,13 @@ msgstr "Коэффициент эффективности кэша чтения.
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:167
msgid "Read Cache Hit Ratio:"
-msgstr "Процент чтения из кэша:"
+msgstr "Коэффициент эффективности кэша чтения:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:168
msgid ""
"The total number of read operations performed since this session was started."
msgstr ""
-"Общее количество операций чтения, выполненных с начала данной сессии."
+"Общее количество операций чтения, выполненных с начала текущего сеанса."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:169
msgid "Reads:"
@@ -3525,15 +3584,15 @@ msgstr "Операций чтения:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:170
#: deluge/ui/console/modes/preferences/preference_panes.py:723
msgid "Read"
-msgstr ""
+msgstr "Чтение"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:171
msgid ""
"The number of 16 KiB blocks currently in the disk cache. This includes both "
"read and write cache."
msgstr ""
-"Количество блоков по 16 КБ, находящихся сейчас в дисковом кэше. Включает кэш "
-"чтения и записи."
+"Количество блоков по 16 КиБ, находящихся сейчас в дисковом кэше. Включает "
+"кэш чтения и записи."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:172
msgid "Cache Size:"
@@ -3546,16 +3605,16 @@ msgstr "Размер кэша чтения:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:175
#: deluge/ui/gtk3/glade/connection_manager.ui.h:7
msgid "_Refresh"
-msgstr ""
+msgstr "О_бновить"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:177
msgid ""
"Help us improve Deluge by sending us your Python version, PyGTK version, OS "
"and processor types. Absolutely no other information is sent."
msgstr ""
-"Помогите нам сделать Deluge лучше, автоматически отправляя отчёт о версии "
-"используемых Python, PyGTK, ОС и типе процессора. Никакая другая информация "
-"не отсылается."
+"Помогите нам сделать Deluge лучше, отправляя сведения об используемых "
+"версиях Python, PyGTK, а также о типах ОС и процессора. Никакая другая "
+"информация не отправляется."
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:178
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:77
@@ -3566,7 +3625,7 @@ msgstr "Отправлять анонимную статистику"
#: deluge/ui/console/modes/preferences/preference_panes.py:503
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:57
msgid "System Information"
-msgstr ""
+msgstr "Информация о системе"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:180
msgid "Location:"
@@ -3584,15 +3643,15 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:516
#: deluge/ui/web/js/deluge-all/preferences/OtherPage.js:85
msgid "GeoIP Database"
-msgstr ""
+msgstr "База данных GeoIP"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:183
msgid "Associate with Deluge"
-msgstr ""
+msgstr "Ассоциировать с Deluge"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:184
msgid "Magnet Links"
-msgstr ""
+msgstr "Магнет-ссылки"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:185
#: deluge/ui/web/js/deluge-all/preferences/DaemonPage.js:37
@@ -3625,11 +3684,11 @@ msgstr "Периодически проверять веб-сайт на нал
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:193
msgid "_Delete"
-msgstr ""
+msgstr "_Удалить"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:194
msgid "Accounts"
-msgstr ""
+msgstr "Учётные записи"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:196
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:29
@@ -3659,23 +3718,23 @@ msgstr "Информация"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:201
msgid "_Install"
-msgstr ""
+msgstr "_Установить"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:202
msgid "_Find More..."
-msgstr ""
+msgstr "_Найти ещё на веб-сайте Deluge..."
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:4
msgid "Remove the selected torrent(s)?"
-msgstr ""
+msgstr "Удалить выбранный торрент(ы)?"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:5
msgid "Include downloaded files"
-msgstr ""
+msgstr "Включая загруженные файлы"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:6
msgid "(This is permanent!)"
-msgstr ""
+msgstr "(Это необратимо!)"
#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:1
msgid "Add Peer"
@@ -3687,11 +3746,11 @@ msgstr "имя_узла:порт"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:1
msgid "Properties"
-msgstr ""
+msgstr "Свойства"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:3
msgid "Max drop down rows"
-msgstr ""
+msgstr "Макс. количество строк в выпадающем списке"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:4
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:2
@@ -3701,87 +3760,87 @@ msgstr "<b>Общие</b>"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:5
msgid "Show path entry"
-msgstr ""
+msgstr "Показывать поле для ввода пути"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:6
msgid "Show file chooser"
-msgstr ""
+msgstr "Показывать кнопку для выбора папки"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:7
msgid "Show folder name"
-msgstr ""
+msgstr "Показывать название папки"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:8
msgid "Path Chooser Type"
-msgstr ""
+msgstr "Тип выбора пути"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:9
msgid "Enable autocomplete"
-msgstr ""
+msgstr "Использовать автодополнение"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:10
msgid "Show hidden files"
-msgstr ""
+msgstr "Показывать скрытые файлы"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:11
msgid "Set new key"
-msgstr ""
+msgstr "Назначить комбинацию"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:12
msgid "Press this key to set new key accelerators to trigger auto-complete"
-msgstr ""
+msgstr "Нажмите для назначения новой комбинации клавиш для автодополнения"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:13
msgid "Autocomplete"
-msgstr ""
+msgstr "Автодополнение"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:14
msgid "Save path"
-msgstr ""
+msgstr "Сохранить путь"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:15
msgid "Ctrl+S"
-msgstr ""
+msgstr "Ctrl+S"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:16
msgid "Ctrl+E"
-msgstr ""
+msgstr "Ctrl+E"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:17
msgid "Ctrl+R"
-msgstr ""
+msgstr "Ctrl+R"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:18
msgid "Ctrl+H"
-msgstr ""
+msgstr "Ctrl+H"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:19
msgid "Ctrl+D"
-msgstr ""
+msgstr "Ctrl+D"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:22
msgid "Toggle hidden files"
-msgstr ""
+msgstr "Показать скрытые файлы"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:23
msgid "Default path"
-msgstr ""
+msgstr "Путь по умолчанию"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:24
msgid "Shortcuts"
-msgstr ""
+msgstr "Комбинации клавиш"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:25
msgid "Select a Directory"
-msgstr ""
+msgstr "Выберите папку"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:26
msgid "Saved paths"
-msgstr ""
+msgstr "Сохранённые пути"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:27
msgid "column"
-msgstr ""
+msgstr "столбец"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:29
#: deluge/ui/console/modes/preferences/preferences.py:145
@@ -3797,7 +3856,7 @@ msgstr "Отменить"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:30
msgid "Open"
-msgstr ""
+msgstr "Открыть"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:31
#: deluge/ui/web/js/deluge-all/Toolbar.js:39
@@ -3812,18 +3871,18 @@ msgstr "Добавить"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:32
msgid "Add the current entry value to the list"
-msgstr ""
+msgstr "Добавить путь из поля ввода в список"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:33
#: deluge/ui/web/js/deluge-all/EditTrackersWindow.js:98
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:33
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:102
msgid "Edit"
-msgstr ""
+msgstr "Изменить"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:34
msgid "Edit the selected entry"
-msgstr ""
+msgstr "Изменить выбранный путь"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:35
#: deluge/ui/web/js/deluge-all/Toolbar.js:46
@@ -3835,27 +3894,27 @@ msgstr "Удалить"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:36
msgid "Remove the selected entry"
-msgstr ""
+msgstr "Удалить выбранный путь"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:38
msgid "Move the selected entry up"
-msgstr ""
+msgstr "Переместить выбранный путь выше"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:40
msgid "Move the selected entry down"
-msgstr ""
+msgstr "Переместить выбранный путь ниже"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:41
msgid "Default"
-msgstr ""
+msgstr "По умолчанию"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:42
msgid "No default path set"
-msgstr ""
+msgstr "Путь по умолчанию не задан"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:43
msgid "Open properties dialog"
-msgstr ""
+msgstr "Открыть диалоговое окно «Свойства»"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:1
msgid "Add Infohash"
@@ -3863,7 +3922,7 @@ msgstr "Добавить хэш данных"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
msgid "From Infohash"
-msgstr ""
+msgstr "По хешу данных"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
msgid "Infohash:"
@@ -3878,17 +3937,17 @@ msgstr "Трекеры:"
#: deluge/ui/gtk3/glade/connection_manager.addhost.ui.h:1
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Add Host"
-msgstr "Добавить сервер"
+msgstr "Добавить хост"
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:1
#: deluge/ui/web/js/deluge-all/MoveStorage.js:16
#: deluge/ui/web/js/deluge-all/Menus.js:346
msgid "Move Download Folder"
-msgstr ""
+msgstr "Изменить папку загрузки"
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:4
msgid "Move the torrent(s) download folder."
-msgstr ""
+msgstr "Изменить папку загрузки торрента(ов)."
#: deluge/ui/gtk3/glade/move_storage_dialog.ui.h:5
msgid "Destination:"
@@ -3904,19 +3963,19 @@ msgstr "_Перейти на веб-сайт"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:4
msgid "New Release Available!"
-msgstr ""
+msgstr "Доступна новая версия!"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:5
msgid "Available Version:"
-msgstr ""
+msgstr "Доступная версия:"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:6
msgid "Server Version"
-msgstr ""
+msgstr "Версия сервера"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:7
msgid "Current Version:"
-msgstr ""
+msgstr "Текущая версия:"
#: deluge/ui/gtk3/glade/main_window.new_release.ui.h:8
msgid "Do not show this dialog in the future"
@@ -3926,108 +3985,108 @@ msgstr "Больше не показывать это окно"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:26
#: deluge/ui/web/render/tab_status.html:9
msgid "Down Speed:"
-msgstr ""
+msgstr "Скорость загрузки:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:2
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:28
#: deluge/ui/web/render/tab_status.html:10
msgid "Up Speed:"
-msgstr ""
+msgstr "Скорость отдачи:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:3
#: deluge/ui/web/render/tab_status.html:2
msgid "Downloaded:"
-msgstr ""
+msgstr "Загружено:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:4
#: deluge/ui/web/render/tab_status.html:3
msgid "Uploaded:"
-msgstr ""
+msgstr "Отдано:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:5
#: deluge/ui/web/render/tab_status.html:16
msgid "Seeds:"
-msgstr ""
+msgstr "Сиды:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:6
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:10
#: deluge/ui/web/render/tab_status.html:17
msgid "Peers:"
-msgstr ""
+msgstr "Пиры:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:8
#: deluge/ui/web/render/tab_status.html:18
msgid "Availability:"
-msgstr ""
+msgstr "Доступно:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:9
#: deluge/ui/web/render/tab_status.html:25
msgid "Seed Rank:"
-msgstr ""
+msgstr "Ранг раздачи:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:10
msgid "ETA Time:"
-msgstr ""
+msgstr "Оставшееся время:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:11
#: deluge/ui/web/render/tab_status.html:13
msgid "Last Transfer:"
-msgstr ""
+msgstr "Последняя активность:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:12
#: deluge/ui/web/render/tab_status.html:23
msgid "Active Time:"
-msgstr ""
+msgstr "Время активности:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:13
#: deluge/ui/web/render/tab_status.html:20
msgid "Complete Seen:"
-msgstr ""
+msgstr "Замечен целиком:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:14
#: deluge/ui/web/render/tab_status.html:24
msgid "Seeding Time:"
-msgstr ""
+msgstr "Время раздачи:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:16
#: deluge/ui/web/render/tab_status.html:12
msgid "Pieces:"
-msgstr ""
+msgstr "Частей:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:17
#: deluge/plugins/Label/deluge_label/data/label_add.ui.h:3
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:23
msgid "Name:"
-msgstr "Имя:"
+msgstr "Название:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:18
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:25
msgid "Download Folder:"
-msgstr ""
+msgstr "Папка загрузки:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:19
msgid "Added:"
-msgstr ""
+msgstr "Добавлен:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:20
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:26
msgid "Total Size:"
-msgstr ""
+msgstr "Общий размер:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:21
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:27
msgid "Total Files:"
-msgstr ""
+msgstr "Файлов:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:22
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:24
msgid "Hash:"
-msgstr ""
+msgstr "Хеш:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:23
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:31
msgid "Created By:"
-msgstr ""
+msgstr "Создан в:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:24
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:17
@@ -4036,7 +4095,7 @@ msgstr "Комментарии:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:29
msgid "Owner:"
-msgstr ""
+msgstr "Владелец:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:34
msgid "Move completed:"
@@ -4047,40 +4106,40 @@ msgstr "Перемещать завершённые"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:12
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:205
msgid "Stop seed at ratio:"
-msgstr "Остановить раздачу при рейтинге:"
+msgstr "Останавливать раздачу при рейтинге:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:37
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:40
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:13
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:233
msgid "Remove at ratio"
-msgstr "Удалить при рейтинге"
+msgstr "Удалять при рейтинге"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:44
msgid "Bandwidth Limits"
-msgstr ""
+msgstr "Ограничения пропускной способности"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:46
msgid "Current Tracker:"
-msgstr ""
+msgstr "Текущий трекер:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:47
msgid "Total Trackers:"
-msgstr ""
+msgstr "Всего трекеров:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:48
#: deluge/ui/web/render/tab_status.html:6
msgid "Tracker Status:"
-msgstr ""
+msgstr "Статус трекера:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:49
#: deluge/ui/web/render/tab_status.html:5
msgid "Next Announce:"
-msgstr ""
+msgstr "Следующий анонс:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:50
msgid "Private Torrent:"
-msgstr ""
+msgstr "Приватный торрент:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:51
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:8
@@ -4091,7 +4150,7 @@ msgstr "_Изменить трекеры"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:44
#: deluge/ui/web/js/deluge-all/Menus.js:284
msgid "Top"
-msgstr "Вверх"
+msgstr "В начало"
#: deluge/ui/gtk3/glade/torrent_menu.queue.ui.h:4
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:45
@@ -4102,7 +4161,7 @@ msgstr "В конец"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:1
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:50
msgid "Add Torrents"
-msgstr "Добавить задания"
+msgstr "Добавить торренты"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:5
msgid "_URL"
@@ -4110,11 +4169,11 @@ msgstr "_Ссылка"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:6
msgid "Info_hash"
-msgstr "Хеш _данных"
+msgstr "_Хеш данных"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:11
msgid "Move Complete Folder"
-msgstr ""
+msgstr "Папка для завершённых загрузок"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:12
msgid "Add In _Paused State"
@@ -4129,32 +4188,32 @@ msgstr "Приоритет у первой/последней частей"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:46
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:152
msgid "Skip File Hash Check"
-msgstr ""
+msgstr "Пропустить проверку хеша"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:23
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:170
msgid "Preallocate Disk Space"
-msgstr ""
+msgstr "Резервировать место на диске"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:24
msgid "Preallocate the disk space for the torrent files"
-msgstr ""
+msgstr "Резервировать место на диске для загружаемых файлов"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:25
msgid "Maximum torrent download speed"
-msgstr ""
+msgstr "Максимальная скорость загрузки торрентов"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:27
msgid "Maximum torrent upload speed"
-msgstr ""
+msgstr "Максимальная скорость раздачи торрентов"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:29
msgid "Maximum torrent connections"
-msgstr ""
+msgstr "Максимальное количество соединений для торрента"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:31
msgid "Maximum torrent upload slots"
-msgstr ""
+msgstr "Максимальное количество слотов раздачи для торрента"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:34
msgid "Apply To All"
@@ -4162,7 +4221,7 @@ msgstr "Применить ко всем"
#: deluge/ui/gtk3/glade/add_torrent_dialog.ui.h:35
msgid "Revert To Defaults"
-msgstr "Вернуться к настройкам по умолчанию"
+msgstr "Восстановить параметры по умолчанию"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:1
msgid "_Show Deluge"
@@ -4170,11 +4229,11 @@ msgstr "_Показать Deluge"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:3
msgid "_Pause Session"
-msgstr ""
+msgstr "П_риостановить сеанс"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:4
msgid "_Resume Session"
-msgstr ""
+msgstr "_Возобновить сеанс"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:5
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:1
@@ -4184,7 +4243,7 @@ msgstr "Ограничение скорости _загрузки"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:6
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:2
msgid "_Upload Speed Limit"
-msgstr "_Oграничение скорости раздачи"
+msgstr "_Oграничение скорости отдачи"
#: deluge/ui/gtk3/glade/tray_menu.ui.h:7
msgid "Quit & Shutdown Daemon"
@@ -4200,16 +4259,16 @@ msgstr "Изменить трекеры"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:4
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:19
msgid "_Up"
-msgstr ""
+msgstr "_Вверх"
#: deluge/ui/gtk3/glade/edit_trackers.ui.h:8
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:22
msgid "_Down"
-msgstr ""
+msgstr "В_низ"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:1
msgid "_Add Peer"
-msgstr "_Добавить пира"
+msgstr "_Добавить узел"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_peer.ui.h:2
msgid "Add a peer by its IP"
@@ -4232,7 +4291,7 @@ msgstr "Введите удалённый путь"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:4
msgid "Remote Path"
-msgstr ""
+msgstr "Удалённый путь"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui.h:5
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:5
@@ -4242,43 +4301,43 @@ msgstr "Путь:"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:1
msgid "32 KiB"
-msgstr ""
+msgstr "32 КиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:2
msgid "64 KiB"
-msgstr ""
+msgstr "64 КиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:3
msgid "128 KiB"
-msgstr ""
+msgstr "128 КиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:4
msgid "256 KiB"
-msgstr ""
+msgstr "256 КиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:5
msgid "512 KiB"
-msgstr ""
+msgstr "512 КиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:6
msgid "1 MiB"
-msgstr ""
+msgstr "1 МиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:7
msgid "2 MiB"
-msgstr ""
+msgstr "2 МиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:8
msgid "4 MiB"
-msgstr ""
+msgstr "4 МиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:9
msgid "8 MiB"
-msgstr ""
+msgstr "8 МиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:10
msgid "16 MiB"
-msgstr ""
+msgstr "16 МиБ"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:11
msgid "Create Torrent"
@@ -4301,7 +4360,7 @@ msgstr "Файлы"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:24
msgid "Webseeds"
-msgstr "Веб-ресурсы"
+msgstr "Веб-сиды"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:25
msgid "Piece Size:"
@@ -4313,7 +4372,7 @@ msgstr "Установить флаг приватности"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:27
msgid "Add this torrent to the session"
-msgstr "Добавить задание к выполняющимся"
+msgstr "Добавить торрент в очередь"
#: deluge/ui/gtk3/glade/create_torrent_dialog.ui.h:28
#: deluge/ui/console/modes/preferences/preference_panes.py:279
@@ -4323,7 +4382,7 @@ msgstr "Добавить задание к выполняющимся"
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:80
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:17
msgid "Options"
-msgstr "Настройки"
+msgstr "Параметры"
#: deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui.h:1
msgid "Save .torrent as"
@@ -4331,11 +4390,11 @@ msgstr "Сохранить .torrent как"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:1
msgid "_Open Download Folder"
-msgstr ""
+msgstr "_Открыть папку загрузки"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:2
msgid "_Pause"
-msgstr "_Пауза"
+msgstr "_Приостановить"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:3
msgid "Resu_me"
@@ -4344,31 +4403,31 @@ msgstr "_Возобновить"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:4
#: deluge/ui/gtk3/glade/filtertree_menu.ui.h:4
msgid "Resume selected torrents."
-msgstr "Возобновить выбранные задания."
+msgstr "Возобновить выбранные торренты."
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:5
msgid "Opt_ions"
-msgstr "Параметры"
+msgstr "П_араметры"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:6
msgid "_Queue"
-msgstr "_Очередь"
+msgstr "О_чередь"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:7
msgid "_Update Tracker"
-msgstr "_Обновить трекер"
+msgstr "О_бновить трекер"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:9
msgid "_Remove Torrent"
-msgstr "_Удалить задание"
+msgstr "_Удалить торрент"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:10
msgid "_Force Re-check"
-msgstr "_Перепроверить данные"
+msgstr "П_ерепроверить данные"
#: deluge/ui/gtk3/glade/torrent_menu.ui.h:11
msgid "_Move Download Folder"
-msgstr ""
+msgstr "И_зменить папку загрузки"
#: deluge/ui/gtk3/glade/other_dialog.ui.h:3
msgid "label"
@@ -4396,7 +4455,7 @@ msgstr "Ограничение слотов раз_дачи"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:5
msgid "Stop seed at _ratio"
-msgstr ""
+msgstr "О_становить раздачу при рейтинге"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:6
msgid "_Auto Managed"
@@ -4404,11 +4463,11 @@ msgstr "_Автоматическое управление"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:7
msgid "_Super Seeding"
-msgstr ""
+msgstr "С_упер-раздача"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:8
msgid "_Change Ownership"
-msgstr ""
+msgstr "С_менить владельца"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:1
#: deluge/ui/web/js/deluge-all/AddTrackerWindow.js:26
@@ -4417,15 +4476,15 @@ msgstr "Добавить трекер"
#: deluge/ui/gtk3/glade/edit_trackers.add.ui.h:4
msgid "Add Trackers"
-msgstr ""
+msgstr "Добавить трекеры"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:1
msgid "Add URL"
-msgstr "Добавить URL"
+msgstr "Добавить ссылку"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:4
msgid "From URL"
-msgstr ""
+msgstr "Ссылка"
#: deluge/ui/gtk3/glade/add_torrent_dialog.url.ui.h:5
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:1
@@ -4434,31 +4493,31 @@ msgstr "Ссылка:"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:9
msgid "Deluge Daemons"
-msgstr ""
+msgstr "Службы Deluge"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:10
msgid "Auto-connect to selected Daemon"
-msgstr ""
+msgstr "Автоматически подключаться к выбранной службе"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:11
msgid "Auto-start localhost daemon (if required)"
-msgstr ""
+msgstr "Автоматически запускать локальную службу, если она ещё не запущена"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:12
msgid "Hide this dialog"
-msgstr ""
+msgstr "Не показывать это диалоговое окно"
#: deluge/ui/gtk3/glade/connection_manager.ui.h:13
msgid "Startup Options"
-msgstr ""
+msgstr "Параметры запуска"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:1
msgid "_Open File"
-msgstr ""
+msgstr "_Открыть файл"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:2
msgid "_Show Folder"
-msgstr ""
+msgstr "_Показать папку"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:3
msgid "_Expand All"
@@ -4466,30 +4525,32 @@ msgstr "_Развернуть всё"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:4
msgid "_Skip"
-msgstr ""
+msgstr "П_ропустить"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:5
msgid "_Low"
-msgstr ""
+msgstr "_Низкий"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:6
msgid "_Normal"
-msgstr ""
+msgstr "О_бычный"
#: deluge/ui/gtk3/glade/main_window.tabs.menu_file.ui.h:7
msgid "_High"
-msgstr ""
+msgstr "_Высокий"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
-msgstr ""
+msgstr "Команда Deluge"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
+"Deluge — небольшой свободный кросс-платформенный клиент файлообменной сети "
+"BitTorrent."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4497,14 +4558,25 @@ msgid ""
"Deluge heavily utilises the libtorrent library it has a comprehensive list "
"of the features provided."
msgstr ""
+"Deluge предоставляет общие возможности для клиентов сети BitTorrent, такие "
+"как шифрование протокола, DHT, поиск участников в локальной сети (LPD), "
+"обмен участниками (PEX), UPnP, NAT-PMP, поддержка прокси, веб-раздачи, "
+"ограничение скорости глобально и для отдельных торрентов. Deluge активно "
+"использует библиотеку libtorrent, поэтому он имеет обширный набор "
+"возможностей."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
"handles all the BitTorrent activity and is able to run on headless machines "
"with the user-interfaces connecting remotely from any other platform."
msgstr ""
+"Deluge спроектирован для работы как в качестве обычного автономного "
+"настольного приложения, так и в качестве клиент-сервера. В режиме тонкого "
+"клиента служба Deluge обрабатывает всю активность в сети BitTorrent и может "
+"работать на машинах без мониторов с пользовательскими интерфейсами, "
+"подключающимися удалённо с любой другой платформы."
#: deluge/ui/data/share/applications/deluge.desktop.in.h:2
msgid "BitTorrent Client"
@@ -4520,159 +4592,172 @@ msgstr "Загружайте и обменивайтесь файлами в с
#: deluge/ui/console/console.py:76
msgid "Console Options"
-msgstr ""
+msgstr "Параметры командной строки"
#: deluge/ui/console/console.py:78
msgid ""
"These daemon connect options will be used for commands, or if console ui "
"autoconnect is enabled."
msgstr ""
+"Эти параметры подключения к службе будут использоваться для команд, или "
+"если\n"
+"включено автоматическое подключение текстового пользовательского интерфейса."
#: deluge/ui/console/console.py:87
msgid "Deluge daemon IP address to connect to (default 127.0.0.1)"
msgstr ""
+"Подключаться к службе Deluge по IP-адресу ip_addr (по умолчанию 127.0.0.1)"
#: deluge/ui/console/console.py:96
msgid "Deluge daemon port to connect to (default 58846)"
-msgstr ""
+msgstr "Подключаться к порту port службы Deluge (по умолчанию 58846)"
#: deluge/ui/console/console.py:104
msgid "Deluge daemon username to use when connecting"
-msgstr ""
+msgstr "Использовать имя пользователя user при подключении к службе Deluge"
#: deluge/ui/console/console.py:111
msgid "Deluge daemon password to use when connecting"
-msgstr ""
+msgstr "Использовать пароль pass при подключении к службе Deluge"
#: deluge/ui/console/console.py:131
msgid "Console Commands"
-msgstr ""
+msgstr "Команды командной строки"
#: deluge/ui/console/console.py:132
msgid "Description"
-msgstr ""
+msgstr "Описание"
#: deluge/ui/console/console.py:133
msgid "The following console commands are available:"
-msgstr ""
+msgstr "Доступны следующие команды командной строки:"
#: deluge/ui/console/console.py:134
#: deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui.h:2
msgid "Command"
-msgstr "Ввести комманду"
+msgstr "Команда"
#: deluge/ui/console/cmdline/command.py:208
#, python-format
msgid "`%s` alias"
-msgstr ""
+msgstr "То же, что и `%s`"
#: deluge/ui/console/cmdline/commands/manage.py:29
msgid "Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
msgstr ""
+"Использование: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
#: deluge/ui/console/cmdline/commands/manage.py:35
msgid "an expression matched against torrent ids and torrent names"
-msgstr ""
+msgstr "выражение, сопоставляемое с идентификаторами и именами торрентов"
#: deluge/ui/console/cmdline/commands/manage.py:43
#: deluge/ui/console/cmdline/commands/config.py:88
msgid "set value for this key"
-msgstr ""
+msgstr "установить значение для этого ключа"
#: deluge/ui/console/cmdline/commands/manage.py:46
#: deluge/ui/console/cmdline/commands/config.py:91
msgid "Value to set"
-msgstr ""
+msgstr "Устанавливаемое значение"
#: deluge/ui/console/cmdline/commands/manage.py:53
#: deluge/ui/console/cmdline/commands/config.py:98
msgid "one or more keys separated by space"
-msgstr ""
+msgstr "один или несколько ключей, разделённых пробелом"
#: deluge/ui/console/cmdline/commands/rm.py:33
msgid "Also removes the torrent data"
-msgstr ""
+msgstr "Также удалить загруженные файлы"
#: deluge/ui/console/cmdline/commands/rm.py:40
msgid "List the matching torrents without removing."
-msgstr ""
+msgstr "Перечислить совпадающие торренты без удаления."
#: deluge/ui/console/cmdline/commands/rm.py:46
#: deluge/ui/console/cmdline/commands/recheck.py:28
#: deluge/ui/console/cmdline/commands/move.py:31
msgid "One or more torrent ids"
-msgstr ""
+msgstr "Один или несколько идентификаторов торрентов"
#: deluge/ui/console/cmdline/commands/rm.py:66
#, python-format
msgid "Confirm with -c to remove the listed torrents (Count: %d)"
msgstr ""
+"Используйте ключ -c для подтверждения удаления перечисленных торрентов "
+"(количество: %d)"
#: deluge/ui/console/cmdline/commands/resume.py:22
msgid "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
-msgstr ""
+msgstr "Использование: resume [ * | <torrent-id> [<torrent-id> ...] ]"
#: deluge/ui/console/cmdline/commands/resume.py:29
msgid "One or more torrent ids. Use \"*\" to resume all torrents"
msgstr ""
+"Один или несколько идентификаторов торрентов. Используйте \"*\" для "
+"возобновления всех торрентов"
#: deluge/ui/console/cmdline/commands/pause.py:29
msgid "One or more torrent ids. Use \"*\" to pause all torrents"
msgstr ""
+"Один или несколько идентификаторов торрентов. Используйте \"*\" для "
+"приостановки всех торрентов"
#: deluge/ui/console/cmdline/commands/add.py:38
msgid "Download folder for torrent"
-msgstr ""
+msgstr "Каталог для загрузки торрента"
#: deluge/ui/console/cmdline/commands/add.py:44
msgid "Move the completed torrent to this folder"
-msgstr ""
+msgstr "Перемещать загруженный торрент в этот каталог"
#: deluge/ui/console/cmdline/commands/add.py:50
msgid "One or more torrent files, URLs or magnet URIs"
-msgstr ""
+msgstr "Один или несколько торрент-файлов, ссылки или магнет-ссылки"
#: deluge/ui/console/cmdline/commands/plugin.py:29
msgid "Lists available plugins"
-msgstr ""
+msgstr "Перечислить имеющиеся модули"
#: deluge/ui/console/cmdline/commands/plugin.py:37
msgid "Shows enabled plugins"
-msgstr ""
+msgstr "Показать включённые модули"
#: deluge/ui/console/cmdline/commands/plugin.py:40
msgid "Enables a plugin"
-msgstr ""
+msgstr "Включить модуль"
#: deluge/ui/console/cmdline/commands/plugin.py:43
msgid "Disables a plugin"
-msgstr ""
+msgstr "Отключить дополнение"
#: deluge/ui/console/cmdline/commands/plugin.py:51
msgid "Reload list of available plugins"
-msgstr ""
+msgstr "Перезагрузить список имеющихся модулей"
#: deluge/ui/console/cmdline/commands/plugin.py:54
msgid "Install a plugin from an .egg file"
-msgstr ""
+msgstr "Установить модуль из .egg файла"
#: deluge/ui/console/cmdline/commands/status.py:36
msgid ""
"Raw values for upload/download rates (without KiB/s suffix)(useful for "
"scripts that want to do their own parsing)"
msgstr ""
+"Необработанные значения скорости загрузки и раздачи (без суффикса КиБ/с) "
+"(полезно для сценариев, желающих выполнять собственную обработку)"
#: deluge/ui/console/cmdline/commands/status.py:46
msgid "Do not show torrent status (Improves command speed)"
-msgstr ""
+msgstr "Не показывать состояния торрентов (улучшает скорость команды)"
#: deluge/ui/console/cmdline/commands/connect.py:26
msgid "Usage: connect <host[:port]> [<username>] [<password>]"
-msgstr ""
+msgstr "Использование: connect <host[:port]> [<username>] [<password>]"
#: deluge/ui/console/cmdline/commands/connect.py:30
msgid "Daemon host and port"
-msgstr ""
+msgstr "Хост и порт службы"
#: deluge/ui/console/cmdline/commands/connect.py:36
#: deluge/ui/console/modes/preferences/preference_panes.py:652
@@ -4682,139 +4767,146 @@ msgstr "Пароль"
#: deluge/ui/console/cmdline/commands/move.py:34
msgid "The path to move the torrents to"
-msgstr ""
+msgstr "Путь для перемещения торрентов"
#: deluge/ui/console/cmdline/commands/debug.py:26
msgid "The new state"
-msgstr ""
+msgstr "Новое состояние"
#: deluge/ui/console/cmdline/commands/help.py:29
msgid "One or more commands"
-msgstr ""
+msgstr "Одна или несколько комманд"
#: deluge/ui/console/cmdline/commands/config.py:79
msgid "Usage: config [--set <key> <value>] [<key> [<key>...] ]"
-msgstr ""
+msgstr "Использование: config [--set <key> <value>] [<key> [<key>...] ]"
#: deluge/ui/console/cmdline/commands/info.py:101
msgid "Show more information per torrent."
-msgstr ""
+msgstr "Показывать больше информации о торренте."
#: deluge/ui/console/cmdline/commands/info.py:109
msgid "Show more detailed information including files and peers."
-msgstr ""
+msgstr "Показывать более подробную информацию, включая файлы и узлы."
#: deluge/ui/console/cmdline/commands/info.py:116
#, python-format
msgid "Show torrents with state STATE: %s."
-msgstr ""
+msgstr "Показать торренты с состоянием STATE: %s."
#: deluge/ui/console/cmdline/commands/info.py:132
msgid "Same as --sort but items are in reverse order."
-msgstr ""
+msgstr "То же, что и --sort, но элементы расположены в обратном порядке."
#: deluge/ui/console/cmdline/commands/info.py:138
msgid "One or more torrent ids. If none is given, list all"
msgstr ""
+"Один или несколько идентификаторов торрентов. Если ничего не указано, "
+"перечислить все"
#: deluge/ui/console/modes/connectionmanager.py:44
msgid "Select Host"
-msgstr ""
+msgstr "Выбор хоста"
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Quit"
-msgstr ""
+msgstr "Выйти"
#: deluge/ui/console/modes/connectionmanager.py:51
msgid "Delete Host"
-msgstr ""
+msgstr "Удалить хост"
#: deluge/ui/console/modes/connectionmanager.py:116
msgid "Add Host (Up & Down arrows to navigate, Esc to cancel)"
msgstr ""
+"Добавление хоста (стрелки вверх и вниз для навигации, Esc для отмены)"
#: deluge/ui/console/modes/connectionmanager.py:133
msgid "Error adding host"
-msgstr ""
+msgstr "Ошибка добавления хоста"
#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
msgid "Columns"
-msgstr ""
+msgstr "Столбцы"
#: deluge/ui/console/modes/torrentlist/torrentviewcolumns.py:96
msgid "Width"
-msgstr ""
+msgstr "Ширина"
#: deluge/ui/console/modes/preferences/preference_panes.py:178
msgid "General options"
-msgstr ""
+msgstr "Общие параметры"
#: deluge/ui/console/modes/preferences/preference_panes.py:182
msgid "Ring system bell when a download finishes"
-msgstr ""
+msgstr "Подавать системный звуковой сигнал при окончании загрузки"
#: deluge/ui/console/modes/preferences/preference_panes.py:188
msgid "List complete torrents after incomplete regardless of sorting order"
msgstr ""
+"Перечислять загруженные торренты после загружающихся независимо от порядка "
+"сортировки"
#: deluge/ui/console/modes/preferences/preference_panes.py:193
msgid "Move selection when moving torrents in the queue"
-msgstr ""
+msgstr "Перемещать выделение при перемещении торрентов в очереди"
#: deluge/ui/console/modes/preferences/preference_panes.py:200
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:67
msgid "Language"
-msgstr ""
+msgstr "Язык"
#: deluge/ui/console/modes/preferences/preference_panes.py:202
msgid "Command Line Mode"
-msgstr ""
+msgstr "Режим командной строки"
#: deluge/ui/console/modes/preferences/preference_panes.py:205
msgid "Do not store duplicate input in history"
-msgstr ""
+msgstr "Не сохранять повторяющийся ввод в истории"
#: deluge/ui/console/modes/preferences/preference_panes.py:210
msgid "Store and load command line history in command line mode"
-msgstr ""
+msgstr "Сохранять и загружать историю ввода в режиме командной строки"
#: deluge/ui/console/modes/preferences/preference_panes.py:216
msgid "Third tab lists all remaining torrents in command line mode"
msgstr ""
+"Третье нажатие Tab в режиме командной строки перечисляет все оставшиеся "
+"торренты"
#: deluge/ui/console/modes/preferences/preference_panes.py:221
msgid "Torrents per tab press"
-msgstr ""
+msgstr "Торрентов на нажатие Tab"
#: deluge/ui/console/modes/preferences/preference_panes.py:234
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:18
#: deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js:39
msgid "Folders"
-msgstr ""
+msgstr "Папки"
#: deluge/ui/console/modes/preferences/preference_panes.py:237
msgid "Download To"
-msgstr ""
+msgstr "Загружать в"
#: deluge/ui/console/modes/preferences/preference_panes.py:254
msgid "Move completed to"
-msgstr ""
+msgstr "Перемещать загруженные в"
#: deluge/ui/console/modes/preferences/preference_panes.py:269
msgid "Copy of .torrent files to"
-msgstr ""
+msgstr "Копировать файлы .torrent в"
#: deluge/ui/console/modes/preferences/preference_panes.py:290
msgid "Add Paused"
-msgstr ""
+msgstr "Добавлять приостановленными"
#: deluge/ui/console/modes/preferences/preference_panes.py:293
msgid "Pre-Allocate disk space"
-msgstr ""
+msgstr "Резервировать место на диске"
#: deluge/ui/console/modes/preferences/preference_panes.py:304
msgid "Incomming Ports"
-msgstr ""
+msgstr "Входящие порты"
#: deluge/ui/console/modes/preferences/preference_panes.py:313
#: deluge/ui/console/modes/preferences/preference_panes.py:337
@@ -4833,17 +4925,21 @@ msgstr "Использовать случайные порты"
#: deluge/ui/console/modes/preferences/preference_panes.py:352
msgid "Incoming Interface"
-msgstr ""
+msgstr "Входящий интерфейс"
#: deluge/ui/console/modes/preferences/preference_panes.py:355
msgid "IP address of the interface to listen on (leave empty for default):"
msgstr ""
+"IP-адрес интерфейса для прослушивания (оставьте пустым для использования "
+"значения по умолчанию):"
#: deluge/ui/console/modes/preferences/preference_panes.py:363
msgid ""
"The network interface name or IP address for outgoing BitTorrent "
"connections. (Leave empty for default.):"
msgstr ""
+"Имя сетевого интерфейса или IP-адрес для исходящих BitTorrent соединений "
+"(оставьте пустым для использования значения по умолчанию):"
#: deluge/ui/console/modes/preferences/preference_panes.py:382
msgid "Inbound"
@@ -4851,12 +4947,12 @@ msgstr "Входящий"
#: deluge/ui/console/modes/preferences/preference_panes.py:391
msgid "Outbound"
-msgstr "Исходящиц"
+msgstr "Исходящий"
#: deluge/ui/console/modes/preferences/preference_panes.py:413
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:38
msgid "Global Bandwidth Usage"
-msgstr ""
+msgstr "Глобальные ограничения пропускной способности"
#: deluge/ui/console/modes/preferences/preference_panes.py:416
#: deluge/ui/console/modes/preferences/preference_panes.py:469
@@ -4872,20 +4968,20 @@ msgstr "Максимум слотов отдачи"
#: deluge/ui/console/modes/preferences/preference_panes.py:430
#: deluge/ui/console/modes/preferences/preference_panes.py:483
msgid "Maximum Download Speed (KiB/s)"
-msgstr ""
+msgstr "Максимальная скорость загрузки (КиБ/с)"
#: deluge/ui/console/modes/preferences/preference_panes.py:437
#: deluge/ui/console/modes/preferences/preference_panes.py:490
msgid "Maximum Upload Speed (KiB/s)"
-msgstr ""
+msgstr "Максимальная скорость отдачи (КиБ/с)"
#: deluge/ui/console/modes/preferences/preference_panes.py:444
msgid "Maximum Half-Open Connections"
-msgstr "Максимум полуоткрытых соеденений"
+msgstr "Максимум полуоткрытых соединений"
#: deluge/ui/console/modes/preferences/preference_panes.py:451
msgid "Maximum Connection Attempts per Second"
-msgstr "Максимум попыток соеденения в сек."
+msgstr "Максимум попыток соединения в секунду"
#: deluge/ui/console/modes/preferences/preference_panes.py:463
msgid "Rate Limit IP Overhead"
@@ -4894,43 +4990,43 @@ msgstr "Ограничивать скорость с учётом издерже
#: deluge/ui/console/modes/preferences/preference_panes.py:466
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:148
msgid "Per Torrent Bandwidth Usage"
-msgstr ""
+msgstr "Ограничения пропускной способности для торрента"
#: deluge/ui/console/modes/preferences/preference_panes.py:513
msgid "Yes, please send anonymous statistics."
-msgstr ""
+msgstr "Отправлять анонимную статистику."
#: deluge/ui/console/modes/preferences/preference_panes.py:531
msgid "Daemon Port"
-msgstr ""
+msgstr "Порт службы"
#: deluge/ui/console/modes/preferences/preference_panes.py:538
msgid "Allow remote connections"
-msgstr ""
+msgstr "Разрешить удалённые соединения"
#: deluge/ui/console/modes/preferences/preference_panes.py:561
msgid "Total"
-msgstr ""
+msgstr "Всего"
#: deluge/ui/console/modes/preferences/preference_panes.py:593
msgid "Share Ratio"
-msgstr "Рейтинг"
+msgstr "Рейтинг раздачи"
#: deluge/ui/console/modes/preferences/preference_panes.py:601
msgid "Time Ratio"
-msgstr ""
+msgstr "Коэффициент времени"
#: deluge/ui/console/modes/preferences/preference_panes.py:609
msgid "Time (m)"
-msgstr ""
+msgstr "Длительность (мин)"
#: deluge/ui/console/modes/preferences/preference_panes.py:633
msgid "Remove torrent (Unchecked pauses torrent)"
-msgstr ""
+msgstr "Удалять торрент (если не выбрано, приостанавливать торрент)"
#: deluge/ui/console/modes/preferences/preference_panes.py:646
msgid "Proxy Settings"
-msgstr ""
+msgstr "Параметры прокси"
#: deluge/ui/console/modes/preferences/preference_panes.py:649
msgid "Type"
@@ -4938,55 +5034,55 @@ msgstr "Тип"
#: deluge/ui/console/modes/preferences/preference_panes.py:653
msgid "Hostname"
-msgstr ""
+msgstr "Имя хоста"
#: deluge/ui/console/modes/preferences/preference_panes.py:673
msgid "Proxy Type Help"
-msgstr ""
+msgstr "Подсказка по типам прокси"
#: deluge/ui/console/modes/preferences/preference_panes.py:697
msgid "Cache Size (16 KiB blocks)"
-msgstr ""
+msgstr "Размер кэша (блоков по 16 КиБ)"
#: deluge/ui/console/modes/preferences/preference_panes.py:704
msgid "Cache Expiry (seconds)"
-msgstr ""
+msgstr "Срок действия кэша (секунд)"
#: deluge/ui/console/modes/preferences/preference_panes.py:712
msgid "Blocks Written"
-msgstr ""
+msgstr "Блоков записано"
#: deluge/ui/console/modes/preferences/preference_panes.py:716
msgid "Writes"
-msgstr ""
+msgstr "Операций записи"
#: deluge/ui/console/modes/preferences/preference_panes.py:720
msgid "Write Cache Hit Ratio"
-msgstr ""
+msgstr "Коэффициент эффективности кэша записи"
#: deluge/ui/console/modes/preferences/preference_panes.py:725
msgid "Blocks Read"
-msgstr ""
+msgstr "Блоков считано"
#: deluge/ui/console/modes/preferences/preference_panes.py:729
msgid "Blocks Read hit"
-msgstr ""
+msgstr "Считано блоков из кэша"
#: deluge/ui/console/modes/preferences/preference_panes.py:732
msgid "Reads"
-msgstr ""
+msgstr "Операций чтения"
#: deluge/ui/console/modes/preferences/preference_panes.py:735
msgid "Read Cache Hit Ratio"
-msgstr ""
+msgstr "Коэффициент эффективности кэша чтения"
#: deluge/ui/console/modes/preferences/preference_panes.py:741
msgid "Cache Size"
-msgstr ""
+msgstr "Размер кэша"
#: deluge/ui/console/modes/preferences/preference_panes.py:746
msgid "Read Cache Size"
-msgstr ""
+msgstr "Размер кэша чтения"
#: deluge/ui/console/modes/preferences/preferences.py:145
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:333
@@ -4999,23 +5095,23 @@ msgstr "Применить"
#: deluge/ui/web/js/deluge-all/OtherLimitWindow.js:52
#: deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js:88
msgid "OK"
-msgstr ""
+msgstr "OK"
#: deluge/ui/console/widgets/fields.py:1070
msgid "Select Language"
-msgstr ""
+msgstr "Выберите язык"
#: deluge/ui/console/widgets/statusbars.py:120
#, python-format
msgid "IP {!white,blue!}%s{!status!}"
-msgstr ""
+msgstr "IP {!white,blue!}%s{!status!}"
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:114
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:116
#: deluge/plugins/Blocklist/deluge_blocklist/common.py:118
#, python-format
msgid "The IP address \"%s\" is badly formed"
-msgstr ""
+msgstr "IP-адрес «%s» имеет неверный формат"
#: deluge/plugins/Blocklist/deluge_blocklist/webui.py:21
msgid "Emule IP list (GZip)"
@@ -5036,6 +5132,7 @@ msgstr "PeerGuardian P2B (GZip)"
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:45
msgid "Blocked IP Ranges /Whitelisted IP Ranges"
msgstr ""
+"Диапазоны заблокированных IP-адресов / Диапазоны разрешённых IP-адресов"
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:56
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:156
@@ -5045,7 +5142,7 @@ msgstr "Чёрный список"
#: deluge/plugins/Blocklist/deluge_blocklist/gtkui.py:233
msgid "Bad IP address"
-msgstr ""
+msgstr "Неверный IP-адрес"
#: deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py:40
msgid "Invalid leader"
@@ -5074,11 +5171,11 @@ msgstr "Импортировать чёрный список при запуск
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:6
#: deluge/plugins/WebUi/deluge_webui/data/config.ui.h:4
msgid "<b>Settings</b>"
-msgstr "<b>Настройки</b>"
+msgstr "<b>Параметры</b>"
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:7
msgid "Download the blocklist file if necessary and import the file."
-msgstr "Скачать при необходимости черный список и загрузить его."
+msgstr "Скачать при необходимости чёрный список и импортировать файл."
#: deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui.h:8
msgid "Check Download and Import"
@@ -5153,13 +5250,15 @@ msgstr "<b>Команды</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:327
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:342
msgid "Incompatible Option"
-msgstr ""
+msgstr "Несовместимые параметры"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:418
msgid ""
"\"Watch Folder\" directory and \"Copy of .torrent files to\" directory "
"cannot be the same!"
msgstr ""
+"Каталоги «Папка для слежения» и «Копировать файлы .torrent в» не могут "
+"совпадать!"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:462
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:466
@@ -5168,12 +5267,12 @@ msgstr "Автодобавление"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:495
msgid "Double-click to toggle"
-msgstr ""
+msgstr "Двойное нажатие для переключения"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:503
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:511
msgid "Double-click to edit"
-msgstr ""
+msgstr "Двойное нажатие для изменения"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:507
msgid "Path"
@@ -5197,6 +5296,8 @@ msgid ""
"If a .torrent file is added to this directory,\n"
"it will be added to the session."
msgstr ""
+"Если в этот каталог добавить файл .torrent,\n"
+"он будет добавлен в сеанс."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:4
#: deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui.h:2
@@ -5220,6 +5321,8 @@ msgid ""
"Once the torrent is added to the session,\n"
"the .torrent will be deleted."
msgstr ""
+"Как только торрент будет добавлен в сеанс,\n"
+"файл .torrent будет удалён."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:10
msgid "Append extension after adding:"
@@ -5231,6 +5334,9 @@ msgid ""
"an extension will be appended to the .torrent\n"
"and it will remain in the same directory."
msgstr ""
+"Как только торрент будет добавлен в сеанс,\n"
+"к файлу .torrent будет добавлено расширение,\n"
+"и он останется в том же каталоге."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:14
msgid ".added"
@@ -5242,12 +5348,18 @@ msgid ""
"the .torrent will copied to the chosen directory\n"
"and deleted from the watch folder."
msgstr ""
+"Как только торрент будет добавлен в сеанс,\n"
+"файл .torrent будет скопирован в выбранный каталог\n"
+"и удалён из папки для слежения."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:20
msgid ""
"Once the torrent is deleted from the session,\n"
"also delete the .torrent file used to add it."
msgstr ""
+"Как только торрент будет удалён из сеанса,\n"
+"также будет удалён файл .torrent, который\n"
+"использовался для его добавления."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:22
msgid "<b>Torrent File Action</b>"
@@ -5259,7 +5371,7 @@ msgstr "Указать папку для загрузок"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:24
msgid "This folder will be where the torrent data is downloaded to."
-msgstr ""
+msgstr "В эту папку будут загружаться данные торрентов."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:25
msgid "<b>Download Folder</b>"
@@ -5267,11 +5379,11 @@ msgstr "<b>Папка для загрузок</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:26
msgid "Set move completed folder"
-msgstr ""
+msgstr "Указать папку для перемещения завершённых загрузок"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:27
msgid "<b>Move Completed</b>"
-msgstr "Переместить завершённые закачки"
+msgstr "<b>Перемещение завершённых загрузок</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:28
msgid "Label: "
@@ -5287,35 +5399,35 @@ msgstr "Основное"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:31
msgid "The user selected here will be the owner of the torrent."
-msgstr ""
+msgstr "Выбранный здесь пользователь будет владельцем торрента."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:32
msgid "<b>Owner</b>"
-msgstr ""
+msgstr "<b>Владелец</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:33
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:103
msgid "Max Upload Speed:"
-msgstr "Максимальная скорость раздачи:"
+msgstr "Максимальная скорость отдачи:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:34
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:130
msgid "Max Connections:"
-msgstr "Максимальное кол-во соединений:"
+msgstr "Максимум соединений:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:35
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:153
msgid "Max Upload Slots:"
-msgstr "Макс. количество слотов раздачи:"
+msgstr "Максимум слотов отдачи:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:37
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:77
msgid "Max Download Speed:"
-msgstr "Максимальная скорость приёма:"
+msgstr "Максимальная скорость загрузки:"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:38
msgid "<b>Bandwidth</b>"
-msgstr "<b>Ограничения:</b>"
+msgstr "<b>Ограничения</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:41
#: deluge/ui/web/render/tab_status.html:19
@@ -5336,19 +5448,19 @@ msgstr "<b>Очередь:</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/config.ui.h:1
msgid "<b>Watch Folders:</b>"
-msgstr "<b>Папки поиска:</b>"
+msgstr "<b>Папки для слежения:</b>"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:60
msgid "minutes"
-msgstr ""
+msgstr "мин"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:62
msgid "1 minute"
-msgstr ""
+msgstr "1 минута"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:64
msgid "1 second"
-msgstr ""
+msgstr "1 секунда"
#: deluge/plugins/Stats/deluge_stats/gtkui.py:66
msgid "seconds"
@@ -5356,51 +5468,51 @@ msgstr "секунд(ы)"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:1
msgid "Stats"
-msgstr ""
+msgstr "Статистика"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:2
msgid "Resolution"
-msgstr ""
+msgstr "Разрешение"
#: deluge/plugins/Stats/deluge_stats/data/tabs.ui.h:5
msgid "Seeds/Peers"
-msgstr ""
+msgstr "Сиды/пиры"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:1
msgid "Download color:"
-msgstr ""
+msgstr "Цвет загрузки:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:2
msgid "Upload color:"
-msgstr ""
+msgstr "Цвет раздачи:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:3
msgid "<b>Connections Graph</b>"
-msgstr ""
+msgstr "<b>График соединений</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:4
msgid "<b>Bandwidth Graph</b>"
-msgstr ""
+msgstr "<b>График пропускной способности</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:5
msgid "DHT nodes:"
-msgstr ""
+msgstr "Узлы DHT:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:6
msgid "Cached DHT nodes:"
-msgstr ""
+msgstr "Кэшированные узлы DHT:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:7
msgid "DHT torrents:"
-msgstr ""
+msgstr "Торренты DHT:"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:9
msgid "<b>Seeds / Peers</b>"
-msgstr ""
+msgstr "<b>Сиды/пиры</b>"
#: deluge/plugins/Stats/deluge_stats/data/config.ui.h:11
msgid "<b>Graph Colors</b>"
-msgstr ""
+msgstr "<b>Цвета графиков</b>"
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:35
#: deluge/plugins/WebUi/deluge_webui/gtkui.py:47
@@ -5430,8 +5542,8 @@ msgstr "Прослушиваемый порт:"
#: deluge/plugins/Label/deluge_label/core.py:184
msgid "Invalid label, valid characters:[a-z0-9_-]"
msgstr ""
-"Неправильная метка. Допустимые символы: латиница (A-Z), арабские цифры (0-"
-"9), дефис, подчеркивание."
+"Неверная метка. Допустимые символы: латиница (A-Z), арабские цифры (0-9), "
+"дефис, подчёркивание."
#: deluge/plugins/Label/deluge_label/core.py:186
msgid "Empty Label"
@@ -5474,11 +5586,11 @@ msgstr "Параметры метки"
#: deluge/plugins/Label/deluge_label/gtkui/__init__.py:49
#: deluge/plugins/Label/deluge_label/gtkui/__init__.py:77
msgid "Label"
-msgstr "Подпись"
+msgstr "Метка"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:1
msgid "tracker1.org"
-msgstr ""
+msgstr "tracker1.org"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:3
msgid "<b>Label Options</b>"
@@ -5498,7 +5610,7 @@ msgstr "Применить настройки очереди:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:17
msgid "Apply folder settings:"
-msgstr ""
+msgstr "Применить параметры папки:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:19
msgid "<i>(1 line per tracker)</i>"
@@ -5519,8 +5631,7 @@ msgstr "<b>Добавить метку</b>"
#: deluge/plugins/Label/deluge_label/data/label_pref.ui.h:1
msgid "<i>Use the sidebar to add,edit and remove labels. </i>\n"
msgstr ""
-"<i>Используйте боковую панель для добавления, редактирования и удаления "
-"меток. </i>\n"
+"<i>Используйте боковую панель для добавления, правки и удаления меток. </i>\n"
#: deluge/plugins/Label/deluge_label/data/label_pref.ui.h:3
msgid "<b>Labels</b>"
@@ -5536,11 +5647,11 @@ msgstr "Всплывающее уведомление не включено."
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:177
msgid "libnotify is not installed"
-msgstr ""
+msgstr "Библиотека libnotify не установлена"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:185
msgid "Failed to popup notification"
-msgstr ""
+msgstr "Не удалось показать всплывающее уведомление"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:188
msgid "Notification popup shown"
@@ -5599,7 +5710,7 @@ msgstr "Сервер неверно ответил на приветствие H
#: deluge/plugins/Notifications/deluge_notifications/core.py:149
#, python-format
msgid "Server refused username/password combination: %s"
-msgstr ""
+msgstr "Сервер отклонил комбинацию имени пользователя и пароля: %s"
#: deluge/plugins/Notifications/deluge_notifications/core.py:174
msgid "Notification email sent."
@@ -5643,7 +5754,7 @@ msgstr "Звук включён"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:4
msgid "<b>UI Notifications</b>"
-msgstr "<b>Уведомления пользовательского интерфейса</b>"
+msgstr "<b>Уведомления интерфейса</b>"
#: deluge/plugins/Notifications/deluge_notifications/data/config.ui.h:9
msgid "<b>Recipients</b>"
@@ -5701,7 +5812,7 @@ msgstr "Планировщик"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:289
msgid "<b>Schedule</b>"
-msgstr ""
+msgstr "<b>Расписание</b>"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:301
msgid "Download Limit:"
@@ -5713,11 +5824,11 @@ msgstr "Ограничение отдачи:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:319
msgid "Active Torrents:"
-msgstr "Выполняемые задания:"
+msgstr "Активные торренты:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:328
msgid "Active Downloading:"
-msgstr "Активные закачки:"
+msgstr "Активные загрузки:"
#: deluge/plugins/Scheduler/deluge_scheduler/gtkui.py:337
msgid "Active Seeding:"
@@ -5729,27 +5840,27 @@ msgstr "<b>Ограничения:</b>"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:13
msgid "File Browser"
-msgstr ""
+msgstr "Файловый браузер"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:25
msgid "Back"
-msgstr ""
+msgstr "Назад"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:29
msgid "Forward"
-msgstr ""
+msgstr "Вперёд"
#: deluge/ui/web/js/deluge-all/FileBrowser.js:37
msgid "Home"
-msgstr ""
+msgstr "Домашняя папка"
#: deluge/ui/web/js/deluge-all/Toolbar.js:32
msgid "Create"
-msgstr ""
+msgstr "Создать"
#: deluge/ui/web/js/deluge-all/Toolbar.js:100
msgid "Help"
-msgstr ""
+msgstr "Справка"
#: deluge/ui/web/js/deluge-all/Toolbar.js:108
msgid "Logout"
@@ -5761,30 +5872,30 @@ msgstr "Сохранить"
#: deluge/ui/web/js/deluge-all/AboutWindow.js:19
msgid "About Deluge"
-msgstr ""
+msgstr "О Deluge"
#: deluge/ui/web/js/deluge-all/AboutWindow.js:102
msgid "Copyright 2007-2018 Deluge Team"
-msgstr ""
+msgstr "Copyright 2007-2018 Команда Deluge"
#: deluge/ui/web/js/deluge-all/RemoveWindow.js:33
msgid "Remove With Data"
-msgstr ""
+msgstr "Удалить с загруженными данными"
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:17
msgid "Add Connection"
-msgstr ""
+msgstr "Добавить подключение"
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:44
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:44
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:53
msgid "Host:"
-msgstr "Узел:"
+msgstr "Хост:"
#: deluge/ui/web/js/deluge-all/AddConnectionWindow.js:96
#, python-brace-format
msgid "Unable to add host: {0}"
-msgstr ""
+msgstr "Не удалось добавить хост: {0}"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:37
msgid "Move"
@@ -5792,15 +5903,15 @@ msgstr "Переместить"
#: deluge/ui/web/js/deluge-all/MoveStorage.js:54
msgid "Browse"
-msgstr ""
+msgstr "Обзор"
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:17
msgid "Edit Connection"
-msgstr ""
+msgstr "Изменить подключение"
#: deluge/ui/web/js/deluge-all/EditConnectionWindow.js:115
msgid "Unable to edit host"
-msgstr ""
+msgstr "Не удалось изменить хост"
#: deluge/ui/web/js/deluge-all/LoginWindow.js:22
#: deluge/ui/web/js/deluge-all/LoginWindow.js:31
@@ -5809,27 +5920,27 @@ msgstr "Войти"
#: deluge/ui/web/js/deluge-all/LoginWindow.js:108
msgid "Login Failed"
-msgstr ""
+msgstr "Ошибка входа"
#: deluge/ui/web/js/deluge-all/LoginWindow.js:109
msgid "You entered an incorrect password"
-msgstr ""
+msgstr "Введён неверный пароль"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:228
msgid "Public"
-msgstr ""
+msgstr "Публичный"
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:292
msgid "Last Transfer"
-msgstr ""
+msgstr "Последняя активность"
#: deluge/ui/web/js/deluge-all/Deluge.js:158
msgid "Mixed"
-msgstr ""
+msgstr "Смешанный"
#: deluge/ui/web/js/deluge-all/Statusbar.js:87
msgid "Set Maximum Connections"
-msgstr "Максимальное количество соединений"
+msgstr "Установить максимум соединений"
#: deluge/ui/web/js/deluge-all/Statusbar.js:97
msgid "Download Speed"
@@ -5840,35 +5951,35 @@ msgstr "Скорость загрузки"
#: deluge/ui/web/js/deluge-all/Menus.js:79
#: deluge/ui/web/js/deluge-all/Menus.js:124
msgid "5 KiB/s"
-msgstr ""
+msgstr "5 КиБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:108
#: deluge/ui/web/js/deluge-all/Statusbar.js:167
#: deluge/ui/web/js/deluge-all/Menus.js:85
#: deluge/ui/web/js/deluge-all/Menus.js:130
msgid "10 KiB/s"
-msgstr ""
+msgstr "10 КиБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:114
#: deluge/ui/web/js/deluge-all/Statusbar.js:173
#: deluge/ui/web/js/deluge-all/Menus.js:91
#: deluge/ui/web/js/deluge-all/Menus.js:136
msgid "30 KiB/s"
-msgstr ""
+msgstr "30 КиБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:120
#: deluge/ui/web/js/deluge-all/Statusbar.js:179
#: deluge/ui/web/js/deluge-all/Menus.js:97
#: deluge/ui/web/js/deluge-all/Menus.js:142
msgid "80 KiB/s"
-msgstr ""
+msgstr "80 КиБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:126
#: deluge/ui/web/js/deluge-all/Statusbar.js:185
#: deluge/ui/web/js/deluge-all/Menus.js:103
#: deluge/ui/web/js/deluge-all/Menus.js:148
msgid "300 KiB/s"
-msgstr ""
+msgstr "300 КиБ/с"
#: deluge/ui/web/js/deluge-all/Statusbar.js:145
msgid "Set Maximum Download Speed"
@@ -5876,11 +5987,11 @@ msgstr "Установить максимальную скорость загр
#: deluge/ui/web/js/deluge-all/Statusbar.js:156
msgid "Upload Speed"
-msgstr "Скорость раздачи"
+msgstr "Скорость отдачи"
#: deluge/ui/web/js/deluge-all/Statusbar.js:204
msgid "Set Maximum Upload Speed"
-msgstr "Установить лимит скорости раздачи"
+msgstr "Установить максимум скорости отдачи"
#: deluge/ui/web/js/deluge-all/Statusbar.js:215
msgid "Protocol Traffic Download/Upload"
@@ -5888,12 +5999,12 @@ msgstr "Трафик протокола - Загрузка/Отдача"
#: deluge/ui/web/js/deluge-all/Statusbar.js:242
msgid "Freespace in download folder"
-msgstr ""
+msgstr "Свободное место в папке для загрузки"
#: deluge/ui/web/js/deluge-all/Statusbar.js:357
#, python-brace-format
msgid "<b>IP</b> {0}"
-msgstr ""
+msgstr "<b>IP</b> {0}"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:33
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:187
@@ -5904,29 +6015,30 @@ msgstr "Соединиться"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:197
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:379
msgid "Stop Daemon"
-msgstr ""
+msgstr "Остановить службу"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:185
msgid "Disconnect"
-msgstr ""
+msgstr "Отключиться"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:204
msgid "Start Daemon"
-msgstr ""
+msgstr "Запустить службу"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:322
msgid "Change Default Password"
-msgstr ""
+msgstr "Изменить пароль по умолчанию"
#: deluge/ui/web/js/deluge-all/ConnectionManager.js:324
msgid ""
"We recommend changing the default password.<br><br>Would you like to change "
"it now?"
msgstr ""
+"Рекомендуется изменить пароль по умолчанию.<br><br>Изменить его сейчас?"
#: deluge/ui/web/js/deluge-all/Sidebar.js:13
msgid "Tracker Host"
-msgstr ""
+msgstr "Хост трекера"
#: deluge/ui/web/js/deluge-all/Sidebar.js:33
msgid "Filters"
@@ -5934,19 +6046,19 @@ msgstr "Фильтры"
#: deluge/ui/web/js/deluge-all/UI.js:142
msgid "Connection restored"
-msgstr ""
+msgstr "Соединение восстановлено"
#: deluge/ui/web/js/deluge-all/UI.js:153
msgid "Lost Connection"
-msgstr ""
+msgstr "Соединение потеряно"
#: deluge/ui/web/js/deluge-all/UI.js:154
msgid "The connection to the webserver has been lost!"
-msgstr ""
+msgstr "Соединение с веб-сервером потеряно!"
#: deluge/ui/web/js/deluge-all/UI.js:160
msgid "Lost connection to webserver"
-msgstr ""
+msgstr "Потеряно соединение с веб-сервером"
#: deluge/ui/web/js/deluge-all/Menus.js:72
msgid "D/L Speed Limit"
@@ -5958,7 +6070,7 @@ msgstr "Ограничение скорости загрузки"
#: deluge/ui/web/js/deluge-all/Menus.js:162
msgid "Connection Limit"
-msgstr "Лимит соединений"
+msgstr "Ограничение соединений"
#: deluge/ui/web/js/deluge-all/Menus.js:207
msgid "Upload Slot Limit"
@@ -5970,11 +6082,11 @@ msgstr "Обновить трекер"
#: deluge/ui/web/js/deluge-all/Menus.js:339
msgid "Force Recheck"
-msgstr "Быстрая перепроверка"
+msgstr "Перепроверить принудительно"
#: deluge/ui/web/js/deluge-all/Menus.js:359
msgid "Expand All"
-msgstr ""
+msgstr "Развернуть всё"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:13
msgid "Details"
@@ -5982,15 +6094,15 @@ msgstr "Подробности"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:28
msgid "Comment:"
-msgstr ""
+msgstr "Комментарий:"
#: deluge/ui/web/js/deluge-all/details/DetailsTab.js:29
msgid "Status:"
-msgstr ""
+msgstr "Статус:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:242
msgid "Move Completed:"
-msgstr ""
+msgstr "Перемещать завершённые:"
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:272
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:116
@@ -6003,7 +6115,7 @@ msgstr "Приватный"
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:39
msgid "Loading"
-msgstr ""
+msgstr "Загружается"
#: deluge/ui/web/js/deluge-all/details/StatusTab.js:118
msgid "True"
@@ -6018,40 +6130,43 @@ msgid ""
"Help us improve Deluge by sending us your Python version, PyGTK version, OS "
"and processor types. Absolutely no other information is sent."
msgstr ""
+"Помогите нам сделать Deluge лучше, отправляя сведения об используемых "
+"версиях Python, PyGTK, а также о типах ОС и процессора. Никакая другая "
+"информация не отправляется."
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:215
msgid "Pause torrent"
-msgstr ""
+msgstr "Приостановить торрент"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:17
msgid "Install Plugin"
-msgstr ""
+msgstr "Установить модуль"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:33
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:109
msgid "Install"
-msgstr ""
+msgstr "Установить"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:45
msgid "Select an egg"
-msgstr ""
+msgstr "Выберите egg"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:46
msgid "Plugin Egg"
-msgstr ""
+msgstr "Модуль Egg"
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:49
msgid "Browse..."
-msgstr ""
+msgstr "Обзор..."
#: deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js:59
msgid "Uploading your plugin..."
-msgstr ""
+msgstr "Ваш модуль загружается..."
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:52
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:162
msgid "Maximum Connections:"
-msgstr "Максимальное число соединений:"
+msgstr "Максимум соединений:"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:74
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:184
@@ -6061,63 +6176,63 @@ msgstr "Максимальная скорость приёма (КБ/с):"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:85
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:195
msgid "Maximum Upload Speed (KiB/s):"
-msgstr "Лимит скорости раздачи (КБ/с):"
+msgstr "Максимальная скорость отдачи (КиБ/с):"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:96
msgid "Maximum Half-Open Connections:"
-msgstr "Максимальное число полуоткрытых соединений:"
+msgstr "Максимум полуоткрытых соединений:"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:107
msgid "Maximum Connection Attempts per Second:"
-msgstr "Максимальное число попыток соединения в секунду:"
+msgstr "Максимум попыток соединения в секунду:"
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:173
msgid "Maximum Upload Slots:"
-msgstr "Лимит числа слотов раздачи:"
+msgstr "Максимум слотов отдачи:"
#: deluge/ui/web/js/deluge-all/preferences/CachePage.js:43
msgid "Cache Size (16 KiB Blocks):"
-msgstr ""
+msgstr "Размер кэша (блоков по 16 КиБ):"
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:132
msgid "Force Use of Proxy"
-msgstr ""
+msgstr "Использовать прокси принудительно"
#: deluge/ui/web/js/deluge-all/preferences/PluginsPage.js:116
msgid "Find More"
-msgstr ""
+msgstr "Найти ещё"
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:69
msgid "Use Random Port"
-msgstr ""
+msgstr "Использовать случайный порт"
#: deluge/ui/web/js/deluge-all/preferences/NetworkPage.js:241
msgid "Type Of Service"
-msgstr ""
+msgstr "Тип обслуживания (ToS)"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:53
msgid "Show filters with zero torrents"
-msgstr ""
+msgstr "Показывать фильтры с нулевым числом торрентов"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:60
msgid "Allow the use of multiple filters at once"
-msgstr ""
+msgstr "Разрешить использование нескольких фильтров одновременно"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:94
msgid "WebUI Password"
-msgstr ""
+msgstr "Пароль веб-интерфейса"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:110
msgid "Old:"
-msgstr ""
+msgstr "Старый:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:114
msgid "New:"
-msgstr ""
+msgstr "Новый:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:118
msgid "Confirm:"
-msgstr ""
+msgstr "Подтвердите:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:124
msgid "Server"
@@ -6125,27 +6240,28 @@ msgstr "Сервер"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:140
msgid "Session Timeout:"
-msgstr ""
+msgstr "Время ожидания сеанса"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:165
msgid "Enable SSL (paths relative to Deluge config folder)"
-msgstr ""
+msgstr "Включить SSL (пути относ. папки конфиг. Deluge)"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:177
msgid "Private Key:"
-msgstr ""
+msgstr "Закрытый ключ:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:187
msgid "Certificate:"
-msgstr ""
+msgstr "Сертификат:"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:205
msgid "WebUI Language Changed"
-msgstr ""
+msgstr "Язык веб-интерфейса изменён"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:207
msgid "Do you want to refresh the page now to use the new language?"
msgstr ""
+"Вы хотите обновить страницу сейчас, чтобы сразу использовать новый язык?"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:210
msgid "Refresh"
@@ -6153,27 +6269,27 @@ msgstr "Обновить"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:244
msgid "Invalid Password"
-msgstr ""
+msgstr "Неверный пароль"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:245
msgid "Your passwords don't match!"
-msgstr ""
+msgstr "Введённые пароли не совпадают!"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:260
msgid "Your old password was incorrect!"
-msgstr ""
+msgstr "Введён неверный старый пароль!"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:269
msgid "Change Successful"
-msgstr ""
+msgstr "Успешно изменено"
#: deluge/ui/web/js/deluge-all/preferences/InterfacePage.js:270
msgid "Your password was successfully changed!"
-msgstr ""
+msgstr "Ваш пароль был изменён успешно!"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:13
msgid "Add from Url"
-msgstr ""
+msgstr "Добавить по ссылке"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:37
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:143
@@ -6182,11 +6298,11 @@ msgstr "Url"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:45
msgid "Cookies"
-msgstr ""
+msgstr "Куки"
#: deluge/ui/web/js/deluge-all/add/UrlWindow.js:99
msgid "Failed to download torrent"
-msgstr ""
+msgstr "Не удалось скачать торрент"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:133
msgid "File"
@@ -6194,15 +6310,15 @@ msgstr "Файл"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:149
msgid "Infohash"
-msgstr ""
+msgstr "Хэш данных"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:260
msgid "Uploading your torrent..."
-msgstr ""
+msgstr "Загрузка торрента…"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:292
msgid "Failed to upload torrent"
-msgstr ""
+msgstr "Не удалось загрузить торрент"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:317
msgid "Not a valid torrent"
@@ -6210,23 +6326,23 @@ msgstr "Недействительный торрент"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:50
msgid "Move Completed Folder"
-msgstr ""
+msgstr "Каталог для перемещения завершённых загрузок"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:85
msgid "Max Down Speed"
-msgstr ""
+msgstr "Макс. ск. загрузки"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:93
msgid "Max Up Speed"
-msgstr ""
+msgstr "Макс. скор. отдачи"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:125
msgid "Add In Paused State"
-msgstr "Поставить на паузу"
+msgstr "Добавить остановленным"
#: deluge/ui/web/js/deluge-all/add/OptionsTab.js:161
msgid "Super Seed"
-msgstr ""
+msgstr "Супер-раздача"
#: deluge/ui/web/js/deluge-all/add/FilesTab.js:43
msgid "Download"
diff --git a/deluge/i18n/si.po b/deluge/i18n/si.po
index 61266b337..fdaad7910 100644
--- a/deluge/i18n/si.po
+++ b/deluge/i18n/si.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4452,16 +4452,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4470,7 +4470,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/sk.po b/deluge/i18n/sk.po
index 33aaf0acc..c5dadee18 100644
--- a/deluge/i18n/sk.po
+++ b/deluge/i18n/sk.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4441,16 +4441,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4459,7 +4459,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/sl.po b/deluge/i18n/sl.po
index 51c231c7c..6a0f9f6fa 100644
--- a/deluge/i18n/sl.po
+++ b/deluge/i18n/sl.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4445,16 +4445,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4463,7 +4463,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/sr.po b/deluge/i18n/sr.po
index 5f406fbd5..59f9c2a0e 100644
--- a/deluge/i18n/sr.po
+++ b/deluge/i18n/sr.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4469,16 +4469,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4487,7 +4487,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/sv.po b/deluge/i18n/sv.po
index 051eecdf8..18f82f2e1 100644
--- a/deluge/i18n/sv.po
+++ b/deluge/i18n/sv.po
@@ -16,8 +16,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
"Language: sv\n"
#: deluge/common.py:411
@@ -4529,16 +4529,16 @@ msgstr "_Normal"
msgid "_High"
msgstr "_Hög"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Deluge arbetsgruppen"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr "Deluge är en lätt, fri, plattformsoberoende BitTorrent-klient."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4552,7 +4552,7 @@ msgstr ""
"hastighetsgränser. Eftersom Deluge utnyttjar biblioteket libtorrent har den "
"en omfattande lista över funktionerna."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -6325,5 +6325,5 @@ msgstr "Tid kvar:"
msgid "Date Added:"
msgstr "Tillagd:"
-#~ msgid "<b>Languge</b>"
+#~ msgid "<b>Language</b>"
#~ msgstr "<b>Språk</b>"
diff --git a/deluge/i18n/ta.po b/deluge/i18n/ta.po
index 9d604d887..015c304ae 100644
--- a/deluge/i18n/ta.po
+++ b/deluge/i18n/ta.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/te.po b/deluge/i18n/te.po
index 6ec7cde20..357d260cb 100644
--- a/deluge/i18n/te.po
+++ b/deluge/i18n/te.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/th.po b/deluge/i18n/th.po
index 8f27d04db..f95396009 100644
--- a/deluge/i18n/th.po
+++ b/deluge/i18n/th.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/tl.po b/deluge/i18n/tl.po
index e02de2943..db38679ce 100644
--- a/deluge/i18n/tl.po
+++ b/deluge/i18n/tl.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/tlh.po b/deluge/i18n/tlh.po
index fa8c2a849..646e1a2a4 100644
--- a/deluge/i18n/tlh.po
+++ b/deluge/i18n/tlh.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/tr.po b/deluge/i18n/tr.po
index 65cb2cc02..69dee5694 100644
--- a/deluge/i18n/tr.po
+++ b/deluge/i18n/tr.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4541,18 +4541,18 @@ msgstr "_Normal"
msgid "_High"
msgstr "_Yüksek"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Deluge Takımı"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
"Deluge, hafiftir, Özgür Yazılımdır, platformlar arası bir BitTorrent "
"istemcisidir."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4566,7 +4566,7 @@ msgstr ""
"özellikleri içerir. Deluge, libtorrent kütüphanesini yoğun bir şekilde "
"kullandığından, sağlanan özelliklerin kapsamlı bir listesine sahiptir."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/uk.po b/deluge/i18n/uk.po
index 3ab7af1db..2559bbfad 100644
--- a/deluge/i18n/uk.po
+++ b/deluge/i18n/uk.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2021-07-07 20:36+0000\n"
+"PO-Revision-Date: 2021-12-23 09:01+0000\n"
"Last-Translator: ma$terok <Unknown>\n"
"Language-Team: Ukrainian <uk@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -301,7 +301,7 @@ msgstr "Залишилося"
#: deluge/ui/common.py:58 deluge/ui/gtk3/torrentview.py:373
#: deluge/ui/web/js/deluge-all/TorrentGrid.js:165
msgid "Ratio"
-msgstr "Відношення"
+msgstr "Рейтинг"
#: deluge/ui/common.py:59 deluge/ui/gtk3/torrentview.py:340
#: deluge/ui/gtk3/peers_tab.py:133
@@ -1797,28 +1797,28 @@ msgid ""
"delete this exception statement from all source files in the program, then "
"also delete it here."
msgstr ""
-"Ця проґрама є вільним проґрамним забезпеченням, Ви можете розповсюджувати та "
-"/ або модифікувати його на умовах GNU General Public License, опублікованій "
-"Free Software Foundation, версії 3, або (за вашим вибором) будь-якої "
-"пізнішої версії.\n"
+"Ця програма є безкоштовним програмним забезпеченням; ви можете поширювати "
+"його та/або змінювати відповідно до умов Загальної публічної ліцензії GNU, "
+"опублікованої Фондом вільного програмного забезпечення; або версії 3 "
+"Ліцензії, або (на ваш вибір) будь якої пізнішої версії.\n"
"\n"
-"Ця проґрама поширюється зі сподіванням, що вона буде корисною, але БЕЗ БУДЬ-"
-"ЯКИХ ЗАПОРУК, навіть без запорук КОМЕРЦІЙНОЇ ЦІННОСТІ чи ПРИДАТНОСТІ ДЛЯ "
-"КОНКРЕТНИХ ЦІЛЕЙ. Див громадської ліцензії GNU General ліцензії для більш "
-"докладної інформації.\n"
+"Ця програма розповсюджується в надії, що вона буде корисною, але БЕЗ БУДЬ "
+"ЯКИХ ГАРАНТІЙ; навіть без прихованої гарантії КОМЕРЦІЙНОЇ ВИГОДИ чи "
+"ПРИДАТНОСТІ ДЛЯ КОНКРЕТНИХ ЦІЛЕЙ. Докладніше дивися у Загальнодоступній "
+"ліцензії GNU.\n"
"\n"
-"Ви повинні були отримати копію Public License GNU General разом з цією "
-"проґрамою, якщо ні, див <http://www.gnu.org/licenses>.\n"
+"Ви повинні були отримати копію Загальної публічної ліцензії GNU разом із "
+"цією програмою; якщо ні, дивися <http://www.gnu.org/licenses>.\n"
"\n"
-"Крім того, в якості особливого винятку, власників авторських прав дати "
-"дозвіл, щоб зв'язати код частини цієї проґрами з бібліотекою OpenSSL. Ви "
-"повинні коритися GNU General Public License у всіх відношеннях для всіх код, "
-"що використовується, крім OpenSSL.\n"
+"Крім того, як особливий виняток, власники авторських прав дають дозвіл "
+"пов'язувати код частин цієї програми з бібліотекою OpenSSL. Ви повинні "
+"дотримуватись Загальної загальнодоступної ліцензії GNU у всіх відношеннях до "
+"всього коду, що використовується, крім OpenSSL.\n"
"\n"
-"Якщо ви зміните файл (и) з виключенням цього, ви можете розширити це виняток "
-"для вашої версії файлу (ів), але ви не зобов'язані це робити. Якщо ви не "
-"хочете зробити це, вилучіть це виняток заява від вашої версії. Якщо ви "
-"вилучите це виняток заява всі вихідні файли в проґраму, то і вилучати її тут."
+"Якщо ви змінюєте файл (и) з цим винятком, ви можете поширити цей виняток на "
+"свою версію файлу (ів), але ви не зобов’язані це робити. Якщо ви не хочете "
+"цього робити, видаліть цей виняток із своєї версії. Якщо ви видалите цей "
+"виняток із усіх вихідних файлів програми, також видаліть його тут."
#: deluge/ui/gtk3/aboutdialog.py:829
#: deluge/ui/web/js/deluge-all/AboutWindow.js:65
@@ -3223,12 +3223,12 @@ msgstr "Активні торенти"
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:187
#: deluge/ui/web/render/tab_status.html:4
msgid "Share Ratio:"
-msgstr "Співвідношення:"
+msgstr "Рейтинг:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:101
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:142
msgid "Time Ratio:"
-msgstr ""
+msgstr "Співвідношення часу:"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:102
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:157
@@ -3239,7 +3239,7 @@ msgstr "Час(хв)"
#: deluge/ui/console/modes/preferences/preference_panes.py:590
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:118
msgid "Seeding Rotation"
-msgstr ""
+msgstr "Ротація завершених"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:104
msgid "Pause Torrent"
@@ -3249,7 +3249,7 @@ msgstr "Призупинити торент"
#: deluge/ui/console/modes/preferences/preference_panes.py:627
#: deluge/ui/web/js/deluge-all/preferences/QueuePage.js:173
msgid "Share Ratio Reached"
-msgstr ""
+msgstr "Співвідношення досягнуто"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:107
msgid ""
@@ -3456,7 +3456,7 @@ msgstr "Примусово використовувати проксі"
#: deluge/ui/console/modes/preferences/preference_panes.py:671
#: deluge/ui/web/js/deluge-all/preferences/ProxyField.js:141
msgid "Hide Client Identity"
-msgstr ""
+msgstr "Приховати дані клієнта"
#: deluge/ui/gtk3/glade/preferences_dialog.ui.h:149
msgid ""
@@ -3728,7 +3728,7 @@ msgstr "Додати завантажені файли"
#: deluge/ui/gtk3/glade/remove_torrent_dialog.ui.h:6
msgid "(This is permanent!)"
-msgstr "(Це постійно!)"
+msgstr "Назавжди"
#: deluge/ui/gtk3/glade/connect_peer_dialog.ui.h:1
msgid "Add Peer"
@@ -3754,7 +3754,7 @@ msgstr "<b>Загальні</b>"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:5
msgid "Show path entry"
-msgstr ""
+msgstr "Вказати шлях"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:6
msgid "Show file chooser"
@@ -3766,7 +3766,7 @@ msgstr "Показати ім'я теки"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:8
msgid "Path Chooser Type"
-msgstr ""
+msgstr "Тип вибору шляху"
#: deluge/ui/gtk3/glade/path_combo_chooser.ui.h:9
msgid "Enable autocomplete"
@@ -3917,7 +3917,7 @@ msgstr "Додати хеш даних"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:4
msgid "From Infohash"
-msgstr ""
+msgstr "З хешу"
#: deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui.h:5
msgid "Infohash:"
@@ -4021,12 +4021,12 @@ msgstr "Рейтинг роздачі:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:10
msgid "ETA Time:"
-msgstr ""
+msgstr "Строк завантаження:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:11
#: deluge/ui/web/render/tab_status.html:13
msgid "Last Transfer:"
-msgstr ""
+msgstr "Остання передача:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:12
#: deluge/ui/web/render/tab_status.html:23
@@ -4036,7 +4036,7 @@ msgstr "Час активности:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:13
#: deluge/ui/web/render/tab_status.html:20
msgid "Complete Seen:"
-msgstr ""
+msgstr "Завершені:"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:14
#: deluge/ui/web/render/tab_status.html:24
@@ -4108,7 +4108,7 @@ msgstr "Зупинити роздачу при коефіцієнті:"
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:13
#: deluge/ui/web/js/deluge-all/details/OptionsTab.js:233
msgid "Remove at ratio"
-msgstr "Вилучити при коефіцієнті"
+msgstr "Вилучити при рейтингу"
#: deluge/ui/gtk3/glade/main_window.tabs.ui.h:44
msgid "Bandwidth Limits"
@@ -4450,7 +4450,7 @@ msgstr "Межа _слотів роздачі"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:5
msgid "Stop seed at _ratio"
-msgstr ""
+msgstr "Зупинити при спів_відношенні"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:6
msgid "_Auto Managed"
@@ -4458,7 +4458,7 @@ msgstr "Автоматичне керування"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:7
msgid "_Super Seeding"
-msgstr ""
+msgstr "_Супер-сід"
#: deluge/ui/gtk3/glade/torrent_menu.options.ui.h:8
msgid "_Change Ownership"
@@ -4534,16 +4534,16 @@ msgstr "_Звичайний"
msgid "_High"
msgstr "_Високий"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Команда Deluge"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr "Deluge - легкий, вільний, багатоплатформовий BitTorrent клієнт."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4557,7 +4557,7 @@ msgstr ""
"швидкості потоку. Оскільки Deluge активно використовує бібліотеку "
"libtorrent, він має вичерпний перелік наданих функцій."
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -4657,7 +4657,7 @@ msgstr "Встановлюване значення"
#: deluge/ui/console/cmdline/commands/manage.py:53
#: deluge/ui/console/cmdline/commands/config.py:98
msgid "one or more keys separated by space"
-msgstr ""
+msgstr "один або кілька ключів, розділених пробілами"
#: deluge/ui/console/cmdline/commands/rm.py:33
msgid "Also removes the torrent data"
@@ -4681,15 +4681,19 @@ msgstr ""
#: deluge/ui/console/cmdline/commands/resume.py:22
msgid "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
-msgstr ""
+msgstr "Використання: відновити [ * | <torrent-id> [<torrent-id> ...] ]"
#: deluge/ui/console/cmdline/commands/resume.py:29
msgid "One or more torrent ids. Use \"*\" to resume all torrents"
msgstr ""
+"Один або кілька ідентифікаторів торрентів. Для відновлення всіх торрентів "
+"використовуйте \"*\""
#: deluge/ui/console/cmdline/commands/pause.py:29
msgid "One or more torrent ids. Use \"*\" to pause all torrents"
msgstr ""
+"Один або кілька ідентифікаторів торрентів. Використовуйте \"*\" для "
+"призупинення всіх торрентів"
#: deluge/ui/console/cmdline/commands/add.py:38
msgid "Download folder for torrent"
@@ -4760,7 +4764,7 @@ msgstr "Шлях для переміщення торентів"
#: deluge/ui/console/cmdline/commands/debug.py:26
msgid "The new state"
-msgstr ""
+msgstr "Новий стан"
#: deluge/ui/console/cmdline/commands/help.py:29
msgid "One or more commands"
@@ -4861,10 +4865,12 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:216
msgid "Third tab lists all remaining torrents in command line mode"
msgstr ""
+"На третій вкладці перераховані всі торренти, що залишилися, у режимі "
+"командного рядка"
#: deluge/ui/console/modes/preferences/preference_panes.py:221
msgid "Torrents per tab press"
-msgstr ""
+msgstr "Торренти за натисненням вкладки"
#: deluge/ui/console/modes/preferences/preference_panes.py:234
#: deluge/plugins/Label/deluge_label/data/label_options.ui.h:18
@@ -4939,7 +4945,7 @@ msgstr "Вихідні"
#: deluge/ui/console/modes/preferences/preference_panes.py:413
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:38
msgid "Global Bandwidth Usage"
-msgstr ""
+msgstr "Глобальне використання пропускної здатності"
#: deluge/ui/console/modes/preferences/preference_panes.py:416
#: deluge/ui/console/modes/preferences/preference_panes.py:469
@@ -4977,7 +4983,7 @@ msgstr "Обмежувати швидкість із урахуванням ви
#: deluge/ui/console/modes/preferences/preference_panes.py:466
#: deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js:148
msgid "Per Torrent Bandwidth Usage"
-msgstr ""
+msgstr "Налаштування швидкості торенту"
#: deluge/ui/console/modes/preferences/preference_panes.py:513
msgid "Yes, please send anonymous statistics."
@@ -4997,11 +5003,11 @@ msgstr "Загалом"
#: deluge/ui/console/modes/preferences/preference_panes.py:593
msgid "Share Ratio"
-msgstr "Коефіцієнт обміну"
+msgstr "Рейтинг"
#: deluge/ui/console/modes/preferences/preference_panes.py:601
msgid "Time Ratio"
-msgstr ""
+msgstr "Співвідношення часу"
#: deluge/ui/console/modes/preferences/preference_panes.py:609
msgid "Time (m)"
@@ -5009,7 +5015,7 @@ msgstr "Час (хв)"
#: deluge/ui/console/modes/preferences/preference_panes.py:633
msgid "Remove torrent (Unchecked pauses torrent)"
-msgstr ""
+msgstr "Видалити торент (Не позначені призупиняют торент)"
#: deluge/ui/console/modes/preferences/preference_panes.py:646
msgid "Proxy Settings"
@@ -5037,7 +5043,7 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:712
msgid "Blocks Written"
-msgstr ""
+msgstr "Записано блоків"
#: deluge/ui/console/modes/preferences/preference_panes.py:716
msgid "Writes"
@@ -5049,11 +5055,11 @@ msgstr ""
#: deluge/ui/console/modes/preferences/preference_panes.py:725
msgid "Blocks Read"
-msgstr ""
+msgstr "Зчитано блоків"
#: deluge/ui/console/modes/preferences/preference_panes.py:729
msgid "Blocks Read hit"
-msgstr ""
+msgstr "Зчитано блоків з кешу"
#: deluge/ui/console/modes/preferences/preference_panes.py:732
msgid "Reads"
@@ -5245,6 +5251,8 @@ msgid ""
"\"Watch Folder\" directory and \"Copy of .torrent files to\" directory "
"cannot be the same!"
msgstr ""
+"\"Тека для стеження\" і тека \"Копіювати .torrent файли до\" не можуть бути "
+"однаковими!"
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:462
#: deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py:466
@@ -5334,6 +5342,9 @@ msgid ""
"the .torrent will copied to the chosen directory\n"
"and deleted from the watch folder."
msgstr ""
+"Після додавання торрента,\n"
+".torrent буде скопійовано до вибраної теки\n"
+"і видалено з теки для стеження."
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:20
msgid ""
@@ -5361,7 +5372,7 @@ msgstr "<b>Тека для завантаження</b>"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:26
msgid "Set move completed folder"
-msgstr ""
+msgstr "Вкажіть теку для переміщення завершених"
#: deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui.h:27
msgid "<b>Move Completed</b>"
@@ -5628,7 +5639,7 @@ msgstr "Спливні вікна вимкнено"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:177
msgid "libnotify is not installed"
-msgstr ""
+msgstr "libnotify не встановлено"
#: deluge/plugins/Notifications/deluge_notifications/gtkui.py:185
msgid "Failed to popup notification"
@@ -6291,7 +6302,7 @@ msgstr "Файл"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:149
msgid "Infohash"
-msgstr ""
+msgstr "Хеш-сума"
#: deluge/ui/web/js/deluge-all/add/AddWindow.js:260
msgid "Uploading your torrent..."
@@ -6337,5 +6348,5 @@ msgstr "ETA:"
msgid "Date Added:"
msgstr "Дата додавання:"
-#~ msgid "<b>Languge</b>"
+#~ msgid "<b>Language</b>"
#~ msgstr "<b>Мова</b>"
diff --git a/deluge/i18n/ur.po b/deluge/i18n/ur.po
index 0ba1818c3..6c1cbe27d 100644
--- a/deluge/i18n/ur.po
+++ b/deluge/i18n/ur.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/util.py b/deluge/i18n/util.py
index 81530c2ee..f6920fbb3 100644
--- a/deluge/i18n/util.py
+++ b/deluge/i18n/util.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007,2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,7 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
+import builtins
import ctypes
import gettext
import locale
@@ -17,8 +15,6 @@ import os
import sys
from glob import glob
-from six.moves import builtins
-
import deluge.common
from .languages import LANGUAGES
@@ -55,7 +51,7 @@ def get_languages():
name = LANGUAGES[lang_code]
lang.append([lang_code, _(name)])
- lang = sorted(lang, key=lambda l: l[1])
+ lang = sorted(lang, key=lambda k: k[1])
return lang
@@ -69,18 +65,21 @@ def set_language(lang):
:param lang: the language, e.g. "en", "de" or "en_GB"
:type lang: str
"""
+ if not lang:
+ return
+
# Necessary to set these environment variables for GtkBuilder
deluge.common.set_env_variable('LANGUAGE', lang) # Windows/Linux
deluge.common.set_env_variable('LANG', lang) # For OSX
- translations_path = get_translations_path()
try:
- ro = gettext.translation(
- 'deluge', localedir=translations_path, languages=[lang]
+ translation = gettext.translation(
+ 'deluge', localedir=get_translations_path(), languages=[lang]
)
- ro.install()
- except IOError as ex:
- log.warning('IOError when loading translations: %s', ex)
+ except OSError:
+ log.warning('Unable to find translation (.mo) to set language: %s', lang)
+ else:
+ translation.install()
def setup_mock_translation(warn_msg=None):
@@ -110,19 +109,17 @@ def setup_translation():
gettext.bindtextdomain(I18N_DOMAIN, translations_path)
gettext.textdomain(I18N_DOMAIN)
- # Workaround for Python 2 unicode gettext (keyword removed in Py3).
- kwargs = {} if not deluge.common.PY2 else {'unicode': True}
-
- gettext.install(I18N_DOMAIN, translations_path, names=['ngettext'], **kwargs)
+ gettext.install(I18N_DOMAIN, translations_path, names=['ngettext'])
builtins.__dict__['_n'] = builtins.__dict__['ngettext']
def load_libintl(libintls):
errors = []
+ libintl = None
for library in libintls:
try:
libintl = ctypes.cdll.LoadLibrary(library)
except OSError as ex:
- errors.append(ex)
+ errors.append(str(ex))
else:
break
diff --git a/deluge/i18n/vi.po b/deluge/i18n/vi.po
index 58d97f1f1..c8e21166e 100644
--- a/deluge/i18n/vi.po
+++ b/deluge/i18n/vi.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4466,16 +4466,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4484,7 +4484,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/zh_CN.po b/deluge/i18n/zh_CN.po
index bdd70febd..8472bd909 100644
--- a/deluge/i18n/zh_CN.po
+++ b/deluge/i18n/zh_CN.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: deluge\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2019-11-12 14:55+0000\n"
-"PO-Revision-Date: 2019-12-06 05:38+0000\n"
+"PO-Revision-Date: 2022-12-28 05:35+0000\n"
"Last-Translator: 玉堂白鹤 <yjwork@qq.com>\n"
"Language-Team: Chinese (Simplified) <zh_CN@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: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
"Language: zh_CN\n"
#: deluge/common.py:411
@@ -132,18 +132,18 @@ msgstr "设置日志级别(无、错误、警告、信息、调试)"
msgid ""
"Enable logfile rotation, with optional maximum logfile size, default: "
"%(const)s (Logfile rotation count is 5)"
-msgstr ""
+msgstr "启用日志文件循环,使用可选的最大日志文件大小,默认值:%(const)s (日志文件循环计数为 5)"
#: deluge/argparserbase.py:223
msgid "Quieten logging output (Same as `--loglevel none`)"
-msgstr ""
+msgstr "静态日志记录输出(与“--loglevel none”相同)"
#: deluge/argparserbase.py:231
#, python-format
msgid ""
"Profile %(prog)s with cProfile. Outputs to stdout unless a filename is "
"specified"
-msgstr ""
+msgstr "使用 cProfile 的配置文件 %(prog)s 。除非指定了文件名,否则输出到标准输出"
#: deluge/argparserbase.py:351
msgid "Process Control Options"
@@ -151,7 +151,7 @@ msgstr "进程控制选项"
#: deluge/argparserbase.py:357
msgid "Pidfile to store the process id"
-msgstr ""
+msgstr "用于存储进程 id 的 Pidfile"
#: deluge/argparserbase.py:365
msgid "Do not daemonize (fork) this process"
@@ -2421,7 +2421,7 @@ msgstr " 种子已加入队列"
#: deluge/ui/gtk3/torrentview.py:421
msgid "Torrent is shared between other Deluge users or not."
-msgstr ""
+msgstr "Torrent 是否在其他 Deluge 用户之间共享。"
#: deluge/ui/gtk3/removetorrentdialog.py:67
msgid "Remove the selected torrents?"
@@ -4443,16 +4443,16 @@ msgstr "正常(_N)"
msgid "_High"
msgstr "高(_H)"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr "Deluge Team"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr "Deluge 是一个轻量级的免费软件,跨平台的 BitTorrent 客户端。"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4461,7 +4461,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
@@ -6203,5 +6203,5 @@ msgstr "估计剩余时间:"
msgid "Date Added:"
msgstr "添加日期:"
-#~ msgid "<b>Languge</b>"
+#~ msgid "<b>Language</b>"
#~ msgstr "<b>语言</b>"
diff --git a/deluge/i18n/zh_HK.po b/deluge/i18n/zh_HK.po
index 2f316ae27..678969200 100644
--- a/deluge/i18n/zh_HK.po
+++ b/deluge/i18n/zh_HK.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
#: deluge/common.py:411
msgid "B"
@@ -4422,16 +4422,16 @@ msgstr ""
msgid "_High"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4440,7 +4440,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/i18n/zh_TW.po b/deluge/i18n/zh_TW.po
index 716f94308..b14d00679 100644
--- a/deluge/i18n/zh_TW.po
+++ b/deluge/i18n/zh_TW.po
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2021-09-10 18:01+0000\n"
-"X-Generator: Launchpad (build aca2013fd8cd2fea408d75f89f9bc012fbab307d)\n"
+"X-Launchpad-Export-Date: 2023-11-06 19:12+0000\n"
+"X-Generator: Launchpad (build f1e537f62ee3967c2b3f24dd10eacf1696334fe6)\n"
"Language: zh_TW\n"
#: deluge/common.py:411
@@ -4426,16 +4426,16 @@ msgstr "普通(_N)"
msgid "_High"
msgstr "高(_H)"
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:1
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:1
msgid "Deluge Team"
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:2
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:2
msgid ""
"Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:3
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:3
msgid ""
"Deluge contains the common features to BitTorrent clients such as Protocol "
"Encryption, DHT, Local Peer Discovery (LSD), Peer Exchange (PEX), UPnP, NAT-"
@@ -4444,7 +4444,7 @@ msgid ""
"of the features provided."
msgstr ""
-#: deluge/ui/data/share/appdata/deluge.appdata.xml.in.h:4
+#: deluge/ui/data/share/metainfo/deluge.metainfo.xml.in.h:4
msgid ""
"Deluge has been designed to run as both a normal standalone desktop "
"application and as a client-server. In Thinclient mode a Deluge daemon "
diff --git a/deluge/log.py b/deluge/log.py
index fa83091d1..ef31f4ddc 100644
--- a/deluge/log.py
+++ b/deluge/log.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2010 Pedro Algarvio <pedro@algarvio.me>
@@ -9,8 +8,6 @@
#
"""Logging functions"""
-from __future__ import unicode_literals
-
import inspect
import logging
import logging.handlers
@@ -39,7 +36,7 @@ MAX_LOGGER_NAME_LENGTH = 10
class Logging(LoggingLoggerClass):
def __init__(self, logger_name):
- super(Logging, self).__init__(logger_name)
+ super().__init__(logger_name)
# This makes module name padding increase to the biggest module name
# so that logs keep readability.
@@ -54,39 +51,31 @@ class Logging(LoggingLoggerClass):
)
)
- @defer.inlineCallbacks
def garbage(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.log(self, 1, msg, *args, **kwargs)
+ LoggingLoggerClass.log(self, 1, msg, *args, **kwargs)
- @defer.inlineCallbacks
def trace(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.log(self, 5, msg, *args, **kwargs)
+ LoggingLoggerClass.log(self, 5, msg, *args, **kwargs)
- @defer.inlineCallbacks
def debug(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.debug(self, msg, *args, **kwargs)
+ LoggingLoggerClass.debug(self, msg, *args, **kwargs)
- @defer.inlineCallbacks
def info(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.info(self, msg, *args, **kwargs)
+ LoggingLoggerClass.info(self, msg, *args, **kwargs)
- @defer.inlineCallbacks
def warning(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.warning(self, msg, *args, **kwargs)
+ LoggingLoggerClass.warning(self, msg, *args, **kwargs)
warn = warning
- @defer.inlineCallbacks
def error(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.error(self, msg, *args, **kwargs)
+ LoggingLoggerClass.error(self, msg, *args, **kwargs)
- @defer.inlineCallbacks
def critical(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.critical(self, msg, *args, **kwargs)
+ LoggingLoggerClass.critical(self, msg, *args, **kwargs)
- @defer.inlineCallbacks
def exception(self, msg, *args, **kwargs):
- yield LoggingLoggerClass.exception(self, msg, *args, **kwargs)
+ LoggingLoggerClass.exception(self, msg, *args, **kwargs)
def findCaller(self, *args, **kwargs): # NOQA: N802
f = logging.currentframe().f_back
@@ -102,10 +91,7 @@ class Logging(LoggingLoggerClass):
continue
rv = (co.co_filename, f.f_lineno, co.co_name, None)
break
- if common.PY2:
- return rv[:-1]
- else:
- return rv
+ return rv
levels = {
@@ -161,12 +147,15 @@ def setup_logger(
handler_cls = getattr(
logging.handlers, 'WatchedFileHandler', logging.FileHandler
)
- handler = handler_cls(filename, mode=filemode, encoding='utf-8')
+ try:
+ handler = handler_cls(filename, mode=filemode, encoding='utf-8')
+ except FileNotFoundError:
+ handler = logging.StreamHandler(stream=output_stream)
+ log = logging.getLogger(__name__)
+ log.error(f'Unable to write to log file `{filename}`')
else:
handler = logging.StreamHandler(stream=output_stream)
- handler.setLevel(level)
-
formatter = logging.Formatter(
DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, datefmt='%H:%M:%S'
)
@@ -218,7 +207,7 @@ class TwistedLoggingObserver(PythonLoggingObserver):
def tweak_logging_levels():
"""This function allows tweaking the logging levels for all or some loggers.
- This is mostly usefull for developing purposes hence the contents of the
+ This is mostly useful for developing purposes hence the contents of the
file are NOT like regular deluge config file's.
To use is, create a file named "logging.conf" on your Deluge's config dir
@@ -243,7 +232,7 @@ def tweak_logging_levels():
log.warning(
'logging.conf found! tweaking logging levels from %s', logging_config_file
)
- with open(logging_config_file, 'r') as _file:
+ with open(logging_config_file) as _file:
for line in _file:
if line.strip().startswith('#'):
continue
@@ -314,7 +303,7 @@ Triggering code:
"""
-class _BackwardsCompatibleLOG(object):
+class _BackwardsCompatibleLOG:
def __getattribute__(self, name):
import warnings
diff --git a/deluge/maketorrent.py b/deluge/maketorrent.py
index 528638e43..07a2a9d17 100644
--- a/deluge/maketorrent.py
+++ b/deluge/maketorrent.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import os
from hashlib import sha1 as sha
@@ -32,7 +29,7 @@ class InvalidPieceSize(Exception):
pass
-class TorrentMetadata(object):
+class TorrentMetadata:
"""This class is used to create .torrent files.
Examples:
@@ -120,7 +117,7 @@ class TorrentMetadata(object):
files = []
padding_count = 0
# Collect a list of file paths and add padding files if necessary
- for (dirpath, dirnames, filenames) in os.walk(self.data_path):
+ for dirpath, dirnames, filenames in os.walk(self.data_path):
for index, filename in enumerate(filenames):
size = get_path_size(
os.path.join(self.data_path, dirpath, filename)
diff --git a/deluge/metafile.py b/deluge/metafile.py
index 8c28c7e91..81a371ff3 100644
--- a/deluge/metafile.py
+++ b/deluge/metafile.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Original file from BitTorrent-5.3-GPL.tar.gz
# Copyright (C) Bram Cohen
@@ -11,12 +10,13 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
+import copy
import logging
import os.path
import time
+from enum import Enum
from hashlib import sha1 as sha
+from hashlib import sha256
import deluge.component as component
from deluge.bencode import bencode
@@ -44,7 +44,36 @@ def dummy(*v):
pass
-class RemoteFileProgress(object):
+class TorrentFormat(str, Enum):
+ V1 = 'v1'
+ V2 = 'v2'
+ HYBRID = 'hybrid'
+
+ @classmethod
+ def _missing_(cls, value):
+ if not value:
+ return None
+
+ value = value.lower()
+ for member in cls:
+ if member.value == value:
+ return member
+
+ def to_lt_flag(self):
+ if self.value == 'v1':
+ return 64
+ if self.value == 'v2':
+ return 32
+ return 0
+
+ def includes_v1(self):
+ return self == self.__class__.V1 or self == self.__class__.HYBRID
+
+ def includes_v2(self):
+ return self == self.__class__.V2 or self == self.__class__.HYBRID
+
+
+class RemoteFileProgress:
def __init__(self, session_id):
self.session_id = session_id
@@ -54,7 +83,7 @@ class RemoteFileProgress(object):
)
-def make_meta_file(
+def make_meta_file_content(
path,
url,
piece_length,
@@ -63,24 +92,16 @@ def make_meta_file(
comment=None,
safe=None,
content_type=None,
- target=None,
webseeds=None,
name=None,
private=False,
created_by=None,
trackers=None,
+ torrent_format=TorrentFormat.V1,
):
data = {'creation date': int(gmtime())}
if url:
data['announce'] = url.strip()
- a, b = os.path.split(path)
- if not target:
- if b == '':
- f = a + '.torrent'
- else:
- f = os.path.join(a, b + '.torrent')
- else:
- f = target
if progress is None:
progress = dummy
@@ -92,10 +113,20 @@ def make_meta_file(
if session_id:
progress = RemoteFileProgress(session_id)
- info = makeinfo(path, piece_length, progress, name, content_type, private)
+ info, piece_layers = makeinfo(
+ path,
+ piece_length,
+ progress,
+ name,
+ content_type,
+ private,
+ torrent_format,
+ )
# check_info(info)
data['info'] = info
+ if piece_layers is not None:
+ data['piece layers'] = piece_layers
if title:
data['title'] = title.encode('utf8')
if comment:
@@ -124,8 +155,55 @@ def make_meta_file(
data['announce-list'] = trackers
data['encoding'] = 'UTF-8'
- with open(f, 'wb') as file_:
- file_.write(bencode(utf8_encode_structure(data)))
+ return bencode(utf8_encode_structure(data))
+
+
+def default_meta_file_path(content_path):
+ a, b = os.path.split(content_path)
+ if b == '':
+ f = a + '.torrent'
+ else:
+ f = os.path.join(a, b + '.torrent')
+ return f
+
+
+def make_meta_file(
+ path,
+ url,
+ piece_length,
+ progress=None,
+ title=None,
+ comment=None,
+ safe=None,
+ content_type=None,
+ target=None,
+ webseeds=None,
+ name=None,
+ private=False,
+ created_by=None,
+ trackers=None,
+):
+ if not target:
+ target = default_meta_file_path(path)
+
+ file_content = make_meta_file_content(
+ path,
+ url,
+ piece_length,
+ progress=progress,
+ title=title,
+ comment=comment,
+ safe=safe,
+ content_type=content_type,
+ webseeds=webseeds,
+ name=name,
+ private=private,
+ created_by=created_by,
+ trackers=trackers,
+ )
+
+ with open(target, 'wb') as file_:
+ file_.write(file_content)
def calcsize(path):
@@ -135,101 +213,237 @@ def calcsize(path):
return total
-def makeinfo(path, piece_length, progress, name=None, content_type=None, private=False):
+def _next_pow2(num):
+ import math
+
+ if not num:
+ return 1
+ return 2 ** math.ceil(math.log2(num))
+
+
+def _sha256_merkle_root(leafs, nb_leafs, padding, in_place=True) -> bytes:
+ """
+ Build the root of the merkle hash tree from the (possibly incomplete) leafs layer.
+ If len(leafs) < nb_leafs, it will be padded with the padding repeated as many times
+ as needed to have nb_leafs in total.
+ """
+ if not in_place:
+ leafs = copy.copy(leafs)
+
+ while nb_leafs > 1:
+ nb_leafs = nb_leafs // 2
+ for i in range(nb_leafs):
+ node1 = leafs[2 * i] if 2 * i < len(leafs) else padding
+ node2 = leafs[2 * i + 1] if 2 * i + 1 < len(leafs) else padding
+ h = sha256(node1)
+ h.update(node2)
+ if i < len(leafs):
+ leafs[i] = h.digest()
+ else:
+ leafs.append(h.digest())
+ return leafs[0] if leafs else padding
+
+
+def _sha256_buffer_blocks(buffer, block_len):
+ import math
+
+ nb_blocks = math.ceil(len(buffer) / block_len)
+ blocks = [
+ sha256(buffer[i * block_len : (i + 1) * block_len]).digest()
+ for i in range(nb_blocks)
+ ]
+ return blocks
+
+
+def makeinfo_lt(
+ path, piece_length, name=None, private=False, torrent_format=TorrentFormat.V1
+):
+ """
+ Make info using via the libtorrent library.
+ """
+ from deluge._libtorrent import lt
+
+ if not name:
+ name = os.path.split(path)[1]
+
+ fs = lt.file_storage()
+ if os.path.isfile(path):
+ lt.add_files(fs, path)
+ else:
+ for p, f in subfiles(path):
+ fs.add_file(os.path.join(name, *p), os.path.getsize(f))
+ torrent = lt.create_torrent(
+ fs, piece_size=piece_length, flags=torrent_format.to_lt_flag()
+ )
+
+ lt.set_piece_hashes(torrent, os.path.dirname(path))
+ torrent.set_priv(private)
+
+ t = torrent.generate()
+ info = t[b'info']
+ pieces_layers = t.get(b'piece layers', None)
+
+ return info, pieces_layers
+
+
+def makeinfo(
+ path,
+ piece_length,
+ progress,
+ name=None,
+ content_type=None,
+ private=False,
+ torrent_format=TorrentFormat.V1,
+):
# HEREDAVE. If path is directory, how do we assign content type?
+
+ v2_block_len = 2**14 # 16 KiB
+ v2_blocks_per_piece = 1
+ v2_block_padding = b''
+ v2_piece_padding = b''
+ if torrent_format.includes_v2():
+ if _next_pow2(piece_length) != piece_length or piece_length < v2_block_len:
+ raise ValueError(
+ 'Bittorrent v2 piece size must be a power of 2; and bigger than 16 KiB'
+ )
+
+ v2_blocks_per_piece = piece_length // v2_block_len
+ v2_block_padding = bytes(32) # 32 = size of sha256 in bytes
+ v2_piece_padding = _sha256_merkle_root(
+ [], nb_leafs=v2_blocks_per_piece, padding=v2_block_padding
+ )
+
path = os.path.abspath(path)
- piece_count = 0
+ files = []
+ pieces = []
+ file_tree = {}
+ piece_layers = {}
if os.path.isdir(path):
- subs = sorted(subfiles(path))
- pieces = []
- sh = sha()
- done = 0
- fs = []
+ if not name:
+ name = os.path.split(path)[1]
+ subs = subfiles(path)
+ if torrent_format.includes_v2():
+ subs = sorted(subs)
+ length = None
totalsize = 0.0
- totalhashed = 0
for p, f in subs:
totalsize += os.path.getsize(f)
- if totalsize >= piece_length:
- import math
-
- num_pieces = math.ceil(totalsize / piece_length)
+ else:
+ name = os.path.split(path)[1]
+ subs = [([name], path)]
+ length = os.path.getsize(path)
+ totalsize = length
+ is_multi_file = len(subs) > 1
+ sh = sha()
+ done = 0
+ totalhashed = 0
+
+ next_progress_event = piece_length
+ for p, f in subs:
+ file_pieces_v2 = []
+ pos = 0
+ size = os.path.getsize(f)
+ p2 = [n.encode('utf8') for n in p]
+ if content_type:
+ files.append(
+ {b'length': size, b'path': p2, b'content_type': content_type}
+ ) # HEREDAVE. bad for batch!
else:
- num_pieces = 1
-
- for p, f in subs:
- pos = 0
- size = os.path.getsize(f)
- p2 = [n.encode('utf8') for n in p]
- if content_type:
- fs.append(
- {'length': size, 'path': p2, 'content_type': content_type}
- ) # HEREDAVE. bad for batch!
- else:
- fs.append({'length': size, 'path': p2})
- with open(f, 'rb') as file_:
- while pos < size:
- a = min(size - pos, piece_length - done)
- sh.update(file_.read(a))
- done += a
- pos += a
- totalhashed += a
-
- if done == piece_length:
- pieces.append(sh.digest())
- piece_count += 1
- done = 0
- sh = sha()
- progress(piece_count, num_pieces)
- if done > 0:
+ files.append({b'length': size, b'path': p2})
+ with open(f, 'rb') as file_:
+ while pos < size:
+ to_read = min(size - pos, piece_length)
+ buffer = memoryview(file_.read(to_read))
+ pos += to_read
+
+ if torrent_format.includes_v1():
+ a = piece_length - done
+ for sub_buffer in (buffer[:a], buffer[a:]):
+ if sub_buffer:
+ sh.update(sub_buffer)
+ done += len(sub_buffer)
+
+ if done == piece_length:
+ pieces.append(sh.digest())
+ done = 0
+ sh = sha()
+ if torrent_format.includes_v2():
+ block_hashes = _sha256_buffer_blocks(buffer, v2_block_len)
+ num_leafs = v2_blocks_per_piece
+ if size <= piece_length:
+ # The special case when the file is smaller than a piece: only pad till the next power of 2
+ num_leafs = _next_pow2(len(block_hashes))
+ root = _sha256_merkle_root(
+ block_hashes, num_leafs, v2_block_padding, in_place=True
+ )
+ file_pieces_v2.append(root)
+
+ totalhashed += to_read
+ if totalhashed >= next_progress_event:
+ next_progress_event = totalhashed + piece_length
+ progress(totalhashed, totalsize)
+
+ if torrent_format == TorrentFormat.HYBRID and is_multi_file and done > 0:
+ # Add padding file to force piece-alignment
+ padding = piece_length - done
+ sh.update(bytes(padding))
+ files.append(
+ {
+ b'length': padding,
+ b'attr': b'p',
+ b'path': [b'.pad', str(padding).encode()],
+ }
+ )
pieces.append(sh.digest())
- piece_count += 1
- progress(piece_count, num_pieces)
-
- if not name:
- name = os.path.split(path)[1]
-
- return {
- 'pieces': b''.join(pieces),
- 'piece length': piece_length,
- 'files': fs,
- 'name': name.encode('utf8'),
- 'private': private,
- }
- else:
- size = os.path.getsize(path)
- if size >= piece_length:
- num_pieces = size // piece_length
+ done = 0
+ sh = sha()
+
+ if torrent_format.includes_v2():
+ # add file to the `file tree` and, if needed, to the `piece layers` structures
+ pieces_root = _sha256_merkle_root(
+ file_pieces_v2,
+ _next_pow2(len(file_pieces_v2)),
+ v2_piece_padding,
+ in_place=False,
+ )
+ dst_directory = file_tree
+ for directory in p2[:-1]:
+ dst_directory = dst_directory.setdefault(directory, {})
+ dst_directory[p2[-1]] = {
+ b'': {
+ b'length': size,
+ b'pieces root': pieces_root,
+ }
+ }
+ if len(file_pieces_v2) > 1:
+ piece_layers[pieces_root] = b''.join(file_pieces_v2)
+
+ if done > 0:
+ pieces.append(sh.digest())
+ progress(totalsize, totalsize)
+
+ info = {
+ b'piece length': piece_length,
+ b'name': name.encode('utf8'),
+ }
+ if private:
+ info[b'private'] = 1
+ if content_type:
+ info[b'content_type'] = content_type
+ if torrent_format.includes_v1():
+ info[b'pieces'] = b''.join(pieces)
+ if is_multi_file:
+ info[b'files'] = files
else:
- num_pieces = 1
-
- pieces = []
- p = 0
- with open(path, 'rb') as _file:
- while p < size:
- x = _file.read(min(piece_length, size - p))
- pieces.append(sha(x).digest())
- piece_count += 1
- p += piece_length
- if p > size:
- p = size
- progress(piece_count, num_pieces)
- name = os.path.split(path)[1].encode('utf8')
- if content_type is not None:
- return {
- 'pieces': b''.join(pieces),
- 'piece length': piece_length,
- 'length': size,
- 'name': name,
- 'content_type': content_type,
- 'private': private,
+ info[b'length'] = length
+ if torrent_format.includes_v2():
+ info.update(
+ {
+ b'meta version': 2,
+ b'file tree': file_tree,
}
- return {
- 'pieces': b''.join(pieces),
- 'piece length': piece_length,
- 'length': size,
- 'name': name,
- 'private': private,
- }
+ )
+ return info, piece_layers if torrent_format.includes_v2() else None
def subfiles(d):
diff --git a/deluge/path_chooser_common.py b/deluge/path_chooser_common.py
index 0f93feef6..0ea92341c 100644
--- a/deluge/path_chooser_common.py
+++ b/deluge/path_chooser_common.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 Bro <bro.development@gmail.com>
#
@@ -8,12 +7,8 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
-from deluge.common import PY2
-
def is_hidden(filepath):
def has_hidden_attribute(filepath):
@@ -45,7 +40,7 @@ def get_completion_paths(args):
:param args: options
:type args: dict
:returns: the args argument containing the available completions for the completion_text
- :rtype: list
+ :rtype: dict
"""
args['paths'] = []
@@ -54,10 +49,7 @@ def get_completion_paths(args):
def get_subdirs(dirname):
try:
- if PY2:
- return os.walk(dirname).__next__[1]
- else:
- return next(os.walk(dirname))[1]
+ return next(os.walk(dirname))[1]
except StopIteration:
# Invalid dirname
return []
diff --git a/deluge/pluginmanagerbase.py b/deluge/pluginmanagerbase.py
index f838cd3cb..835dbb268 100644
--- a/deluge/pluginmanagerbase.py
+++ b/deluge/pluginmanagerbase.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -9,8 +8,7 @@
"""PluginManagerBase"""
-from __future__ import unicode_literals
-
+import email
import logging
import os.path
@@ -37,7 +35,7 @@ METADATA_KEYS = [
]
DEPRECATION_WARNING = """
-The plugin %s is not using the "deluge.plugins" namespace.
+The plugin %s is not using the "deluge_" namespace.
In order to avoid package name clashes between regular python packages and
deluge plugins, the way deluge plugins should be created has changed.
If you're seeing this message and you're not the developer of the plugin which
@@ -47,7 +45,7 @@ git repository to have an idea of what needs to be changed.
"""
-class PluginManagerBase(object):
+class PluginManagerBase:
"""PluginManagerBase is a base class for PluginManagers to inherit"""
def __init__(self, config_file, entry_name):
@@ -105,7 +103,9 @@ class PluginManagerBase(object):
for dirname in plugin_dirs:
pkg_resources.working_set.add_entry(dirname)
- self.pkg_env = pkg_resources.Environment(plugin_dirs, None)
+ self.pkg_env = pkg_resources.Environment(
+ plugin_dirs, platform=None, python=None
+ )
self.available_plugins = []
for name in self.pkg_env:
@@ -129,7 +129,7 @@ class PluginManagerBase(object):
"""
if plugin_name not in self.available_plugins:
- log.warning('Cannot enable non-existant plugin %s', plugin_name)
+ log.warning('Cannot enable non-existent plugin %s', plugin_name)
return defer.succeed(False)
if plugin_name in self.plugins:
@@ -162,7 +162,7 @@ class PluginManagerBase(object):
log.exception(ex)
return_d = defer.fail(False)
- if not instance.__module__.startswith('deluge.plugins.'):
+ if not instance.__module__.startswith('deluge_'):
import warnings
warnings.warn_explicit(
@@ -243,7 +243,7 @@ class PluginManagerBase(object):
del self.plugins[name]
self.config['enabled_plugins'].remove(name)
except Exception as ex:
- log.warning('Problems occured disabling plugin: %s', name)
+ log.warning('Problems occurred disabling plugin: %s', name)
log.debug(ex)
ret = False
else:
@@ -255,28 +255,25 @@ class PluginManagerBase(object):
def get_plugin_info(self, name):
"""Returns a dictionary of plugin info from the metadata"""
- info = {}.fromkeys(METADATA_KEYS)
- last_header = ''
- cont_lines = []
- # Missing plugin info
+
if not self.pkg_env[name]:
- log.warning('Failed to retrive info for plugin: %s', name)
- for k in info:
- info[k] = 'not available'
+ log.warning('Failed to retrieve info for plugin: %s', name)
+ info = {}.fromkeys(METADATA_KEYS, '')
+ info['Name'] = info['Version'] = 'not available'
return info
- for line in self.pkg_env[name][0].get_metadata('PKG-INFO').splitlines():
- if not line:
- continue
- if line[0] in ' \t' and (
- len(line.split(':', 1)) == 1 or line.split(':', 1)[0] not in info
- ):
- # This is a continuation
- cont_lines.append(line.strip())
- else:
- if cont_lines:
- info[last_header] = '\n'.join(cont_lines).strip()
- cont_lines = []
- if line.split(':', 1)[0] in info:
- last_header = line.split(':', 1)[0]
- info[last_header] = line.split(':', 1)[1].strip()
+
+ pkg_info = self.pkg_env[name][0].get_metadata('PKG-INFO')
+ return self.parse_pkg_info(pkg_info)
+
+ @staticmethod
+ def parse_pkg_info(pkg_info):
+ metadata_msg = email.message_from_string(pkg_info)
+ metadata_ver = metadata_msg.get('Metadata-Version')
+
+ info = {key: metadata_msg.get(key, '') for key in METADATA_KEYS}
+
+ # Optional Description field in body (Metadata spec >=2.1)
+ if not info['Description'] and metadata_ver.startswith('2'):
+ info['Description'] = metadata_msg.get_payload().strip()
+
return info
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/__init__.py b/deluge/plugins/AutoAdd/deluge_autoadd/__init__.py
index a409cfcce..5f5e76616 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/__init__.py
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 GazpachoKing <chase.sterling@gmail.com>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -22,7 +19,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class Gtk3UIPlugin(PluginInitBase):
@@ -30,7 +27,7 @@ class Gtk3UIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(Gtk3UIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -38,4 +35,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/common.py b/deluge/plugins/AutoAdd/deluge_autoadd/common.py
index 9b4b1e703..6a790cbd5 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/common.py
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/core.py b/deluge/plugins/AutoAdd/deluge_autoadd/core.py
index 9a2260610..271d5f0d9 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/core.py
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 GazpachoKing <chase.sterling@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -13,8 +12,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import shutil
@@ -30,7 +27,7 @@ import deluge.configmanager
from deluge._libtorrent import lt
from deluge.common import AUTH_LEVEL_ADMIN, is_magnet
from deluge.core.rpcserver import export
-from deluge.error import AddTorrentError
+from deluge.error import AddTorrentError, InvalidTorrentError
from deluge.event import DelugeEvent
from deluge.plugins.pluginbase import CorePluginBase
@@ -83,7 +80,6 @@ def check_input(cond, message):
class Core(CorePluginBase):
def enable(self):
-
# reduce typing, assigning some values to self...
self.config = deluge.configmanager.ConfigManager('autoadd.conf', DEFAULT_PREFS)
self.config.run_converter((0, 1), 2, self.__migrate_config_1_to_2)
@@ -152,7 +148,7 @@ class Core(CorePluginBase):
try:
with open(filename, file_mode) as _file:
filedump = _file.read()
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to open %s: %s', filename, ex)
raise ex
@@ -161,7 +157,10 @@ class Core(CorePluginBase):
# Get the info to see if any exceptions are raised
if not magnet:
- lt.torrent_info(lt.bdecode(filedump))
+ decoded_torrent = lt.bdecode(filedump)
+ if decoded_torrent is None:
+ raise InvalidTorrentError('Torrent file failed decoding.')
+ lt.torrent_info(decoded_torrent)
return filedump
@@ -169,9 +168,9 @@ class Core(CorePluginBase):
log.debug('Attempting to open %s for splitting magnets.', filename)
magnets = []
try:
- with open(filename, 'r') as _file:
+ with open(filename) as _file:
magnets = list(filter(len, _file.read().splitlines()))
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to open %s: %s', filename, ex)
if len(magnets) < 2:
@@ -196,7 +195,7 @@ class Core(CorePluginBase):
try:
with open(mname, 'w') as _mfile:
_mfile.write(magnet)
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to open %s: %s', mname, ex)
return magnets
@@ -271,7 +270,7 @@ class Core(CorePluginBase):
try:
filedump = self.load_torrent(filepath, magnet)
- except (IOError, EOFError) as ex:
+ except (OSError, EOFError, RuntimeError, InvalidTorrentError) as ex:
# If torrent is invalid, keep track of it so can try again on the next pass.
# This catches torrent files that may not be fully saved to disk at load time.
log.debug('Torrent is invalid: %s', ex)
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd.js b/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd.js
index ebed40180..e68fce307 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd.js
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd.js
@@ -42,22 +42,21 @@ Deluge.ux.preferences.AutoAddPage = Ext.extend(Ext.Panel, {
dataIndex: 'enabled',
tpl: new Ext.XTemplate('{enabled:this.getCheckbox}', {
getCheckbox: function (checked, selected) {
- Deluge.ux.AutoAdd.onClickFunctions[
- selected.id
- ] = function () {
- if (selected.enabled) {
- deluge.client.autoadd.disable_watchdir(
- selected.id
- );
- checked = false;
- } else {
- deluge.client.autoadd.enable_watchdir(
- selected.id
- );
- checked = true;
- }
- autoAdd.updateWatchDirs();
- };
+ Deluge.ux.AutoAdd.onClickFunctions[selected.id] =
+ function () {
+ if (selected.enabled) {
+ deluge.client.autoadd.disable_watchdir(
+ selected.id
+ );
+ checked = false;
+ } else {
+ deluge.client.autoadd.enable_watchdir(
+ selected.id
+ );
+ checked = true;
+ }
+ autoAdd.updateWatchDirs();
+ };
return (
'<input id="enabled-' +
selected.id +
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.js b/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.js
index 797d76585..7ec4448b9 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.js
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.js
@@ -90,9 +90,8 @@ Deluge.ux.AutoAdd.AutoAddWindowBase = Ext.extend(Ext.Window, {
options['enabled'] = Ext.getCmp('enabled').getValue();
options['path'] = Ext.getCmp('path').getValue();
- options['download_location'] = Ext.getCmp(
- 'download_location'
- ).getValue();
+ options['download_location'] =
+ Ext.getCmp('download_location').getValue();
options['move_completed_path'] = Ext.getCmp(
'move_completed_path'
).getValue();
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui b/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui
index a4cd364e9..f1870f183 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/data/autoadd_options.ui
@@ -150,8 +150,6 @@
<property name="tooltip_text" translatable="yes">If a .torrent file is added to this directory,
it will be added to the session.</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -284,8 +282,6 @@ and it will remain in the same directory.</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="text" translatable="yes">.added</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -329,8 +325,6 @@ and deleted from the watch folder.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -445,8 +439,6 @@ also delete the .torrent file used to add it.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -534,8 +526,6 @@ also delete the .torrent file used to add it.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -799,8 +789,6 @@ also delete the .torrent file used to add it.</property>
<object class="GtkSpinButton" id="max_download_speed">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
@@ -815,8 +803,6 @@ also delete the .torrent file used to add it.</property>
<object class="GtkSpinButton" id="max_upload_speed">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment2</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
@@ -833,8 +819,6 @@ also delete the .torrent file used to add it.</property>
<object class="GtkSpinButton" id="max_connections">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment3</property>
<property name="climb_rate">1</property>
</object>
@@ -850,8 +834,6 @@ also delete the .torrent file used to add it.</property>
<object class="GtkSpinButton" id="max_upload_slots">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment4</property>
<property name="climb_rate">1</property>
</object>
@@ -1063,8 +1045,6 @@ also delete the .torrent file used to add it.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment5</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py b/deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py
index 35531419b..80fb9fcde 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 GazpachoKing <chase.sterling@gmail.com>
#
@@ -12,14 +11,12 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
# isort:imports-thirdparty
from gi.repository import Gtk
@@ -41,7 +38,7 @@ class IncompatibleOption(Exception):
pass
-class OptionsDialog(object):
+class OptionsDialog:
spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio']
spin_int_ids = ['max_upload_slots', 'max_connections']
chk_ids = [
@@ -327,7 +324,7 @@ class OptionsDialog(object):
dialogs.ErrorDialog(_('Incompatible Option'), str(ex), self.dialog).run()
def on_error_show(self, result):
- d = dialogs.ErrorDialog(_('Error'), result.value.exception_msg, self.dialog)
+ d = dialogs.ErrorDialog(_('Error'), result.value.message, self.dialog)
result.cleanFailure()
d.run()
diff --git a/deluge/plugins/AutoAdd/deluge_autoadd/webui.py b/deluge/plugins/AutoAdd/deluge_autoadd/webui.py
index 7f36ba659..d328432fe 100644
--- a/deluge/plugins/AutoAdd/deluge_autoadd/webui.py
+++ b/deluge/plugins/AutoAdd/deluge_autoadd/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 GazpachoKing <chase.sterling@gmail.com>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
diff --git a/deluge/plugins/AutoAdd/setup.py b/deluge/plugins/AutoAdd/setup.py
index fcd018395..5a01ee9aa 100644
--- a/deluge/plugins/AutoAdd/setup.py
+++ b/deluge/plugins/AutoAdd/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 GazpachoKing <chase.sterling@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/__init__.py b/deluge/plugins/Blocklist/deluge_blocklist/__init__.py
index 96ccc02ae..40ce1d18f 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/__init__.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -17,7 +14,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -25,7 +22,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -33,4 +30,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/common.py b/deluge/plugins/Blocklist/deluge_blocklist/common.py
index a9299cd2e..35b2f87c5 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/common.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,13 +11,10 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from functools import wraps
from sys import exc_info
-import six
from pkg_resources import resource_filename
@@ -47,7 +43,7 @@ def raises_errors_as(error):
return func(self, *args, **kwargs)
except Exception:
(value, tb) = exc_info()[1:]
- six.reraise(error, value, tb)
+ raise error(value).with_traceback(tb) from None
return wrapper
@@ -74,7 +70,7 @@ class BadIP(Exception):
_message = None
def __init__(self, message):
- super(BadIP, self).__init__(message)
+ super().__init__(message)
def __set_message(self, message):
self._message = message
@@ -86,7 +82,7 @@ class BadIP(Exception):
del __get_message, __set_message
-class IP(object):
+class IP:
__slots__ = ('q1', 'q2', 'q3', 'q4', '_long')
def __init__(self, q1, q2, q3, q4):
@@ -109,7 +105,7 @@ class IP(object):
@classmethod
def parse(cls, ip):
try:
- q1, q2, q3, q4 = [int(q) for q in ip.split('.')]
+ q1, q2, q3, q4 = (int(q) for q in ip.split('.'))
except ValueError:
raise BadIP(_('The IP address "%s" is badly formed' % ip))
if q1 < 0 or q2 < 0 or q3 < 0 or q4 < 0:
@@ -169,7 +165,7 @@ class IP(object):
return self.long == other.long
def __repr__(self):
- return '<%s long=%s address="%s">' % (
+ return '<{} long={} address="{}">'.format(
self.__class__.__name__,
self.long,
self.address,
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/core.py b/deluge/plugins/Blocklist/deluge_blocklist/core.py
index a096b8ac9..176576740 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/core.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2009-2010 John Garland <johnnybg+deluge@gmail.com>
@@ -8,14 +7,13 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
import os
import shutil
import time
from datetime import datetime, timedelta
from email.utils import formatdate
+from urllib.parse import urljoin
from twisted.internet import defer, threads
from twisted.internet.task import LoopingCall
@@ -32,12 +30,6 @@ from .common import IP, BadIP
from .detect import UnknownFormatError, create_reader, detect_compression, detect_format
from .readers import ReaderParseError
-try:
- from urllib.parse import urljoin
-except ImportError:
- # PY2 fallback
- from urlparse import urljoin # pylint: disable=ungrouped-imports
-
# TODO: check return values for deferred callbacks
# TODO: review class attributes for redundancy
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist.js b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist.js
index 8f1b93c21..3c10b81bc 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist.js
+++ b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist.js
@@ -55,7 +55,7 @@ Deluge.ux.preferences.BlocklistPage = Ext.extend(Ext.Panel, {
});
this.checkListDays = this.SettingsFset.add({
- fieldLabel: _('Check for new list every:'),
+ fieldLabel: _('Check for new list every (days):'),
labelSeparator: '',
name: 'check_list_days',
value: 4,
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.png b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.png
index 9441214cb..f1a02e756 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.png
+++ b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.png
Binary files differ
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui
index 013d8e70e..8c1f7a7ab 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui
+++ b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_pref.ui
@@ -53,8 +53,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -124,8 +122,6 @@
<object class="GtkSpinButton" id="spin_check_days">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
</object>
<packing>
@@ -139,7 +135,7 @@
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">Check for new list every:</property>
+ <property name="label" translatable="yes">Check for new list every (days):</property>
<property name="xalign">0</property>
</object>
<packing>
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/decompressers.py b/deluge/plugins/Blocklist/deluge_blocklist/decompressers.py
index 35211b706..cd2ee8cad 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/decompressers.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/decompressers.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -8,8 +7,6 @@
#
# pylint: disable=redefined-builtin
-from __future__ import unicode_literals
-
import bz2
import gzip
import zipfile
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/detect.py b/deluge/plugins/Blocklist/deluge_blocklist/detect.py
index 262d5de4f..43ad305f2 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/detect.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/detect.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from .decompressers import BZipped2, GZipped, Zipped
from .readers import EmuleReader, PeerGuardianReader, SafePeerReader
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/gtkui.py b/deluge/plugins/Blocklist/deluge_blocklist/gtkui.py
index b6e5d5508..e6105cda0 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/gtkui.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,14 +6,12 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from datetime import datetime
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
# isort:imports-thirdparty
from gi.repository import Gtk
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py b/deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py
index ba410c2cf..b5fb181a2 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/peerguardian.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Steve 'Tarka' Smith (tarka@internode.on.net)
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import gzip
import logging
import socket
@@ -23,14 +20,14 @@ class PGException(Exception):
# Incrementally reads PeerGuardian blocklists v1 and v2.
# See http://wiki.phoenixlabs.org/wiki/P2B_Format
-class PGReader(object):
+class PGReader:
def __init__(self, filename):
log.debug('PGReader loading: %s', filename)
try:
with gzip.open(filename, 'rb') as _file:
self.fd = _file
- except IOError:
+ except OSError:
log.debug('Blocklist: PGReader: Incorrect file type or list is corrupt')
# 4 bytes, should be 0xffffffff
@@ -65,8 +62,5 @@ class PGReader(object):
return (start, end)
- # Python 2 compatibility
- next = __next__
-
def close(self):
self.fd.close()
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/readers.py b/deluge/plugins/Blocklist/deluge_blocklist/readers.py
index 4079e849e..14230ed77 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/readers.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/readers.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import re
@@ -23,7 +20,7 @@ class ReaderParseError(Exception):
pass
-class BaseReader(object):
+class BaseReader:
"""Base reader for blocklist files"""
def __init__(self, _file):
diff --git a/deluge/plugins/Blocklist/deluge_blocklist/webui.py b/deluge/plugins/Blocklist/deluge_blocklist/webui.py
index 3da43c451..8ba49117d 100644
--- a/deluge/plugins/Blocklist/deluge_blocklist/webui.py
+++ b/deluge/plugins/Blocklist/deluge_blocklist/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -26,6 +23,5 @@ FORMAT_LIST = [
class WebUI(WebPluginBase):
-
scripts = [get_resource('blocklist.js')]
debug_scripts = scripts
diff --git a/deluge/plugins/Blocklist/setup.py b/deluge/plugins/Blocklist/setup.py
index 54ad002a3..2aa683407 100644
--- a/deluge/plugins/Blocklist/setup.py
+++ b/deluge/plugins/Blocklist/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
diff --git a/deluge/plugins/Execute/deluge_execute/__init__.py b/deluge/plugins/Execute/deluge_execute/__init__.py
index c6d55f4ec..3edfc4b1d 100644
--- a/deluge/plugins/Execute/deluge_execute/__init__.py
+++ b/deluge/plugins/Execute/deluge_execute/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -17,7 +14,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -25,7 +22,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -33,4 +30,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Execute/deluge_execute/common.py b/deluge/plugins/Execute/deluge_execute/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/Execute/deluge_execute/common.py
+++ b/deluge/plugins/Execute/deluge_execute/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/Execute/deluge_execute/core.py b/deluge/plugins/Execute/deluge_execute/core.py
index f5fa2c2e6..6d33e546d 100644
--- a/deluge/plugins/Execute/deluge_execute/core.py
+++ b/deluge/plugins/Execute/deluge_execute/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import hashlib
import logging
import os
diff --git a/deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui b/deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui
index e2a5cd507..5d6354bc9 100644
--- a/deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui
+++ b/deluge/plugins/Execute/deluge_execute/data/execute_prefs.ui
@@ -71,8 +71,6 @@
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
diff --git a/deluge/plugins/Execute/deluge_execute/gtkui.py b/deluge/plugins/Execute/deluge_execute/gtkui.py
index c0c720089..f56a6defa 100644
--- a/deluge/plugins/Execute/deluge_execute/gtkui.py
+++ b/deluge/plugins/Execute/deluge_execute/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,13 +6,11 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
# isort:imports-thirdparty
from gi.repository import Gtk
@@ -41,7 +38,7 @@ EVENT_MAP = {
EVENTS = ['complete', 'added', 'removed']
-class ExecutePreferences(object):
+class ExecutePreferences:
def __init__(self, plugin):
self.plugin = plugin
diff --git a/deluge/plugins/Execute/deluge_execute/webui.py b/deluge/plugins/Execute/deluge_execute/webui.py
index 8327001b8..3586371a7 100644
--- a/deluge/plugins/Execute/deluge_execute/webui.py
+++ b/deluge/plugins/Execute/deluge_execute/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -19,6 +16,5 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
-
scripts = [get_resource('execute.js')]
debug_scripts = scripts
diff --git a/deluge/plugins/Execute/setup.py b/deluge/plugins/Execute/setup.py
index 174d1a356..b65c1bd3d 100644
--- a/deluge/plugins/Execute/setup.py
+++ b/deluge/plugins/Execute/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
diff --git a/deluge/plugins/Extractor/deluge_extractor/__init__.py b/deluge/plugins/Extractor/deluge_extractor/__init__.py
index 6db72b63b..87d1584cd 100644
--- a/deluge/plugins/Extractor/deluge_extractor/__init__.py
+++ b/deluge/plugins/Extractor/deluge_extractor/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -21,7 +18,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -29,7 +26,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -37,4 +34,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Extractor/deluge_extractor/common.py b/deluge/plugins/Extractor/deluge_extractor/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/Extractor/deluge_extractor/common.py
+++ b/deluge/plugins/Extractor/deluge_extractor/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/Extractor/deluge_extractor/core.py b/deluge/plugins/Extractor/deluge_extractor/core.py
index 8fa5bd3da..23b2a00ac 100644
--- a/deluge/plugins/Extractor/deluge_extractor/core.py
+++ b/deluge/plugins/Extractor/deluge_extractor/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import errno
import logging
import os
@@ -37,14 +34,11 @@ if windows_check():
'C:\\Program Files (x86)\\7-Zip\\7z.exe',
]
- try:
- import winreg
- except ImportError:
- import _winreg as winreg # For Python 2.
+ import winreg
try:
hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\7-Zip')
- except WindowsError:
+ except OSError:
pass
else:
win_7z_path = os.path.join(winreg.QueryValueEx(hkey, 'Path')[0], '7z.exe')
diff --git a/deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui b/deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui
index 6f34b4411..9e8070b50 100644
--- a/deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui
+++ b/deluge/plugins/Extractor/deluge_extractor/data/extractor_prefs.ui
@@ -62,8 +62,6 @@
<child>
<object class="GtkEntry" id="entry_path">
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/plugins/Extractor/deluge_extractor/gtkui.py b/deluge/plugins/Extractor/deluge_extractor/gtkui.py
index 113b33f6d..a754a5f22 100644
--- a/deluge/plugins/Extractor/deluge_extractor/gtkui.py
+++ b/deluge/plugins/Extractor/deluge_extractor/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,13 +10,11 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
# isort:imports-thirdparty
from gi.repository import Gtk
diff --git a/deluge/plugins/Extractor/deluge_extractor/webui.py b/deluge/plugins/Extractor/deluge_extractor/webui.py
index feb7b4a83..0f58658d9 100644
--- a/deluge/plugins/Extractor/deluge_extractor/webui.py
+++ b/deluge/plugins/Extractor/deluge_extractor/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -23,6 +20,5 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
-
scripts = [get_resource('extractor.js')]
debug_scripts = scripts
diff --git a/deluge/plugins/Extractor/setup.py b/deluge/plugins/Extractor/setup.py
index 25ab153b3..09385c608 100644
--- a/deluge/plugins/Extractor/setup.py
+++ b/deluge/plugins/Extractor/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
diff --git a/deluge/plugins/Label/deluge_label/__init__.py b/deluge/plugins/Label/deluge_label/__init__.py
index bc0b0f243..a6c72f82b 100644
--- a/deluge/plugins/Label/deluge_label/__init__.py
+++ b/deluge/plugins/Label/deluge_label/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -21,7 +18,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -29,7 +26,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -37,4 +34,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Label/deluge_label/common.py b/deluge/plugins/Label/deluge_label/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/Label/deluge_label/common.py
+++ b/deluge/plugins/Label/deluge_label/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/Label/deluge_label/core.py b/deluge/plugins/Label/deluge_label/core.py
index b16156c91..c28490b46 100644
--- a/deluge/plugins/Label/deluge_label/core.py
+++ b/deluge/plugins/Label/deluge_label/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -15,8 +14,6 @@
torrent-label core plugin.
adds a status field for tracker.
"""
-from __future__ import unicode_literals
-
import logging
import re
@@ -140,6 +137,7 @@ class Core(CorePluginBase):
log.debug('post_torrent_remove')
if torrent_id in self.torrent_labels:
del self.torrent_labels[torrent_id]
+ self.config.save()
# Utils #
def clean_config(self):
@@ -194,8 +192,7 @@ class Core(CorePluginBase):
"""remove a label"""
check_input(label_id in self.labels, _('Unknown Label'))
del self.labels[label_id]
- self.clean_config()
- self.config.save()
+ self.save_config()
def _set_torrent_options(self, torrent_id, label_id):
options = self.labels[label_id]
diff --git a/deluge/plugins/Label/deluge_label/data/label.js b/deluge/plugins/Label/deluge_label/data/label.js
index b84ea6b69..a0327e397 100644
--- a/deluge/plugins/Label/deluge_label/data/label.js
+++ b/deluge/plugins/Label/deluge_label/data/label.js
@@ -148,8 +148,7 @@ Deluge.ux.LabelOptionsWindow = Ext.extend(Ext.Window, {
xtype: 'fieldset',
border: false,
labelWidth: 1,
- style:
- 'margin-bottom: 0px; padding-bottom: 0px;',
+ style: 'margin-bottom: 0px; padding-bottom: 0px;',
items: [
{
xtype: 'checkbox',
@@ -218,8 +217,7 @@ Deluge.ux.LabelOptionsWindow = Ext.extend(Ext.Window, {
xtype: 'fieldset',
border: false,
labelWidth: 1,
- style:
- 'margin-bottom: 0px; padding-bottom: 0px;',
+ style: 'margin-bottom: 0px; padding-bottom: 0px;',
items: [
{
xtype: 'checkbox',
@@ -260,8 +258,7 @@ Deluge.ux.LabelOptionsWindow = Ext.extend(Ext.Window, {
width: 60,
decimalPrecision: 2,
incrementValue: 0.1,
- style:
- 'position: relative; left: 100px',
+ style: 'position: relative; left: 100px',
disabled: true,
},
{
@@ -285,8 +282,7 @@ Deluge.ux.LabelOptionsWindow = Ext.extend(Ext.Window, {
xtype: 'fieldset',
border: false,
labelWidth: 1,
- style:
- 'margin-bottom: 0px; padding-bottom: 0px;',
+ style: 'margin-bottom: 0px; padding-bottom: 0px;',
items: [
{
xtype: 'checkbox',
@@ -339,8 +335,7 @@ Deluge.ux.LabelOptionsWindow = Ext.extend(Ext.Window, {
xtype: 'fieldset',
border: false,
labelWidth: 1,
- style:
- 'margin-bottom: 0px; padding-bottom: 0px;',
+ style: 'margin-bottom: 0px; padding-bottom: 0px;',
items: [
{
xtype: 'checkbox',
@@ -408,9 +403,8 @@ Deluge.ux.LabelOptionsWindow = Ext.extend(Ext.Window, {
onOkClick: function () {
var values = this.form.getForm().getFieldValues();
if (values['auto_add_trackers']) {
- values['auto_add_trackers'] = values['auto_add_trackers'].split(
- '\n'
- );
+ values['auto_add_trackers'] =
+ values['auto_add_trackers'].split('\n');
}
deluge.client.label.set_options(this.label, values);
this.hide();
diff --git a/deluge/plugins/Label/deluge_label/data/label_add.ui b/deluge/plugins/Label/deluge_label/data/label_add.ui
index 68f8a72b2..e55067596 100644
--- a/deluge/plugins/Label/deluge_label/data/label_add.ui
+++ b/deluge/plugins/Label/deluge_label/data/label_add.ui
@@ -141,8 +141,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="activates_default">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/plugins/Label/deluge_label/data/label_options.ui b/deluge/plugins/Label/deluge_label/data/label_options.ui
index c0fca4ff5..d390865b1 100644
--- a/deluge/plugins/Label/deluge_label/data/label_options.ui
+++ b/deluge/plugins/Label/deluge_label/data/label_options.ui
@@ -209,8 +209,6 @@
<object class="GtkSpinButton" id="max_upload_speed">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
</object>
<packing>
@@ -239,8 +237,6 @@
<object class="GtkSpinButton" id="max_download_speed">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment2</property>
</object>
<packing>
@@ -310,8 +306,6 @@
<object class="GtkSpinButton" id="max_upload_slots">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment3</property>
<property name="numeric">True</property>
</object>
@@ -342,8 +336,6 @@
<object class="GtkSpinButton" id="max_connections">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment4</property>
<property name="numeric">True</property>
</object>
@@ -483,8 +475,6 @@
<object class="GtkSpinButton" id="stop_ratio">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment5</property>
<property name="digits">2</property>
</object>
@@ -599,8 +589,6 @@
<child>
<object class="GtkEntry" id="move_completed_path_entry">
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
diff --git a/deluge/plugins/Label/deluge_label/gtkui/__init__.py b/deluge/plugins/Label/deluge_label/gtkui/__init__.py
index eeaeadcc8..617071628 100644
--- a/deluge/plugins/Label/deluge_label/gtkui/__init__.py
+++ b/deluge/plugins/Label/deluge_label/gtkui/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge import component # for systray
diff --git a/deluge/plugins/Label/deluge_label/gtkui/label_config.py b/deluge/plugins/Label/deluge_label/gtkui/label_config.py
index b1bf56de6..26c827e9a 100644
--- a/deluge/plugins/Label/deluge_label/gtkui/label_config.py
+++ b/deluge/plugins/Label/deluge_label/gtkui/label_config.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from gi.repository.Gtk import Builder
@@ -20,7 +17,7 @@ from ..common import get_resource
log = logging.getLogger(__name__)
-class LabelConfig(object):
+class LabelConfig:
"""
there used to be some options here...
"""
diff --git a/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py b/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py
index 34358db6e..9d164b284 100644
--- a/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py
+++ b/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
@@ -8,13 +7,11 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
# isort:imports-thirdparty
from gi.repository import Gtk
@@ -32,9 +29,8 @@ NO_LABEL = 'No Label'
# menu
-class LabelSidebarMenu(object):
+class LabelSidebarMenu:
def __init__(self):
-
self.treeview = component.get('FilterTreeView')
self.menu = self.treeview.menu
self.items = []
@@ -90,7 +86,7 @@ class LabelSidebarMenu(object):
for item in self.items:
item.set_sensitive(sensitive)
- # add is allways enabled.
+ # add is always enabled.
self.item_add.set_sensitive(True)
else:
# not a label -->hide everything.
@@ -107,7 +103,7 @@ class LabelSidebarMenu(object):
# dialogs:
-class AddDialog(object):
+class AddDialog:
def __init__(self):
pass
@@ -129,7 +125,7 @@ class AddDialog(object):
self.dialog.destroy()
-class OptionsDialog(object):
+class OptionsDialog:
spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio']
spin_int_ids = ['max_upload_slots', 'max_connections']
chk_ids = [
@@ -174,7 +170,7 @@ class OptionsDialog(object):
self.builder.connect_signals(self)
# Show the label name in the header label
self.builder.get_object('label_header').set_markup(
- '<b>%s:</b> %s' % (_('Label Options'), self.label)
+ '<b>{}:</b> {}'.format(_('Label Options'), self.label)
)
for chk_id, group in self.sensitive_groups:
diff --git a/deluge/plugins/Label/deluge_label/gtkui/submenu.py b/deluge/plugins/Label/deluge_label/gtkui/submenu.py
index c5f80e70c..54b659429 100644
--- a/deluge/plugins/Label/deluge_label/gtkui/submenu.py
+++ b/deluge/plugins/Label/deluge_label/gtkui/submenu.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -8,8 +7,6 @@
#
-from __future__ import unicode_literals
-
import logging
from gi.repository.Gtk import Menu, MenuItem
@@ -54,7 +51,7 @@ class LabelMenu(MenuItem):
if label == NO_LABEL:
item = MenuItem(_(NO_LABEL)) # noqa: F821
else:
- item = MenuItem(label.replace('_', '__'))
+ item = MenuItem(label)
item.connect('activate', self.on_select_label, label)
self.sub_menu.append(item)
self.show_all()
diff --git a/deluge/plugins/Label/deluge_label/test.py b/deluge/plugins/Label/deluge_label/test.py
index 5c9ffcd00..739bae429 100644
--- a/deluge/plugins/Label/deluge_label/test.py
+++ b/deluge/plugins/Label/deluge_label/test.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# -*- coding: utf-8 -*-
#
@@ -10,8 +9,6 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
from deluge.ui.client import sclient
sclient.set_core_uri()
diff --git a/deluge/plugins/Label/deluge_label/webui.py b/deluge/plugins/Label/deluge_label/webui.py
index 58c38e941..9ccfa92b5 100644
--- a/deluge/plugins/Label/deluge_label/webui.py
+++ b/deluge/plugins/Label/deluge_label/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
diff --git a/deluge/plugins/Label/setup.py b/deluge/plugins/Label/setup.py
index 567335be2..f8f2c5d3a 100644
--- a/deluge/plugins/Label/setup.py
+++ b/deluge/plugins/Label/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
diff --git a/deluge/plugins/Notifications/deluge_notifications/__init__.py b/deluge/plugins/Notifications/deluge_notifications/__init__.py
index 810e284df..d52b48de3 100644
--- a/deluge/plugins/Notifications/deluge_notifications/__init__.py
+++ b/deluge/plugins/Notifications/deluge_notifications/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Pedro Algarvio <pedro@algarvio.me>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -22,7 +19,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -30,7 +27,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -38,4 +35,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Notifications/deluge_notifications/common.py b/deluge/plugins/Notifications/deluge_notifications/common.py
index 6966122ca..9993f5ce4 100644
--- a/deluge/plugins/Notifications/deluge_notifications/common.py
+++ b/deluge/plugins/Notifications/deluge_notifications/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Pedro Algarvio <pedro@algarvio.me>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os.path
@@ -30,7 +27,7 @@ def get_resource(filename):
return resource_filename(__package__, os.path.join('data', filename))
-class CustomNotifications(object):
+class CustomNotifications:
def __init__(self, plugin_name=None):
self.custom_notifications = {'email': {}, 'popup': {}, 'blink': {}, 'sound': {}}
diff --git a/deluge/plugins/Notifications/deluge_notifications/core.py b/deluge/plugins/Notifications/deluge_notifications/core.py
index 9eede4576..aa200f9bd 100644
--- a/deluge/plugins/Notifications/deluge_notifications/core.py
+++ b/deluge/plugins/Notifications/deluge_notifications/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Pedro Algarvio <pedro@algarvio.me>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import smtplib
from email.utils import formatdate
@@ -119,7 +116,6 @@ Date: %(date)s
message = '\r\n'.join((headers + message).splitlines())
try:
- # Python 2.6
server = smtplib.SMTP(
self.config['smtp_host'], self.config['smtp_port'], timeout=60
)
@@ -152,7 +148,7 @@ Date: %(date)s
try:
try:
- server.sendmail(self.config['smtp_from'], to_addrs, message)
+ server.sendmail(self.config['smtp_from'], to_addrs, message.encode())
except smtplib.SMTPException as ex:
err_msg = (
_('There was an error sending the notification email: %s') % ex
diff --git a/deluge/plugins/Notifications/deluge_notifications/data/config.ui b/deluge/plugins/Notifications/deluge_notifications/data/config.ui
index c16b37acd..399cc9e1c 100644
--- a/deluge/plugins/Notifications/deluge_notifications/data/config.ui
+++ b/deluge/plugins/Notifications/deluge_notifications/data/config.ui
@@ -187,8 +187,6 @@
<object class="GtkEntry" id="smtp_host">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -217,8 +215,6 @@
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="width_chars">5</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
<property name="climb_rate">1</property>
<property name="numeric">True</property>
@@ -246,8 +242,6 @@
<object class="GtkEntry" id="smtp_user">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -273,8 +267,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -427,8 +419,6 @@
<object class="GtkEntry" id="smtp_from">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
diff --git a/deluge/plugins/Notifications/deluge_notifications/gtkui.py b/deluge/plugins/Notifications/deluge_notifications/gtkui.py
index cb26901a7..4dc5ff8d1 100644
--- a/deluge/plugins/Notifications/deluge_notifications/gtkui.py
+++ b/deluge/plugins/Notifications/deluge_notifications/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Pedro Algarvio <pedro@algarvio.me>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from os.path import basename
diff --git a/deluge/plugins/Notifications/deluge_notifications/test.py b/deluge/plugins/Notifications/deluge_notifications/test.py
index 2e6f9755b..013cdbfe1 100644
--- a/deluge/plugins/Notifications/deluge_notifications/test.py
+++ b/deluge/plugins/Notifications/deluge_notifications/test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# vim: sw=4 ts=4 fenc=utf-8 et
# ==============================================================================
# Copyright © 2009-2010 UfSoft.org - Pedro Algarvio <pedro@algarvio.me>
@@ -6,8 +5,6 @@
# License: BSD - Please view the LICENSE file for additional information.
# ==============================================================================
-from __future__ import unicode_literals
-
import logging
from twisted.internet import task
@@ -70,14 +67,14 @@ class TestEmailNotifications(component.Component):
def custom_email_message_provider(self, *evt_args, **evt_kwargs):
log.debug('Running custom email message provider: %s %s', evt_args, evt_kwargs)
- subject = '%s Email Subject: %s' % (self.events[0].__class__.__name__, self.n)
- message = '%s Email Message: %s' % (self.events[0].__class__.__name__, self.n)
+ subject = f'{self.events[0].__class__.__name__} Email Subject: {self.n}'
+ message = f'{self.events[0].__class__.__name__} Email Message: {self.n}'
return subject, message
def custom_popup_message_provider(self, *evt_args, **evt_kwargs):
log.debug('Running custom popup message provider: %s %s', evt_args, evt_kwargs)
- title = '%s Popup Title: %s' % (self.events[0].__class__.__name__, self.n)
- message = '%s Popup Message: %s' % (self.events[0].__class__.__name__, self.n)
+ title = f'{self.events[0].__class__.__name__} Popup Title: {self.n}'
+ message = f'{self.events[0].__class__.__name__} Popup Message: {self.n}'
return title, message
def custom_blink_message_provider(self, *evt_args, **evt_kwargs):
diff --git a/deluge/plugins/Notifications/deluge_notifications/webui.py b/deluge/plugins/Notifications/deluge_notifications/webui.py
index d3529c4f9..bf3e829d2 100644
--- a/deluge/plugins/Notifications/deluge_notifications/webui.py
+++ b/deluge/plugins/Notifications/deluge_notifications/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Pedro Algarvio <pedro@algarvio.me>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -24,7 +21,6 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
-
scripts = [get_resource('notifications.js')]
debug_scripts = scripts
diff --git a/deluge/plugins/Notifications/setup.py b/deluge/plugins/Notifications/setup.py
index 2b2f5aebc..3d8742392 100755
--- a/deluge/plugins/Notifications/setup.py
+++ b/deluge/plugins/Notifications/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Pedro Algarvio <pedro@algarvio.me>
#
diff --git a/deluge/plugins/Scheduler/deluge_scheduler/__init__.py b/deluge/plugins/Scheduler/deluge_scheduler/__init__.py
index 6db72b63b..87d1584cd 100644
--- a/deluge/plugins/Scheduler/deluge_scheduler/__init__.py
+++ b/deluge/plugins/Scheduler/deluge_scheduler/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -21,7 +18,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -29,7 +26,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -37,4 +34,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Scheduler/deluge_scheduler/common.py b/deluge/plugins/Scheduler/deluge_scheduler/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/Scheduler/deluge_scheduler/common.py
+++ b/deluge/plugins/Scheduler/deluge_scheduler/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/Scheduler/deluge_scheduler/core.py b/deluge/plugins/Scheduler/deluge_scheduler/core.py
index 388e4f0f6..10798ba42 100644
--- a/deluge/plugins/Scheduler/deluge_scheduler/core.py
+++ b/deluge/plugins/Scheduler/deluge_scheduler/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import time
diff --git a/deluge/plugins/Scheduler/deluge_scheduler/gtkui.py b/deluge/plugins/Scheduler/deluge_scheduler/gtkui.py
index 12f5fb63c..16222c835 100644
--- a/deluge/plugins/Scheduler/deluge_scheduler/gtkui.py
+++ b/deluge/plugins/Scheduler/deluge_scheduler/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
from gi.repository import Gdk, Gtk
@@ -30,7 +27,7 @@ DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
class SchedulerSelectWidget(Gtk.DrawingArea):
def __init__(self, hover):
- super(SchedulerSelectWidget, self).__init__()
+ super().__init__()
self.set_events(
Gdk.EventMask.BUTTON_PRESS_MASK
| Gdk.EventMask.BUTTON_RELEASE_MASK
diff --git a/deluge/plugins/Scheduler/deluge_scheduler/webui.py b/deluge/plugins/Scheduler/deluge_scheduler/webui.py
index 518eaa6aa..e41791638 100644
--- a/deluge/plugins/Scheduler/deluge_scheduler/webui.py
+++ b/deluge/plugins/Scheduler/deluge_scheduler/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -10,8 +9,6 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -22,6 +19,5 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
-
scripts = [get_resource('scheduler.js')]
debug_scripts = scripts
diff --git a/deluge/plugins/Scheduler/setup.py b/deluge/plugins/Scheduler/setup.py
index 71b69e9f9..3ac181d08 100644
--- a/deluge/plugins/Scheduler/setup.py
+++ b/deluge/plugins/Scheduler/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
diff --git a/deluge/plugins/Stats/deluge_stats/__init__.py b/deluge/plugins/Stats/deluge_stats/__init__.py
index a40379b9a..ca7b0bb83 100644
--- a/deluge/plugins/Stats/deluge_stats/__init__.py
+++ b/deluge/plugins/Stats/deluge_stats/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -21,7 +18,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -29,7 +26,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -37,4 +34,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Stats/deluge_stats/common.py b/deluge/plugins/Stats/deluge_stats/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/Stats/deluge_stats/common.py
+++ b/deluge/plugins/Stats/deluge_stats/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/Stats/deluge_stats/core.py b/deluge/plugins/Stats/deluge_stats/core.py
index 5f38b69b4..1be51e659 100644
--- a/deluge/plugins/Stats/deluge_stats/core.py
+++ b/deluge/plugins/Stats/deluge_stats/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
# Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
@@ -10,8 +9,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
import time
diff --git a/deluge/plugins/Stats/deluge_stats/graph.py b/deluge/plugins/Stats/deluge_stats/graph.py
index 847c253d1..ddb8f548d 100644
--- a/deluge/plugins/Stats/deluge_stats/graph.py
+++ b/deluge/plugins/Stats/deluge_stats/graph.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
# Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
@@ -14,13 +13,15 @@
port of old plugin by markybob.
"""
-from __future__ import division, unicode_literals
-
import logging
import math
import time
-from gi.repository import cairo
+import gi
+
+gi.require_foreign('cairo')
+
+import cairo # isort:skip (gi checks required before import).
log = logging.getLogger(__name__)
@@ -58,7 +59,7 @@ def change_opacity(color, opactiy):
return tuple(color)
-class Graph(object):
+class Graph:
def __init__(self):
self.width = 100
self.height = 100
@@ -103,20 +104,19 @@ class Graph(object):
def set_interval(self, interval):
self.interval = interval
- def draw_to_context(self, context, width, height):
- self.ctx = context
+ def draw_to_context(self, ctx, width, height):
self.width, self.height = width, height
- self.draw_rect(white, 0, 0, self.width, self.height)
- self.draw_graph()
- return self.ctx
+ self.draw_rect(ctx, white, 0, 0, self.width, self.height)
+ self.draw_graph(ctx)
def draw(self, width, height):
+ """Create surface with context for use in tests"""
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context(surface)
self.draw_to_context(ctx, width, height)
return surface
- def draw_x_axis(self, bounds):
+ def draw_x_axis(self, ctx, bounds):
(left, top, right, bottom) = bounds
duration = self.length * self.interval
start = self.last_update - duration
@@ -141,13 +141,13 @@ class Graph(object):
)
# + 0.5 to allign x to nearest pixel
x = int(ratio * (seconds_to_step + i * x_step) + left) + 0.5
- self.draw_x_text(text, x, bottom)
- self.draw_dotted_line(gray, x, top - 0.5, x, bottom + 0.5)
+ self.draw_x_text(ctx, text, x, bottom)
+ self.draw_dotted_line(ctx, gray, x, top - 0.5, x, bottom + 0.5)
- self.draw_line(gray, left, bottom + 0.5, right, bottom + 0.5)
+ self.draw_line(ctx, gray, left, bottom + 0.5, right, bottom + 0.5)
- def draw_graph(self):
- font_extents = self.ctx.font_extents()
+ def draw_graph(self, ctx):
+ font_extents = ctx.font_extents()
x_axis_space = font_extents[2] + 2 + self.line_size / 2
plot_height = self.height - x_axis_space
# lets say we need 2n-1*font height pixels to plot the y ticks
@@ -170,18 +170,18 @@ class Graph(object):
# find the width of the y_ticks
y_tick_text = [self.left_axis['formatter'](tick) for tick in y_ticks]
- def space_required(text):
- te = self.ctx.text_extents(text)
+ def space_required(ctx, text):
+ te = ctx.text_extents(text)
return math.ceil(te[4] - te[0])
- y_tick_width = max((space_required(text) for text in y_tick_text))
+ y_tick_width = max(space_required(ctx, text) for text in y_tick_text)
top = font_extents[2] / 2
# bounds(left, top, right, bottom)
bounds = (y_tick_width + 4, top + 2, self.width, self.height - x_axis_space)
- self.draw_x_axis(bounds)
- self.draw_left_axis(bounds, y_ticks, y_tick_text)
+ self.draw_x_axis(ctx, bounds)
+ self.draw_left_axis(ctx, bounds, y_ticks, y_tick_text)
def intervalise(self, x, limit=None):
"""Given a value x create an array of tick points to got with the graph
@@ -228,7 +228,7 @@ class Graph(object):
]
return intervals
- def draw_left_axis(self, bounds, y_ticks, y_tick_text):
+ def draw_left_axis(self, ctx, bounds, y_ticks, y_tick_text):
(left, top, right, bottom) = bounds
stats = {}
for stat in self.stat_info:
@@ -245,29 +245,36 @@ class Graph(object):
for i, y_val in enumerate(y_ticks):
y = int(bottom - y_val * ratio) - 0.5
if i != 0:
- self.draw_dotted_line(gray, left, y, right, y)
- self.draw_y_text(y_tick_text[i], left, y)
- self.draw_line(gray, left, top, left, bottom)
+ self.draw_dotted_line(ctx, gray, left, y, right, y)
+ self.draw_y_text(ctx, y_tick_text[i], left, y)
+ self.draw_line(ctx, gray, left, top, left, bottom)
for stat, info in stats.items():
if len(info['values']) > 0:
- self.draw_value_poly(info['values'], info['color'], max_value, bounds)
self.draw_value_poly(
- info['values'], info['fill_color'], max_value, bounds, info['fill']
+ ctx, info['values'], info['color'], max_value, bounds
+ )
+ self.draw_value_poly(
+ ctx,
+ info['values'],
+ info['fill_color'],
+ max_value,
+ bounds,
+ info['fill'],
)
def draw_legend(self):
pass
- def trace_path(self, values, max_value, bounds):
+ def trace_path(self, ctx, values, max_value, bounds):
(left, top, right, bottom) = bounds
ratio = (bottom - top) / max_value
line_width = self.line_size
- self.ctx.set_line_width(line_width)
- self.ctx.move_to(right, bottom)
+ ctx.set_line_width(line_width)
+ ctx.move_to(right, bottom)
- self.ctx.line_to(right, int(bottom - values[0] * ratio))
+ ctx.line_to(right, int(bottom - values[0] * ratio))
x = right
step = (right - left) / (self.length - 1)
@@ -275,64 +282,62 @@ class Graph(object):
if i == self.length - 1:
x = left
- self.ctx.line_to(x, int(bottom - value * ratio))
+ ctx.line_to(x, int(bottom - value * ratio))
x -= step
- self.ctx.line_to(int(right - (len(values) - 1) * step), bottom)
- self.ctx.close_path()
+ ctx.line_to(int(right - (len(values) - 1) * step), bottom)
+ ctx.close_path()
- def draw_value_poly(self, values, color, max_value, bounds, fill=False):
- self.trace_path(values, max_value, bounds)
- self.ctx.set_source_rgba(*color)
+ def draw_value_poly(self, ctx, values, color, max_value, bounds, fill=False):
+ self.trace_path(ctx, values, max_value, bounds)
+ ctx.set_source_rgba(*color)
if fill:
- self.ctx.fill()
+ ctx.fill()
else:
- self.ctx.stroke()
+ ctx.stroke()
- def draw_x_text(self, text, x, y):
+ def draw_x_text(self, ctx, text, x, y):
"""Draws text below and horizontally centered about x,y"""
- fe = self.ctx.font_extents()
- te = self.ctx.text_extents(text)
+ fe = ctx.font_extents()
+ te = ctx.text_extents(text)
height = fe[2]
x_bearing = te[0]
width = te[2]
- self.ctx.move_to(int(x - width / 2 + x_bearing), int(y + height))
- self.ctx.set_source_rgba(*self.black)
- self.ctx.show_text(text)
+ ctx.move_to(int(x - width / 2 + x_bearing), int(y + height))
+ ctx.set_source_rgba(*self.black)
+ ctx.show_text(text)
- def draw_y_text(self, text, x, y):
+ def draw_y_text(self, ctx, text, x, y):
"""Draws text left of and vertically centered about x,y"""
- fe = self.ctx.font_extents()
- te = self.ctx.text_extents(text)
+ fe = ctx.font_extents()
+ te = ctx.text_extents(text)
descent = fe[1]
ascent = fe[0]
x_bearing = te[0]
width = te[4]
- self.ctx.move_to(
- int(x - width - x_bearing - 2), int(y + (ascent - descent) / 2)
- )
- self.ctx.set_source_rgba(*self.black)
- self.ctx.show_text(text)
-
- def draw_rect(self, color, x, y, height, width):
- self.ctx.set_source_rgba(*color)
- self.ctx.rectangle(x, y, height, width)
- self.ctx.fill()
-
- def draw_line(self, color, x1, y1, x2, y2):
- self.ctx.set_source_rgba(*color)
- self.ctx.set_line_width(1)
- self.ctx.move_to(x1, y1)
- self.ctx.line_to(x2, y2)
- self.ctx.stroke()
-
- def draw_dotted_line(self, color, x1, y1, x2, y2):
- self.ctx.set_source_rgba(*color)
- self.ctx.set_line_width(1)
- dash, offset = self.ctx.get_dash()
- self.ctx.set_dash(self.dash_length, 0)
- self.ctx.move_to(x1, y1)
- self.ctx.line_to(x2, y2)
- self.ctx.stroke()
- self.ctx.set_dash(dash, offset)
+ ctx.move_to(int(x - width - x_bearing - 2), int(y + (ascent - descent) / 2))
+ ctx.set_source_rgba(*self.black)
+ ctx.show_text(text)
+
+ def draw_rect(self, ctx, color, x, y, height, width):
+ ctx.set_source_rgba(*color)
+ ctx.rectangle(x, y, height, width)
+ ctx.fill()
+
+ def draw_line(self, ctx, color, x1, y1, x2, y2):
+ ctx.set_source_rgba(*color)
+ ctx.set_line_width(1)
+ ctx.move_to(x1, y1)
+ ctx.line_to(x2, y2)
+ ctx.stroke()
+
+ def draw_dotted_line(self, ctx, color, x1, y1, x2, y2):
+ ctx.set_source_rgba(*color)
+ ctx.set_line_width(1)
+ dash, offset = ctx.get_dash()
+ ctx.set_dash(self.dash_length, 0)
+ ctx.move_to(x1, y1)
+ ctx.line_to(x2, y2)
+ ctx.stroke()
+ ctx.set_dash(dash, offset)
diff --git a/deluge/plugins/Stats/deluge_stats/gtkui.py b/deluge/plugins/Stats/deluge_stats/gtkui.py
index 75e30150e..39c1d4c37 100644
--- a/deluge/plugins/Stats/deluge_stats/gtkui.py
+++ b/deluge/plugins/Stats/deluge_stats/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -10,12 +9,13 @@
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
-#
-
-from __future__ import division, unicode_literals
import logging
+import gi # isort:skip (Required before Gtk import).
+
+gi.require_version('Gtk', '3.0')
+
from gi.repository import Gtk
from gi.repository.Gdk import RGBA
@@ -85,7 +85,7 @@ def text_to_rgba(color):
class GraphsTab(Tab):
def __init__(self, colors):
- super(GraphsTab, self).__init__()
+ super().__init__()
builder = Gtk.Builder()
builder.add_from_file(get_resource('tabs.ui'))
@@ -270,7 +270,7 @@ class GtkUI(Gtk3PluginBase):
for graph, colors in self.config['colors'].items():
gtkconf[graph] = {}
for value, color in colors.items():
- color_btn = self.builder.get_object('%s_%s_color' % (graph, value))
+ color_btn = self.builder.get_object(f'{graph}_{value}_color')
try:
gtkconf[graph][value] = color_btn.get_color().to_string()
except Exception:
@@ -285,7 +285,7 @@ class GtkUI(Gtk3PluginBase):
for graph, colors in self.config['colors'].items():
for value, color in colors.items():
try:
- color_btn = self.builder.get_object('%s_%s_color' % (graph, value))
+ color_btn = self.builder.get_object(f'{graph}_{value}_color')
color_btn.set_rgba(text_to_rgba(color))
except Exception as ex:
log.debug('Unable to set %s %s %s: %s', graph, value, color, ex)
diff --git a/deluge/plugins/Stats/deluge_stats/tests/test_stats.py b/deluge/plugins/Stats/deluge_stats/tests/test_stats.py
index 07741cbf3..d61cd4666 100644
--- a/deluge/plugins/Stats/deluge_stats/tests/test_stats.py
+++ b/deluge/plugins/Stats/deluge_stats/tests/test_stats.py
@@ -1,19 +1,13 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# 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 pytest
+import pytest_twisted
from twisted.internet import defer
-from twisted.trial import unittest
-import deluge.component as component
from deluge.common import fsize, fspeed
-from deluge.tests import common as tests_common
-from deluge.tests.basetest import BaseTestCase
from deluge.ui.client import client
@@ -26,47 +20,45 @@ def print_totals(totals):
print('down:', fsize(totals['total_download'] - totals['total_payload_download']))
-class StatsTestCase(BaseTestCase):
- def set_up(self):
+class TestStatsPlugin:
+ @pytest_twisted.async_yield_fixture(autouse=True)
+ async def set_up(self, component):
defer.setDebugging(True)
- tests_common.set_tmp_config_dir()
client.start_standalone()
client.core.enable_plugin('Stats')
- return component.start()
-
- def tear_down(self):
+ await component.start()
+ yield
client.stop_standalone()
- return component.shutdown()
@defer.inlineCallbacks
def test_client_totals(self):
plugins = yield client.core.get_available_plugins()
if 'Stats' not in plugins:
- raise unittest.SkipTest('WebUi plugin not available for testing')
+ pytest.skip('Stats plugin not available for testing')
totals = yield client.stats.get_totals()
- self.assertEqual(totals['total_upload'], 0)
- self.assertEqual(totals['total_payload_upload'], 0)
- self.assertEqual(totals['total_payload_download'], 0)
- self.assertEqual(totals['total_download'], 0)
+ assert totals['total_upload'] == 0
+ assert totals['total_payload_upload'] == 0
+ assert totals['total_payload_download'] == 0
+ assert totals['total_download'] == 0
# print_totals(totals)
@defer.inlineCallbacks
def test_session_totals(self):
plugins = yield client.core.get_available_plugins()
if 'Stats' not in plugins:
- raise unittest.SkipTest('WebUi plugin not available for testing')
+ pytest.skip('Stats plugin not available for testing')
totals = yield client.stats.get_session_totals()
- self.assertEqual(totals['total_upload'], 0)
- self.assertEqual(totals['total_payload_upload'], 0)
- self.assertEqual(totals['total_payload_download'], 0)
- self.assertEqual(totals['total_download'], 0)
+ assert totals['total_upload'] == 0
+ assert totals['total_payload_upload'] == 0
+ assert totals['total_payload_download'] == 0
+ assert totals['total_download'] == 0
# print_totals(totals)
@pytest.mark.gtkui
@defer.inlineCallbacks
- def test_write(self):
+ def test_write(self, tmp_path):
"""
writing to a file-like object; need this for webui.
@@ -75,14 +67,14 @@ class StatsTestCase(BaseTestCase):
from deluge_stats import graph, gtkui
from deluge.configmanager import ConfigManager
- from deluge.ui.gtkui.gtkui import DEFAULT_PREFS
- from deluge.ui.gtkui.mainwindow import MainWindow
- from deluge.ui.gtkui.pluginmanager import PluginManager
- from deluge.ui.gtkui.preferences import Preferences
- from deluge.ui.gtkui.torrentdetails import TorrentDetails
- from deluge.ui.gtkui.torrentview import TorrentView
+ from deluge.ui.gtk3.gtkui import DEFAULT_PREFS
+ from deluge.ui.gtk3.mainwindow import MainWindow
+ from deluge.ui.gtk3.pluginmanager import PluginManager
+ from deluge.ui.gtk3.preferences import Preferences
+ from deluge.ui.gtk3.torrentdetails import TorrentDetails
+ from deluge.ui.gtk3.torrentview import TorrentView
- ConfigManager('gtkui.conf', defaults=DEFAULT_PREFS)
+ ConfigManager('gtk3ui.conf', defaults=DEFAULT_PREFS)
self.plugins = PluginManager()
MainWindow()
@@ -90,7 +82,7 @@ class StatsTestCase(BaseTestCase):
TorrentDetails()
Preferences()
- class FakeFile(object):
+ class FakeFile:
def __init__(self):
self.data = []
@@ -110,5 +102,5 @@ class StatsTestCase(BaseTestCase):
file_like = FakeFile()
surface.write_to_png(file_like)
data = b''.join(file_like.data)
- with open('file_like.png', 'wb') as _file:
+ with open(tmp_path / 'file_like.png', 'wb') as _file:
_file.write(data)
diff --git a/deluge/plugins/Stats/deluge_stats/webui.py b/deluge/plugins/Stats/deluge_stats/webui.py
index 4c11260e5..2c2ed469c 100644
--- a/deluge/plugins/Stats/deluge_stats/webui.py
+++ b/deluge/plugins/Stats/deluge_stats/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -23,7 +20,6 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
-
scripts = [get_resource('stats.js')]
# The enable and disable methods are not scrictly required on the WebUI
diff --git a/deluge/plugins/Stats/setup.py b/deluge/plugins/Stats/setup.py
index 174c652a9..0f3e0695b 100644
--- a/deluge/plugins/Stats/setup.py
+++ b/deluge/plugins/Stats/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
diff --git a/deluge/plugins/Toggle/deluge_toggle/__init__.py b/deluge/plugins/Toggle/deluge_toggle/__init__.py
index e63e4aa4c..b0332ee9c 100644
--- a/deluge/plugins/Toggle/deluge_toggle/__init__.py
+++ b/deluge/plugins/Toggle/deluge_toggle/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -22,7 +19,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -30,7 +27,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -38,4 +35,4 @@ class WebUIPlugin(PluginInitBase):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/Toggle/deluge_toggle/common.py b/deluge/plugins/Toggle/deluge_toggle/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/Toggle/deluge_toggle/common.py
+++ b/deluge/plugins/Toggle/deluge_toggle/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/Toggle/deluge_toggle/core.py b/deluge/plugins/Toggle/deluge_toggle/core.py
index dad52ce61..ab4581b47 100644
--- a/deluge/plugins/Toggle/deluge_toggle/core.py
+++ b/deluge/plugins/Toggle/deluge_toggle/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
diff --git a/deluge/plugins/Toggle/deluge_toggle/gtkui.py b/deluge/plugins/Toggle/deluge_toggle/gtkui.py
index c54bca46f..bfb90de1b 100644
--- a/deluge/plugins/Toggle/deluge_toggle/gtkui.py
+++ b/deluge/plugins/Toggle/deluge_toggle/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
diff --git a/deluge/plugins/Toggle/deluge_toggle/webui.py b/deluge/plugins/Toggle/deluge_toggle/webui.py
index 8f0fc8c99..637365ca7 100644
--- a/deluge/plugins/Toggle/deluge_toggle/webui.py
+++ b/deluge/plugins/Toggle/deluge_toggle/webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.plugins.pluginbase import WebPluginBase
@@ -24,7 +21,6 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
-
scripts = [get_resource('toggle.js')]
def enable(self):
diff --git a/deluge/plugins/Toggle/setup.py b/deluge/plugins/Toggle/setup.py
index acc6e6c7d..dadd32ebc 100644
--- a/deluge/plugins/Toggle/setup.py
+++ b/deluge/plugins/Toggle/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
diff --git a/deluge/plugins/WebUi/deluge_webui/__init__.py b/deluge/plugins/WebUi/deluge_webui/__init__.py
index a3d29805a..ba978b224 100644
--- a/deluge/plugins/WebUi/deluge_webui/__init__.py
+++ b/deluge/plugins/WebUi/deluge_webui/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.plugins.init import PluginInitBase
@@ -21,7 +18,7 @@ class CorePlugin(PluginInitBase):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
- super(CorePlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
@@ -29,7 +26,7 @@ class GtkUIPlugin(PluginInitBase):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
- super(GtkUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
@@ -37,4 +34,4 @@ class WebUIPlugin(PluginInitBase):
from webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
- super(WebUIPlugin, self).__init__(plugin_name)
+ super().__init__(plugin_name)
diff --git a/deluge/plugins/WebUi/deluge_webui/common.py b/deluge/plugins/WebUi/deluge_webui/common.py
index 4c9db09d5..eb47f1398 100644
--- a/deluge/plugins/WebUi/deluge_webui/common.py
+++ b/deluge/plugins/WebUi/deluge_webui/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -12,8 +11,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os.path
from pkg_resources import resource_filename
diff --git a/deluge/plugins/WebUi/deluge_webui/core.py b/deluge/plugins/WebUi/deluge_webui/core.py
index cc3330fc0..f18203e90 100644
--- a/deluge/plugins/WebUi/deluge_webui/core.py
+++ b/deluge/plugins/WebUi/deluge_webui/core.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from twisted.internet import defer
diff --git a/deluge/plugins/WebUi/deluge_webui/data/config.ui b/deluge/plugins/WebUi/deluge_webui/data/config.ui
index 18647a415..c58edd0cd 100644
--- a/deluge/plugins/WebUi/deluge_webui/data/config.ui
+++ b/deluge/plugins/WebUi/deluge_webui/data/config.ui
@@ -86,8 +86,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
<property name="numeric">True</property>
</object>
diff --git a/deluge/plugins/WebUi/deluge_webui/gtkui.py b/deluge/plugins/WebUi/deluge_webui/gtkui.py
index ca3a16ea4..3d19417dc 100644
--- a/deluge/plugins/WebUi/deluge_webui/gtkui.py
+++ b/deluge/plugins/WebUi/deluge_webui/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -11,8 +10,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from gi.repository import Gtk
diff --git a/deluge/plugins/WebUi/deluge_webui/tests/test_plugin_webui.py b/deluge/plugins/WebUi/deluge_webui/tests/test_plugin_webui.py
index 56e1cc023..413d25925 100644
--- a/deluge/plugins/WebUi/deluge_webui/tests/test_plugin_webui.py
+++ b/deluge/plugins/WebUi/deluge_webui/tests/test_plugin_webui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -6,44 +5,40 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
+import pytest
+import pytest_twisted
-from __future__ import unicode_literals
-
-from twisted.trial import unittest
-
-import deluge.component as component
from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer
from deluge.tests import common
-from deluge.tests.basetest import BaseTestCase
common.disable_new_release_check()
-class WebUIPluginTestCase(BaseTestCase):
- def set_up(self):
- common.set_tmp_config_dir()
+class TestWebUIPlugin:
+ @pytest_twisted.async_yield_fixture(autouse=True)
+ async def set_up(self, request, component):
+ self = request.instance
self.rpcserver = RPCServer(listen=False)
self.core = Core()
- return component.start()
+ await component.start()
- def tear_down(self):
- def on_shutdown(result):
- del self.rpcserver
- del self.core
+ yield
- return component.shutdown().addCallback(on_shutdown)
+ await component.shutdown()
+ del self.rpcserver
+ del self.core
def test_enable_webui(self):
if 'WebUi' not in self.core.get_available_plugins():
- raise unittest.SkipTest('WebUi plugin not available for testing')
+ pytest.skip('WebUi plugin not available for testing')
d = self.core.enable_plugin('WebUi')
def result_cb(result):
if 'WebUi' not in self.core.get_enabled_plugins():
self.fail('Failed to enable WebUi plugin')
- self.assertTrue(result)
+ assert result
d.addBoth(result_cb)
return d
diff --git a/deluge/plugins/WebUi/setup.py b/deluge/plugins/WebUi/setup.py
index 861a05a50..5f2184cc9 100644
--- a/deluge/plugins/WebUi/setup.py
+++ b/deluge/plugins/WebUi/setup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
diff --git a/deluge/plugins/init.py b/deluge/plugins/init.py
index addeae9f3..56b31977d 100644
--- a/deluge/plugins/init.py
+++ b/deluge/plugins/init.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -10,14 +9,12 @@
"""
This base class is used in plugin's __init__ for the plugin entry points.
"""
-from __future__ import unicode_literals
-
import logging
log = logging.getLogger(__name__)
-class PluginInitBase(object):
+class PluginInitBase:
_plugin_cls = None
def __init__(self, plugin_name):
diff --git a/deluge/plugins/pluginbase.py b/deluge/plugins/pluginbase.py
index e80199df1..8d5515690 100644
--- a/deluge/plugins/pluginbase.py
+++ b/deluge/plugins/pluginbase.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2010 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -17,11 +14,10 @@ log = logging.getLogger(__name__)
class PluginBase(component.Component):
-
update_interval = 1
def __init__(self, name):
- super(PluginBase, self).__init__(name, self.update_interval)
+ super().__init__(name, self.update_interval)
def enable(self):
raise NotImplementedError('Need to define an enable method!')
@@ -32,35 +28,37 @@ class PluginBase(component.Component):
class CorePluginBase(PluginBase):
def __init__(self, plugin_name):
- super(CorePluginBase, self).__init__('CorePlugin.' + plugin_name)
+ super().__init__('CorePlugin.' + plugin_name)
# Register RPC methods
component.get('RPCServer').register_object(self, plugin_name.lower())
log.debug('CorePlugin initialized..')
def __del__(self):
- component.get('RPCServer').deregister_object(self)
+ try:
+ component.get('RPCServer').deregister_object(self)
+ except KeyError:
+ log.debug('RPCServer already deregistered')
def enable(self):
- super(CorePluginBase, self).enable()
+ super().enable()
def disable(self):
- super(CorePluginBase, self).disable()
+ super().disable()
class Gtk3PluginBase(PluginBase):
def __init__(self, plugin_name):
- super(Gtk3PluginBase, self).__init__('Gtk3Plugin.' + plugin_name)
+ super().__init__('Gtk3Plugin.' + plugin_name)
log.debug('Gtk3Plugin initialized..')
def enable(self):
- super(Gtk3PluginBase, self).enable()
+ super().enable()
def disable(self):
- super(Gtk3PluginBase, self).disable()
+ super().disable()
class WebPluginBase(PluginBase):
-
scripts = []
debug_scripts = []
@@ -68,7 +66,7 @@ class WebPluginBase(PluginBase):
debug_stylesheets = []
def __init__(self, plugin_name):
- super(WebPluginBase, self).__init__('WebPlugin.' + plugin_name)
+ super().__init__('WebPlugin.' + plugin_name)
# Register JSON rpc methods
component.get('JSON').register_object(self, plugin_name.lower())
diff --git a/deluge/scripts/create_deluge_pngs b/deluge/scripts/create_deluge_pngs
deleted file mode 100755
index 6ed636db7..000000000
--- a/deluge/scripts/create_deluge_pngs
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/bin/bash
-
-# A script to convert the Deluge svg icons to png.
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-data_dir="$DIR/../ui/data"
-zopfli_iter="--iterations=100"
-zopflipng_bin="zopflipng --filters=01234mepb --lossy_8bit --lossy_transparent -y"
-
-# Create deluge png icon pack for all sizes.
-for size in 16 22 24 32 36 48 64 72 96 128 192 256 512; do
- mkdir -p $data_dir/icons/hicolor/${size}x${size}/apps
- in_svg=$data_dir/pixmaps/deluge.svg
- out_png=$data_dir/icons/hicolor/${size}x${size}/apps/deluge.png
- rsvg-convert -w ${size} -h ${size} -o $out_png $in_svg
- if [ "$size" -gt 128 ]; then
- zopfli_iter=""
- fi
- echo $zopflipng_bin $zopfli_iter
- eval $zopflipng_bin $zopfli_iter $out_png $out_png
-done
-
-# Create deluge-panel png for systray.
-for size in 16 22 24; do
- in_png=$data_dir/icons/hicolor/${size}x${size}/apps/deluge.png
- out_png=$data_dir/icons/hicolor/${size}x${size}/apps/deluge-panel.png
- cp $in_png $out_png
-done
-
-# Create deluge.ico icon from pngs.
-for size in 16 32 48 64 128 256; do
- ico_infiles+="$data_dir/icons/hicolor/${size}x${size}/apps/deluge.png "
-done
-convert $ico_infiles $data_dir/pixmaps/deluge.ico
-
-# Copy of deluge.svg to icon theme pack.
-mkdir -p $data_dir/icons/hicolor/scalable/apps/
-cp $data_dir/pixmaps/deluge.svg $data_dir/icons/hicolor/scalable/apps/deluge.svg
-
-# Create 48px deluge.png.
-cp $data_dir/icons/hicolor/48x48/apps/deluge.png $data_dir/pixmaps/deluge.png
-
-# Create 16px png from deluge and status svgs.
-for file in $data_dir/pixmaps/*.svg; do
- out_png=${file%.*}16.png
- rsvg-convert -w 16 -h 16 -o $out_png $file
- eval $zopflipng_bin $out_png $out_png
-done
-
-# Copy 16px deluge and status pngs to webui icons folder.
-for icon in $data_dir/pixmaps/*16.png; do
- iconname=$(basename $icon)
- cp $icon $data_dir/../web/icons/${iconname::-6}.png
-done
-rm $data_dir/../web/icons/tracker*.png
-
-for size in 32 192 512; do
- in_png=$data_dir/icons/hicolor/${size}x${size}/apps/deluge.png
- out_png=$data_dir/../web/icons/deluge-${size}.png
- cp $in_png $out_png
-
-# Create apple and android touch icons with background colour.
-apple_icon=$data_dir/../web/icons/deluge-apple-180.png
-rsvg-convert -w 180 -h 180 -b '#599EEE' -o $apple_icon $data_dir/pixmaps/deluge.svg
-eval $zopflipng_bin $apple_icon $apple_icon
-
-# Create favicon.ico icon from pngs.
-for size in 16 32 48; do
- web_ico_infiles+="$data_dir/icons/hicolor/${size}x${size}/apps/deluge.png "
-done
-convert $web_ico_infiles $data_dir/../web/icons/favicon.ico
diff --git a/deluge/scripts/create_icons.py b/deluge/scripts/create_icons.py
new file mode 100755
index 000000000..479505cf5
--- /dev/null
+++ b/deluge/scripts/create_icons.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python3
+#
+# Create Deluge PNG icons from SVG
+#
+# Required image tools:
+# * rsvg-convert
+# * convert (ImageMagik)
+# * oxipng
+# * pngquant
+#
+import shutil
+import subprocess
+from dataclasses import dataclass, field
+from pathlib import Path
+
+
+@dataclass
+class IconPack:
+ name: str
+ dir: Path
+ icon_sizes: list[int]
+ panel_sizes: list[int]
+ ico_sizes: list[int]
+ pixmaps_dir: Path = field(init=False)
+ theme_dir: Path = field(init=False)
+ theme_svg: Path = field(init=False)
+ theme_pngs: dict[int, Path] = field(init=False)
+ logo_svg: Path = field(init=False)
+ logo_ico: Path = field(init=False)
+ logo_png: Path = field(init=False)
+
+ def __post_init__(self):
+ self.pixmaps_dir = self.dir / 'pixmaps'
+ self.logo_svg = self.pixmaps_dir / f'{self.name}.svg'
+ self.logo_ico = self.pixmaps_dir / f'{self.name}.ico'
+ self.logo_png = self.pixmaps_dir / f'{self.name}.png'
+
+ self.theme_dir = self.dir / 'icons' / 'hicolor'
+ self.theme_svg = self.theme_dir / 'scalable' / 'apps' / f'{self.name}.svg'
+ self.theme_pngs = self.create_theme_pngs_paths(
+ self.name, self.icon_sizes, self.theme_dir
+ )
+
+ @staticmethod
+ def create_theme_pngs_paths(name, icon_sizes, out_dir):
+ return {
+ size: out_dir / f'{size}x{size}' / 'apps' / f'{name}.png'
+ for size in icon_sizes
+ }
+
+
+@dataclass
+class WebIconPack:
+ name: str
+ dir: Path
+ icon_sizes: list[int]
+ favicon_sizes: list[int]
+ icons_dir: Path = field(init=False)
+ touch: Path = field(init=False)
+ favicon: Path = field(init=False)
+
+ def __post_init__(self):
+ self.icons_dir = self.dir / 'icons'
+ self.touch = self.icons_dir / f'{self.name}-apple-180.png'
+ self.favicon = self.icons_dir / 'favicon.ico'
+
+
+def convert_svg_to_png(svg_file, png_file, size, background_color=None):
+ rsvg_options = [
+ '-w',
+ str(size),
+ '-h',
+ str(size),
+ '-o',
+ png_file,
+ ]
+ rsvg_options + ['-b', {background_color}] if background_color else []
+
+ subprocess.run(['rsvg-convert'] + rsvg_options + [svg_file], check=True)
+
+
+def compress_png(png_file):
+ subprocess.run(
+ ['pngquant', '--quality=70-95', '--ext', '.png', '--force', png_file],
+ check=True,
+ )
+ subprocess.run(['oxipng', png_file], check=True)
+
+
+def create_panel_icons(icon_pack, sizes):
+ for size in sizes:
+ app_png = icon_pack[size]
+ panel_png = app_png.with_name(f'{app_png.stem}-panel.png')
+ shutil.copyfile(app_png, panel_png)
+
+
+def create_hicolor_icons(svg_icon, icon_pack):
+ """Convert SVG icon to hicolor PNG icons."""
+ for size, png_file in icon_pack.items():
+ png_file.parent.mkdir(parents=True, exist_ok=True)
+ convert_svg_to_png(svg_icon, png_file, size)
+ compress_png(png_file)
+
+
+def create_ico_icon(icon_pack, sizes, ico_file):
+ infiles = [icon_pack[size] for size in sizes]
+ ico_file.parent.mkdir(parents=True, exist_ok=True)
+
+ subprocess.run(['convert', *infiles, ico_file], check=True)
+
+
+def create_hicolor_svg(src_svg, dest_svg):
+ dest_svg.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copyfile(src_svg, dest_svg)
+
+
+def create_mini_icons(pixmaps_dir):
+ pixmap_svgs = pixmaps_dir.glob('*.svg')
+
+ for svg_file in pixmap_svgs:
+ png_file = pixmaps_dir / f'{svg_file.stem}16.png'
+ convert_svg_to_png(svg_file, png_file, 16)
+ compress_png(png_file)
+
+
+def create_logo(deluge_png, pixmap_png):
+ pixmap_png.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copyfile(deluge_png, pixmap_png)
+
+
+def create_web_status_icons(src_dir: Path, dest_dir: Path):
+ """Web UI status icons from 16px icons."""
+ pngs_16px = src_dir.glob('*16.png')
+ dest_dir.mkdir(parents=True, exist_ok=True)
+ for path in pngs_16px:
+ if path.stem.startswith('tracker'):
+ continue
+ new_name = path.stem.replace('16', '') + '.png'
+ shutil.copyfile(path, dest_dir / new_name)
+
+
+def create_touch_icon(svg_file, png_file, size):
+ """Web icons with background color for Apple or Android"""
+ png_file.parent.mkdir(parents=True, exist_ok=True)
+ convert_svg_to_png(svg_file, png_file, size, background_color='#599EEE')
+ compress_png(png_file)
+
+
+def create_web_icons(app_pngs, sizes, dest_dir):
+ dest_dir.mkdir(parents=True, exist_ok=True)
+ for size in sizes:
+ app_png = app_pngs[size]
+ web_png = dest_dir / f'{app_png.stem}-{size}.png'
+ shutil.copyfile(app_png, web_png)
+
+
+def main():
+ data_dir = Path.cwd() / 'deluge' / 'ui' / 'data'
+ if not data_dir.is_dir():
+ exit(f'No path to UI data dir: {data_dir}')
+
+ # Create Deluge UI icons
+ icon_pack_sizes = [16, 22, 24, 32, 36, 48, 64, 72, 96, 128, 192, 256, 512]
+ panel_icon_sizes = [16, 22, 24]
+ ico_icon_sizes = [16, 32, 48, 64, 128, 256]
+ ui_icons = IconPack(
+ name='deluge',
+ dir=data_dir,
+ icon_sizes=icon_pack_sizes,
+ panel_sizes=panel_icon_sizes,
+ ico_sizes=ico_icon_sizes,
+ )
+
+ # Theme icons for GTK
+ create_hicolor_icons(ui_icons.logo_svg, ui_icons.theme_pngs)
+ create_hicolor_svg(ui_icons.logo_svg, ui_icons.theme_svg)
+ create_mini_icons(ui_icons.pixmaps_dir)
+ # Panel icon for systray
+ create_panel_icons(ui_icons.theme_pngs, ui_icons.panel_sizes)
+
+ # Deluge logos
+ create_ico_icon(ui_icons.theme_pngs, ui_icons.ico_sizes, ui_icons.logo_ico)
+ create_logo(ui_icons.theme_pngs[48], ui_icons.logo_png)
+
+ # Web UI Icons
+ web_icon_sizes = [32, 192, 512]
+ favicon_sizes = [16, 32, 48]
+ web_icons = WebIconPack(
+ name='deluge',
+ dir=data_dir / '..' / 'web',
+ icon_sizes=web_icon_sizes,
+ favicon_sizes=favicon_sizes,
+ )
+ create_web_icons(ui_icons.theme_pngs, web_icons.icon_sizes, web_icons.icons_dir)
+ create_web_status_icons(ui_icons.pixmaps_dir, web_icons.icons_dir)
+ create_touch_icon(ui_icons.logo_svg, web_icons.touch, 180)
+ create_ico_icon(ui_icons.theme_pngs, web_icons.favicon_sizes, web_icons.favicon)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/deluge/scripts/create_plugin.py b/deluge/scripts/create_plugin.py
index 44513ed45..7383661ea 100644
--- a/deluge/scripts/create_plugin.py
+++ b/deluge/scripts/create_plugin.py
@@ -7,8 +7,6 @@ python create_plugin.py --name MyPlugin2 --basepath . --author-name "Your Name"
"""
-from __future__ import print_function, unicode_literals
-
import os
import sys
from argparse import ArgumentParser
@@ -51,7 +49,6 @@ options = parser.parse_args()
def create_plugin():
-
if not options.url:
options.url = ''
@@ -115,9 +112,13 @@ def create_plugin():
# add an input parameter for this?
print('building dev-link..')
- write_file(plugin_base, 'create_dev_link.sh', CREATE_DEV_LINK)
- dev_link_path = os.path.join(plugin_base, 'create_dev_link.sh')
- os.system('chmod +x %s' % dev_link_path) # lazy..
+ if deluge.common.windows_check():
+ write_file(plugin_base, 'create_dev_link.bat', CREATE_DEV_LINK_WIN)
+ dev_link_path = os.path.join(plugin_base, 'create_dev_link.bat')
+ else:
+ write_file(plugin_base, 'create_dev_link.sh', CREATE_DEV_LINK_NIX)
+ dev_link_path = os.path.join(plugin_base, 'create_dev_link.sh')
+ os.system('chmod +x %s' % dev_link_path) # lazy..
os.system(dev_link_path)
@@ -374,7 +375,7 @@ GPL = """# -*- coding: utf-8 -*-
# the OpenSSL library. See LICENSE for more details.
"""
-CREATE_DEV_LINK = """#!/bin/bash
+CREATE_DEV_LINK_NIX = """#!/bin/bash
BASEDIR=$(cd `dirname $0` && pwd)
CONFIG_DIR=$( test -z $1 && echo "%(configdir)s" || echo "$1")
[ -d "$CONFIG_DIR/plugins" ] || echo "Config dir \"$CONFIG_DIR\" is either not a directory \
@@ -388,4 +389,27 @@ cp $BASEDIR/temp/*.egg-link $CONFIG_DIR/plugins
rm -fr $BASEDIR/temp
"""
+CREATE_DEV_LINK_WIN = """@echo off
+set BASEDIR=%%~dp0
+set BASEDIR=%%BASEDIR:~0,-1%%
+if [%%1]==[] (
+ set CONFIG_DIR=%(configdir)s
+) else (
+ set CONFIG_DIR=%%1
+)
+if not exist %%CONFIG_DIR%%\\plugins (
+ echo Config dir %%CONFIG_DIR%% is either not a directory \
+or is not a proper deluge config directory. Exiting
+ exit /b 1
+)
+cd %%BASEDIR%%
+if not exist %%BASEDIR%%\\temp (
+ md %%BASEDIR%%\\temp
+)
+set PYTHONPATH=%%BASEDIR%%/temp
+%(python_path)s setup.py build develop --install-dir %%BASEDIR%%\\temp
+copy "%%BASEDIR%%\\temp\\*.egg-link" "%%CONFIG_DIR%%\\plugins"
+rd /s /q %%BASEDIR%%\\temp
+"""
+
create_plugin()
diff --git a/deluge/scripts/deluge_remote.py b/deluge/scripts/deluge_remote.py
index bacc4f88d..d983e5398 100644
--- a/deluge/scripts/deluge_remote.py
+++ b/deluge/scripts/deluge_remote.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# This software is in the public domain, furnished "as is", without technical
# support, and with no warranty, express or implied, as to its usefulness for
@@ -12,8 +11,6 @@
#
# Authour: Garett Harnish
-from __future__ import unicode_literals
-
import logging
import sys
from optparse import OptionParser
diff --git a/deluge/tests/__init__.py b/deluge/tests/__init__.py
index d3bf10def..7b6afa194 100644
--- a/deluge/tests/__init__.py
+++ b/deluge/tests/__init__.py
@@ -1,7 +1,5 @@
# Increase open file descriptor limit to allow tests to run
# without getting error: what(): epoll: Too many open files
-from __future__ import print_function, unicode_literals
-
from deluge.i18n import setup_translation
try:
diff --git a/deluge/tests/basetest.py b/deluge/tests/basetest.py
deleted file mode 100644
index 11ca18e53..000000000
--- a/deluge/tests/basetest.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
-# the additional special exception to link portions of this program with the OpenSSL library.
-# See LICENSE for more details.
-#
-
-from __future__ import unicode_literals
-
-import warnings
-
-from twisted.internet.defer import maybeDeferred
-from twisted.trial import unittest
-
-import deluge.component as component
-
-
-class BaseTestCase(unittest.TestCase):
- """This is the base class that should be used for all test classes
- that create classes that inherit from deluge.component.Component. It
- ensures that the component registry has been cleaned up when tests
- have finished.
-
- """
-
- def setUp(self): # NOQA: N803
-
- if len(component._ComponentRegistry.components) != 0:
- warnings.warn(
- 'The component._ComponentRegistry.components is not empty on test setup.\n'
- 'This is probably caused by another test that did not clean up after finishing!: %s'
- % component._ComponentRegistry.components
- )
- d = maybeDeferred(self.set_up)
-
- def on_setup_error(error):
- warnings.warn('Error caught in test setup!\n%s' % error.getTraceback())
- self.fail()
-
- return d.addErrback(on_setup_error)
-
- def tearDown(self): # NOQA: N803
- d = maybeDeferred(self.tear_down)
-
- def on_teardown_failed(error):
- warnings.warn('Error caught in test teardown!\n%s' % error.getTraceback())
- self.fail()
-
- def on_teardown_complete(result):
- component._ComponentRegistry.components.clear()
- component._ComponentRegistry.dependents.clear()
-
- return d.addCallbacks(on_teardown_complete, on_teardown_failed)
-
- def set_up(self):
- pass
-
- def tear_down(self):
- pass
diff --git a/deluge/tests/common.py b/deluge/tests/common.py
index be33f8c58..b5941568d 100644
--- a/deluge/tests/common.py
+++ b/deluge/tests/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,22 +6,21 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import os
import sys
-import tempfile
import traceback
+import pytest
from twisted.internet import defer, protocol, reactor
from twisted.internet.defer import Deferred
from twisted.internet.error import CannotListenError
-from twisted.trial import unittest
import deluge.configmanager
import deluge.core.preferencesmanager
import deluge.log
+from deluge.common import get_localhost_auth
from deluge.error import DelugeError
+from deluge.ui.client import Client
# This sets log level to critical, so use log.critical() to debug while running unit tests
deluge.log.setup_logger('none')
@@ -32,12 +30,6 @@ def disable_new_release_check():
deluge.core.preferencesmanager.DEFAULT_PREFS['new_release_check'] = False
-def set_tmp_config_dir():
- config_directory = tempfile.mkdtemp()
- deluge.configmanager.set_config_dir(config_directory)
- return config_directory
-
-
def setup_test_logger(level='info', prefix='deluge'):
deluge.log.setup_logger(level, filename='%s.log' % prefix, twisted_observer=False)
@@ -55,7 +47,7 @@ def todo_test(caller):
filename = os.path.basename(traceback.extract_stack(None, 2)[0][0])
funcname = traceback.extract_stack(None, 2)[0][2]
- raise unittest.SkipTest('TODO: %s:%s' % (filename, funcname))
+ pytest.skip(f'TODO: {filename}:{funcname}')
def add_watchdog(deferred, timeout=0.05, message=None):
@@ -73,7 +65,7 @@ def add_watchdog(deferred, timeout=0.05, message=None):
return watchdog
-class ReactorOverride(object):
+class ReactorOverride:
"""Class used to patch reactor while running unit tests
to avoid starting and stopping the twisted reactor
"""
@@ -97,12 +89,19 @@ class ReactorOverride(object):
class ProcessOutputHandler(protocol.ProcessProtocol):
def __init__(
- self, script, callbacks, logfile=None, print_stdout=True, print_stderr=True
+ self,
+ script,
+ shutdown_func,
+ callbacks,
+ logfile=None,
+ print_stdout=True,
+ print_stderr=True,
):
"""Executes a script and handle the process' output to stdout and stderr.
Args:
script (str): The script to execute.
+ shutdown_func (func): A function which will gracefully stop the called script.
callbacks (list): Callbacks to trigger if the expected output if found.
logfile (str, optional): Filename to wrote the process' output.
print_stderr (bool): Print the process' stderr output to stdout.
@@ -111,6 +110,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
"""
self.callbacks = callbacks
self.script = script
+ self.shutdown_func = shutdown_func
self.log_output = ''
self.stderr_out = ''
self.logfile = logfile
@@ -130,6 +130,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
with open(self.logfile, 'w') as f:
f.write(self.log_output)
+ @defer.inlineCallbacks
def kill(self):
"""Kill the running process.
@@ -142,11 +143,17 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
self.killed = True
self._kill_watchdogs()
self.quit_d = Deferred()
- self.transport.signalProcess('INT')
- return self.quit_d
+ shutdown = self.shutdown_func()
+ shutdown.addTimeout(5, reactor)
+ try:
+ yield shutdown
+ except Exception:
+ self.transport.signalProcess('TERM')
+ result = yield self.quit_d
+ return result
def _kill_watchdogs(self):
- """"Cancel all watchdogs"""
+ """Cancel all watchdogs"""
for w in self.watchdogs:
if not w.called and not w.cancelled:
w.cancel()
@@ -205,7 +212,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
def start_core(
- listen_port=58846,
+ listen_port=58900,
logfile=None,
timeout=10,
timeout_msg=None,
@@ -213,13 +220,14 @@ def start_core(
print_stdout=True,
print_stderr=True,
extra_callbacks=None,
+ config_directory='',
):
"""Start the deluge core as a daemon.
Args:
listen_port (int, optional): The port the daemon listens for client connections.
logfile (str, optional): Logfile name to write the output from the process.
- timeout (int): If none of the callbacks have been triggered before the imeout, the process is killed.
+ timeout (int): If none of the callbacks have been triggered before the timeout, the process is killed.
timeout_msg (str): The message to print when the timeout expires.
custom_script (str): Extra python code to insert into the daemon process script.
print_stderr (bool): If the output from the process' stderr should be printed to stdout.
@@ -234,7 +242,6 @@ def start_core(
or upon timeout expiry. The ProcessOutputHandler is the handler for the deluged process.
"""
- config_directory = set_tmp_config_dir()
daemon_script = """
import sys
import deluge.core.daemon_entry
@@ -254,7 +261,7 @@ except Exception:
import traceback
sys.stderr.write('Exception raised:\\n %%s' %% traceback.format_exc())
""" % {
- 'dir': config_directory,
+ 'dir': config_directory.as_posix(),
'port': listen_port,
'script': custom_script,
}
@@ -289,20 +296,30 @@ except Exception:
if extra_callbacks:
callbacks.extend(extra_callbacks)
+ @defer.inlineCallbacks
+ def shutdown_daemon():
+ username, password = get_localhost_auth()
+ client = Client()
+ yield client.connect(
+ 'localhost', listen_port, username=username, password=password
+ )
+ yield client.daemon.shutdown()
+
process_protocol = start_process(
- daemon_script, callbacks, logfile, print_stdout, print_stderr
+ daemon_script, shutdown_daemon, callbacks, logfile, print_stdout, print_stderr
)
return default_core_cb['deferred'], process_protocol
def start_process(
- script, callbacks, logfile=None, print_stdout=True, print_stderr=True
+ script, shutdown_func, callbacks, logfile=None, print_stdout=True, print_stderr=True
):
"""
Starts an external python process which executes the given script.
Args:
script (str): The content of the script to execute.
+ shutdown_func (func): A function which will gracefully end the called script.
callbacks (list): list of dictionaries specifying callbacks.
logfile (str, optional): Logfile name to write the output from the process.
print_stderr (bool): If the output from the process' stderr should be printed to stdout.
@@ -324,7 +341,12 @@ def start_process(
"""
cwd = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
process_protocol = ProcessOutputHandler(
- script.encode('utf8'), callbacks, logfile, print_stdout, print_stderr
+ script.encode('utf8'),
+ shutdown_func,
+ callbacks,
+ logfile,
+ print_stdout,
+ print_stderr,
)
# Add timeouts to deferreds
diff --git a/deluge/tests/common_web.py b/deluge/tests/common_web.py
index 706eb8d72..f7da5771a 100644
--- a/deluge/tests/common_web.py
+++ b/deluge/tests/common_web.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,21 +6,20 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
+import pytest
import deluge.common
-import deluge.component as component
import deluge.ui.web.auth
import deluge.ui.web.server
from deluge import configmanager
+from deluge.conftest import BaseTestCase
from deluge.ui.web.server import DelugeWeb
-from .basetest import BaseTestCase
from .common import ReactorOverride
-from .daemon_base import DaemonBase
-class WebServerTestBase(BaseTestCase, DaemonBase):
+@pytest.mark.usefixtures('daemon', 'component')
+class WebServerTestBase(BaseTestCase):
"""
Base class for tests that need a running webapi
@@ -30,16 +28,11 @@ class WebServerTestBase(BaseTestCase, DaemonBase):
def set_up(self):
self.host_id = None
deluge.ui.web.server.reactor = ReactorOverride()
- d = self.common_set_up()
- d.addCallback(self.start_core)
- d.addCallback(self.start_webapi)
- return d
+ return self.start_webapi(None)
def start_webapi(self, arg):
- self.webserver_listen_port = 8999
-
config_defaults = deluge.ui.web.server.CONFIG_DEFAULTS.copy()
- config_defaults['port'] = self.webserver_listen_port
+ config_defaults['port'] = 8999
self.config = configmanager.ConfigManager('web.conf', config_defaults)
self.deluge_web = DelugeWeb(daemon=False)
@@ -50,13 +43,8 @@ class WebServerTestBase(BaseTestCase, DaemonBase):
self.host_id = host[0]
self.deluge_web.start()
- def tear_down(self):
- d = component.shutdown()
- d.addCallback(self.terminate_core)
- return d
-
-class WebServerMockBase(object):
+class WebServerMockBase:
"""
Class with utility functions for mocking with tests using the webserver
diff --git a/deluge/tests/daemon_base.py b/deluge/tests/daemon_base.py
index 7352e0d32..707570f17 100644
--- a/deluge/tests/daemon_base.py
+++ b/deluge/tests/daemon_base.py
@@ -1,37 +1,21 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# 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.path
-
import pytest
from twisted.internet import defer
from twisted.internet.error import CannotListenError
import deluge.component as component
-from deluge.common import windows_check
from . import common
-@pytest.mark.usefixtures('get_pytest_basetemp')
-class DaemonBase(object):
- basetemp = None
-
- if windows_check():
- skip = 'windows cant start_core not enough arguments for format string'
-
- @pytest.fixture
- def get_pytest_basetemp(self, request):
- self.basetemp = request.config.option.basetemp
-
+@pytest.mark.usefixtures('config_dir')
+class DaemonBase:
def common_set_up(self):
- common.set_tmp_config_dir()
self.listen_port = 58900
self.core = None
return component.start()
@@ -57,15 +41,7 @@ class DaemonBase(object):
port_range=10,
extra_callbacks=None,
):
- if logfile == '':
- logfile = 'daemon_%s.log' % self.id()
-
- # We are running py.test
- if hasattr(pytest, 'config'):
- if self.basetemp:
- if not os.path.exists(self.basetemp):
- os.makedirs(self.basetemp)
- logfile = os.path.join(self.basetemp, logfile)
+ logfile = f'daemon_{self.id()}.log' if logfile == '' else logfile
for dummy in range(port_range):
try:
@@ -78,6 +54,7 @@ class DaemonBase(object):
print_stdout=print_stdout,
print_stderr=print_stderr,
extra_callbacks=extra_callbacks,
+ config_directory=self.config_dir,
)
yield d
except CannotListenError as ex:
diff --git a/deluge/tests/data/dir_with_single_file.torrent b/deluge/tests/data/dir_with_single_file.torrent
new file mode 100644
index 000000000..33fec2c5f
--- /dev/null
+++ b/deluge/tests/data/dir_with_single_file.torrent
@@ -0,0 +1 @@
+d10:created by13:mktorrent 1.113:creation datei1684991433e4:infod5:filesld6:lengthi9e4:pathl15:single_file.txteee4:name20:dir_with_single_file12:piece lengthi262144e6:pieces20:Wi,=35Yhee \ No newline at end of file
diff --git a/deluge/tests/data/seo.ico b/deluge/tests/data/seo.ico
deleted file mode 100644
index 841e52871..000000000
--- a/deluge/tests/data/seo.ico
+++ /dev/null
Binary files differ
diff --git a/deluge/tests/data/seo.svg b/deluge/tests/data/seo.svg
new file mode 100644
index 000000000..fc96f74d4
--- /dev/null
+++ b/deluge/tests/data/seo.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 95.63 110.42" width="10cm" height="10cm"><defs><style>.cls-1{fill:#ec5728;}.cls-2{fill:#fff;}</style></defs><title>seocom-target</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><polygon class="cls-1" points="95.63 82.81 47.81 110.42 0 82.81 0 27.61 47.81 0 95.63 27.61 95.63 82.81"></polygon><path class="cls-2" d="M47.81,18.64A36.57,36.57,0,1,0,84.38,55.21,36.57,36.57,0,0,0,47.81,18.64Zm0,63.92A27.35,27.35,0,1,1,75.16,55.21,27.35,27.35,0,0,1,47.81,82.56Z"></path><path class="cls-2" d="M47.81,39.46A15.75,15.75,0,1,0,63.56,55.21,15.75,15.75,0,0,0,47.81,39.46Zm0,24.25a8.5,8.5,0,1,1,8.5-8.5A8.51,8.51,0,0,1,47.81,63.71Z"></path></g></g></svg> \ No newline at end of file
diff --git a/deluge/tests/data/v2_hybrid.torrent b/deluge/tests/data/v2_hybrid.torrent
new file mode 100644
index 000000000..e58057cc2
--- /dev/null
+++ b/deluge/tests/data/v2_hybrid.torrent
Binary files differ
diff --git a/deluge/tests/data/v2_test.torrent b/deluge/tests/data/v2_test.torrent
new file mode 100644
index 000000000..fe6cbd044
--- /dev/null
+++ b/deluge/tests/data/v2_test.torrent
Binary files differ
diff --git a/deluge/tests/test_alertmanager.py b/deluge/tests/test_alertmanager.py
index f197882cd..2d18f4bf0 100644
--- a/deluge/tests/test_alertmanager.py
+++ b/deluge/tests/test_alertmanager.py
@@ -1,39 +1,102 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
+from dataclasses import dataclass
-from __future__ import unicode_literals
+import pytest
-import deluge.component as component
from deluge.core.core import Core
-from .basetest import BaseTestCase
+class LtSessionMock:
+ def __init__(self):
+ self.alerts = []
-class AlertManagerTestCase(BaseTestCase):
- def set_up(self):
+ def push_alerts(self, alerts):
+ self.alerts = alerts
+
+ def wait_for_alert(self, timeout):
+ return self.alerts[0] if len(self.alerts) > 0 else None
+
+ def pop_alerts(self):
+ alerts = self.alerts
+ self.alerts = []
+ return alerts
+
+
+@dataclass
+class LtAlertMock:
+ type: int
+ name: str
+ message: str
+
+ def message(self):
+ return self.message
+
+ def what(self):
+ return self.name
+
+
+@pytest.fixture
+def mock_alert1():
+ return LtAlertMock(type=1, name='mock_alert1', message='Alert 1')
+
+
+@pytest.fixture
+def mock_alert2():
+ return LtAlertMock(type=2, name='mock_alert2', message='Alert 2')
+
+
+class TestAlertManager:
+ @pytest.fixture(autouse=True)
+ def set_up(self, component):
self.core = Core()
self.core.config.config['lsd'] = False
self.am = component.get('AlertManager')
- return component.start(['AlertManager'])
+ self.am.session = LtSessionMock()
- def tear_down(self):
- return component.shutdown()
+ component.start(['AlertManager'])
def test_register_handler(self):
def handler(alert):
- return
+ ...
+
+ self.am.register_handler('dummy1', handler)
+ self.am.register_handler('dummy2_alert', handler)
+ assert self.am.handlers['dummy1'] == [handler]
+ assert self.am.handlers['dummy2'] == [handler]
+
+ async def test_pop_alert(self, mock_callback, mock_alert1, mock_alert2):
+ self.am.register_handler('mock_alert1', mock_callback)
+
+ self.am.session.push_alerts([mock_alert1, mock_alert2])
+
+ await mock_callback.deferred
+
+ mock_callback.assert_called_once_with(mock_alert1)
+
+ async def test_pause_not_pop_alert(
+ self, component, mock_alert1, mock_alert2, mock_callback
+ ):
+ await component.pause(['AlertManager'])
+
+ self.am.register_handler('mock_alert1', mock_callback)
+ self.am.session.push_alerts([mock_alert1, mock_alert2])
+
+ await mock_callback.deferred
- self.am.register_handler('dummy_alert', handler)
- self.assertEqual(self.am.handlers['dummy_alert'], [handler])
+ mock_callback.assert_not_called()
+ assert not self.am._event.is_set()
+ assert len(self.am.session.alerts) == 2
def test_deregister_handler(self):
def handler(alert):
- return
+ ...
- self.am.register_handler('dummy_alert', handler)
+ self.am.register_handler('dummy1', handler)
+ self.am.register_handler('dummy2_alert', handler)
self.am.deregister_handler(handler)
- self.assertEqual(self.am.handlers['dummy_alert'], [])
+ assert self.am.handlers['dummy1'] == []
+ assert self.am.handlers['dummy2'] == []
diff --git a/deluge/tests/test_authmanager.py b/deluge/tests/test_authmanager.py
index 91e122f73..aa86fdbac 100644
--- a/deluge/tests/test_authmanager.py
+++ b/deluge/tests/test_authmanager.py
@@ -1,20 +1,16 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.common import get_localhost_auth
+from deluge.conftest import BaseTestCase
from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AuthManager
-from .basetest import BaseTestCase
-
-class AuthManagerTestCase(BaseTestCase):
+class TestAuthManager(BaseTestCase):
def set_up(self):
self.auth = AuthManager()
self.auth.start()
@@ -24,4 +20,4 @@ class AuthManagerTestCase(BaseTestCase):
return component.shutdown()
def test_authorize(self):
- self.assertEqual(self.auth.authorize(*get_localhost_auth()), AUTH_LEVEL_ADMIN)
+ assert self.auth.authorize(*get_localhost_auth()) == AUTH_LEVEL_ADMIN
diff --git a/deluge/tests/test_bencode.py b/deluge/tests/test_bencode.py
index b49c21f83..a4a76818f 100644
--- a/deluge/tests/test_bencode.py
+++ b/deluge/tests/test_bencode.py
@@ -1,19 +1,17 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-from twisted.trial import unittest
+import pytest
from deluge import bencode
from . import common
-class BencodeTestCase(unittest.TestCase):
+class TestBencode:
def test_bencode_unicode_metainfo(self):
filename = common.get_test_data_file('test.torrent')
with open(filename, 'rb') as _file:
@@ -21,14 +19,14 @@ class BencodeTestCase(unittest.TestCase):
bencode.bencode({b'info': metainfo})
def test_bencode_unicode_value(self):
- self.assertEqual(bencode.bencode(b'abc'), b'3:abc')
- self.assertEqual(bencode.bencode('abc'), b'3:abc')
+ assert bencode.bencode(b'abc') == b'3:abc'
+ assert bencode.bencode('abc') == b'3:abc'
def test_bdecode(self):
- self.assertEqual(bencode.bdecode(b'3:dEf'), b'dEf')
- with self.assertRaises(bencode.BTFailure):
+ assert bencode.bdecode(b'3:dEf') == b'dEf'
+ with pytest.raises(bencode.BTFailure):
bencode.bdecode('dEf')
- with self.assertRaises(bencode.BTFailure):
+ with pytest.raises(bencode.BTFailure):
bencode.bdecode(b'dEf')
- with self.assertRaises(bencode.BTFailure):
+ with pytest.raises(bencode.BTFailure):
bencode.bdecode({'dEf': 123})
diff --git a/deluge/tests/test_client.py b/deluge/tests/test_client.py
index ae1e95a71..763d43c59 100644
--- a/deluge/tests/test_client.py
+++ b/deluge/tests/test_client.py
@@ -1,23 +1,17 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-
-from __future__ import unicode_literals
-
+import pytest
+import pytest_twisted
from twisted.internet import defer
-import deluge.component as component
from deluge import error
-from deluge.common import AUTH_LEVEL_NORMAL, get_localhost_auth
+from deluge.common import AUTH_LEVEL_NORMAL, get_localhost_auth, get_version
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
from deluge.ui.client import Client, DaemonSSLProxy, client
-from .basetest import BaseTestCase
-from .daemon_base import DaemonBase
-
class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy):
def authenticate(self, username, password):
@@ -78,24 +72,13 @@ class NoVersionSendingClient(Client):
self.disconnect_callback()
-class ClientTestCase(BaseTestCase, DaemonBase):
- def set_up(self):
- d = self.common_set_up()
- d.addCallback(self.start_core)
- d.addErrback(self.terminate_core)
- return d
-
- def tear_down(self):
- d = component.shutdown()
- d.addCallback(self.terminate_core)
- return d
-
+@pytest.mark.usefixtures('daemon', 'client')
+class TestClient:
def test_connect_no_credentials(self):
d = client.connect('localhost', self.listen_port, username='', password='')
def on_connect(result):
- self.assertEqual(client.get_auth_level(), AUTH_LEVEL_ADMIN)
- self.addCleanup(client.disconnect)
+ assert client.get_auth_level() == AUTH_LEVEL_ADMIN
return result
d.addCallbacks(on_connect, self.fail)
@@ -108,8 +91,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
)
def on_connect(result):
- self.assertEqual(client.get_auth_level(), AUTH_LEVEL_ADMIN)
- self.addCleanup(client.disconnect)
+ assert client.get_auth_level() == AUTH_LEVEL_ADMIN
return result
d.addCallbacks(on_connect, self.fail)
@@ -122,21 +104,18 @@ class ClientTestCase(BaseTestCase, DaemonBase):
)
def on_failure(failure):
- self.assertEqual(failure.trap(error.BadLoginError), error.BadLoginError)
- self.assertEqual(failure.value.message, 'Password does not match')
- self.addCleanup(client.disconnect)
+ assert failure.trap(error.BadLoginError) == error.BadLoginError
+ assert failure.value.message == 'Password does not match'
d.addCallbacks(self.fail, on_failure)
return d
def test_connect_invalid_user(self):
- username, password = get_localhost_auth()
d = client.connect('localhost', self.listen_port, username='invalid-user')
def on_failure(failure):
- self.assertEqual(failure.trap(error.BadLoginError), error.BadLoginError)
- self.assertEqual(failure.value.message, 'Username does not exist')
- self.addCleanup(client.disconnect)
+ assert failure.trap(error.BadLoginError) == error.BadLoginError
+ assert failure.value.message == 'Username does not exist'
d.addCallbacks(self.fail, on_failure)
return d
@@ -146,16 +125,16 @@ class ClientTestCase(BaseTestCase, DaemonBase):
d = client.connect('localhost', self.listen_port, username=username)
def on_failure(failure):
- self.assertEqual(
- failure.trap(error.AuthenticationRequired), error.AuthenticationRequired
+ assert (
+ failure.trap(error.AuthenticationRequired)
+ == error.AuthenticationRequired
)
- self.assertEqual(failure.value.username, username)
- self.addCleanup(client.disconnect)
+ assert failure.value.username == username
d.addCallbacks(self.fail, on_failure)
return d
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_connect_with_password(self):
username, password = get_localhost_auth()
yield client.connect(
@@ -166,19 +145,15 @@ class ClientTestCase(BaseTestCase, DaemonBase):
ret = yield client.connect(
'localhost', self.listen_port, username='testuser', password='testpw'
)
- self.assertEqual(ret, AUTH_LEVEL_NORMAL)
- yield
+ assert ret == AUTH_LEVEL_NORMAL
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_invalid_rpc_method_call(self):
yield client.connect('localhost', self.listen_port, username='', password='')
d = client.core.invalid_method()
def on_failure(failure):
- self.assertEqual(
- failure.trap(error.WrappedException), error.WrappedException
- )
- self.addCleanup(client.disconnect)
+ assert failure.trap(error.WrappedException) == error.WrappedException
d.addCallbacks(self.fail, on_failure)
yield d
@@ -191,10 +166,27 @@ class ClientTestCase(BaseTestCase, DaemonBase):
)
def on_failure(failure):
- self.assertEqual(
- failure.trap(error.IncompatibleClient), error.IncompatibleClient
- )
- self.addCleanup(no_version_sending_client.disconnect)
+ assert failure.trap(error.IncompatibleClient) == error.IncompatibleClient
d.addCallbacks(self.fail, on_failure)
return d
+
+ @pytest_twisted.inlineCallbacks
+ def test_daemon_version(self):
+ username, password = get_localhost_auth()
+ yield client.connect(
+ 'localhost', self.listen_port, username=username, password=password
+ )
+
+ assert client.daemon_version == get_version()
+
+ @pytest_twisted.inlineCallbacks
+ def test_daemon_version_check_min(self):
+ username, password = get_localhost_auth()
+ yield client.connect(
+ 'localhost', self.listen_port, username=username, password=password
+ )
+
+ assert client.daemon_version_check_min(get_version())
+ assert not client.daemon_version_check_min(f'{get_version()}1')
+ assert client.daemon_version_check_min('0.1.0')
diff --git a/deluge/tests/test_common.py b/deluge/tests/test_common.py
index 4f6aa2fd4..a1af6cce9 100644
--- a/deluge/tests/test_common.py
+++ b/deluge/tests/test_common.py
@@ -1,17 +1,15 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
import sys
import tarfile
+from urllib.parse import quote_plus
-from twisted.trial import unittest
+import pytest
from deluge.common import (
VersionSplit,
@@ -22,145 +20,157 @@ from deluge.common import (
fsize,
fspeed,
ftime,
+ get_magnet_info,
get_path_size,
is_infohash,
+ is_interface,
+ is_interface_name,
is_ip,
is_ipv4,
is_ipv6,
is_magnet,
is_url,
+ parse_human_size,
windows_check,
)
-from deluge.i18n import setup_translation
-
-from .common import get_test_data_file, set_tmp_config_dir
+from .common import get_test_data_file
-class CommonTestCase(unittest.TestCase):
- def setUp(self): # NOQA
- self.config_dir = set_tmp_config_dir()
- setup_translation()
-
- def tearDown(self): # NOQA
- pass
+class TestCommon:
def test_fsize(self):
- self.assertEqual(fsize(0), '0 B')
- self.assertEqual(fsize(100), '100 B')
- self.assertEqual(fsize(1023), '1023 B')
- self.assertEqual(fsize(1024), '1.0 KiB')
- self.assertEqual(fsize(1048575), '1024.0 KiB')
- self.assertEqual(fsize(1048576), '1.0 MiB')
- self.assertEqual(fsize(1073741823), '1024.0 MiB')
- self.assertEqual(fsize(1073741824), '1.0 GiB')
- self.assertEqual(fsize(112245), '109.6 KiB')
- self.assertEqual(fsize(110723441824), '103.1 GiB')
- self.assertEqual(fsize(1099511627775), '1024.0 GiB')
- self.assertEqual(fsize(1099511627777), '1.0 TiB')
- self.assertEqual(fsize(766148267453245), '696.8 TiB')
+ assert fsize(0) == '0 B'
+ assert fsize(100) == '100 B'
+ assert fsize(1023) == '1023 B'
+ assert fsize(1024) == '1.0 KiB'
+ assert fsize(1048575) == '1024.0 KiB'
+ assert fsize(1048576) == '1.0 MiB'
+ assert fsize(1073741823) == '1024.0 MiB'
+ assert fsize(1073741824) == '1.0 GiB'
+ assert fsize(112245) == '109.6 KiB'
+ assert fsize(110723441824) == '103.1 GiB'
+ assert fsize(1099511627775) == '1024.0 GiB'
+ assert fsize(1099511627777) == '1.0 TiB'
+ assert fsize(766148267453245) == '696.8 TiB'
def test_fpcnt(self):
- self.assertTrue(fpcnt(0.9311) == '93.11%')
+ assert fpcnt(0.9311) == '93.11%'
def test_fspeed(self):
- self.assertTrue(fspeed(43134) == '42.1 KiB/s')
+ assert fspeed(43134) == '42.1 KiB/s'
def test_fpeer(self):
- self.assertTrue(fpeer(10, 20) == '10 (20)')
- self.assertTrue(fpeer(10, -1) == '10')
+ assert fpeer(10, 20) == '10 (20)'
+ assert fpeer(10, -1) == '10'
def test_ftime(self):
- self.assertEqual(ftime(0), '')
- self.assertEqual(ftime(5), '5s')
- self.assertEqual(ftime(100), '1m 40s')
- self.assertEqual(ftime(3789), '1h 3m')
- self.assertEqual(ftime(23011), '6h 23m')
- self.assertEqual(ftime(391187), '4d 12h')
- self.assertEqual(ftime(604800), '1w 0d')
- self.assertEqual(ftime(13893086), '22w 6d')
- self.assertEqual(ftime(59740269), '1y 46w')
- self.assertEqual(ftime(61.25), '1m 1s')
- self.assertEqual(ftime(119.9), '1m 59s')
+ assert ftime(0) == ''
+ assert ftime(5) == '5s'
+ assert ftime(100) == '1m 40s'
+ assert ftime(3789) == '1h 3m'
+ assert ftime(23011) == '6h 23m'
+ assert ftime(391187) == '4d 12h'
+ assert ftime(604800) == '1w 0d'
+ assert ftime(13893086) == '22w 6d'
+ assert ftime(59740269) == '1y 46w'
+ assert ftime(61.25) == '1m 1s'
+ assert ftime(119.9) == '1m 59s'
def test_fdate(self):
- self.assertTrue(fdate(-1) == '')
+ assert fdate(-1) == ''
def test_is_url(self):
- self.assertTrue(is_url('http://deluge-torrent.org'))
- self.assertFalse(is_url('file://test.torrent'))
+ assert is_url('http://deluge-torrent.org')
+ assert not is_url('file://test.torrent')
def test_is_magnet(self):
- self.assertTrue(
- is_magnet('magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN')
- )
- self.assertFalse(is_magnet(None))
+ assert is_magnet('magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN')
+ assert not is_magnet(None)
def test_is_infohash(self):
- self.assertTrue(is_infohash('2dc5d0e71a66fe69649a640d39cb00a259704973'))
+ assert is_infohash('2dc5d0e71a66fe69649a640d39cb00a259704973')
def test_get_path_size(self):
if windows_check() and sys.version_info < (3, 8):
# https://bugs.python.org/issue1311
- raise unittest.SkipTest('os.devnull returns False on Windows')
- self.assertTrue(get_path_size(os.devnull) == 0)
- self.assertTrue(get_path_size('non-existant.file') == -1)
+ pytest.skip('os.devnull returns False on Windows')
+ assert get_path_size(os.devnull) == 0
+ assert get_path_size('non-existant.file') == -1
def test_is_ip(self):
- self.assertTrue(is_ip('192.0.2.0'))
- self.assertFalse(is_ip('192..0.0'))
- self.assertTrue(is_ip('2001:db8::'))
- self.assertFalse(is_ip('2001:db8:'))
+ assert is_ip('192.0.2.0')
+ assert not is_ip('192..0.0')
+ assert is_ip('2001:db8::')
+ assert not is_ip('2001:db8:')
def test_is_ipv4(self):
- self.assertTrue(is_ipv4('192.0.2.0'))
- self.assertFalse(is_ipv4('192..0.0'))
+ assert is_ipv4('192.0.2.0')
+ assert not is_ipv4('192..0.0')
def test_is_ipv6(self):
- self.assertTrue(is_ipv6('2001:db8::'))
- self.assertFalse(is_ipv6('2001:db8:'))
+ assert is_ipv6('2001:db8::')
+ assert not is_ipv6('2001:db8:')
+
+ def test_is_interface_name(self):
+ if windows_check():
+ assert not is_interface_name('2001:db8:')
+ assert not is_interface_name('{THIS0000-IS00-ONLY-FOR0-TESTING00000}')
+ else:
+ assert is_interface_name('lo')
+ assert not is_interface_name('127.0.0.1')
+ assert not is_interface_name('eth01101')
+
+ def test_is_interface(self):
+ if windows_check():
+ assert is_interface('127.0.0.1')
+ assert not is_interface('127')
+ assert not is_interface('{THIS0000-IS00-ONLY-FOR0-TESTING00000}')
+ else:
+ assert is_interface('lo')
+ assert is_interface('127.0.0.1')
+ assert not is_interface('127.')
+ assert not is_interface('eth01101')
def test_version_split(self):
- self.assertTrue(VersionSplit('1.2.2') == VersionSplit('1.2.2'))
- self.assertTrue(VersionSplit('1.2.1') < VersionSplit('1.2.2'))
- self.assertTrue(VersionSplit('1.1.9') < VersionSplit('1.2.2'))
- self.assertTrue(VersionSplit('1.2.2') > VersionSplit('1.2.1'))
- self.assertTrue(VersionSplit('1.2.2') > VersionSplit('1.2.2-dev0'))
- self.assertTrue(VersionSplit('1.2.2-dev') < VersionSplit('1.3.0-rc2'))
- self.assertTrue(VersionSplit('1.2.2') > VersionSplit('1.2.2-rc2'))
- self.assertTrue(VersionSplit('1.2.2-rc2-dev') < VersionSplit('1.2.2-rc2'))
- self.assertTrue(VersionSplit('1.2.2-rc3') > VersionSplit('1.2.2-rc2'))
- self.assertTrue(VersionSplit('0.14.9') == VersionSplit('0.14.9'))
- self.assertTrue(VersionSplit('0.14.9') > VersionSplit('0.14.5'))
- self.assertTrue(VersionSplit('0.14.10') >= VersionSplit('0.14.9'))
- self.assertTrue(VersionSplit('1.4.0') > VersionSplit('1.3.900.dev123'))
- self.assertTrue(VersionSplit('1.3.2rc2.dev1') < VersionSplit('1.3.2-rc2'))
- self.assertTrue(VersionSplit('1.3.900.dev888') > VersionSplit('1.3.900.dev123'))
- self.assertTrue(VersionSplit('1.4.0') > VersionSplit('1.4.0.dev123'))
- self.assertTrue(VersionSplit('1.4.0.dev1') < VersionSplit('1.4.0'))
- self.assertTrue(VersionSplit('1.4.0a1') < VersionSplit('1.4.0'))
-
- def test_parse_human_size(self):
- from deluge.common import parse_human_size
-
- sizes = [
+ assert VersionSplit('1.2.2') == VersionSplit('1.2.2')
+ assert VersionSplit('1.2.1') < VersionSplit('1.2.2')
+ assert VersionSplit('1.1.9') < VersionSplit('1.2.2')
+ assert VersionSplit('1.2.2') > VersionSplit('1.2.1')
+ assert VersionSplit('1.2.2') > VersionSplit('1.2.2-dev0')
+ assert VersionSplit('1.2.2-dev') < VersionSplit('1.3.0-rc2')
+ assert VersionSplit('1.2.2') > VersionSplit('1.2.2-rc2')
+ assert VersionSplit('1.2.2-rc2-dev') < VersionSplit('1.2.2-rc2')
+ assert VersionSplit('1.2.2-rc3') > VersionSplit('1.2.2-rc2')
+ assert VersionSplit('0.14.9') == VersionSplit('0.14.9')
+ assert VersionSplit('0.14.9') > VersionSplit('0.14.5')
+ assert VersionSplit('0.14.10') >= VersionSplit('0.14.9')
+ assert VersionSplit('1.4.0') > VersionSplit('1.3.900.dev123')
+ assert VersionSplit('1.3.2rc2.dev1') < VersionSplit('1.3.2-rc2')
+ assert VersionSplit('1.3.900.dev888') > VersionSplit('1.3.900.dev123')
+ assert VersionSplit('1.4.0') > VersionSplit('1.4.0.dev123')
+ assert VersionSplit('1.4.0.dev1') < VersionSplit('1.4.0')
+ assert VersionSplit('1.4.0a1') < VersionSplit('1.4.0')
+
+ @pytest.mark.parametrize(
+ ('human_size', 'expected'),
+ [
('1', 1),
('10 bytes', 10),
('2048 bytes', 2048),
('1MiB', 2 ** (10 * 2)),
('1 MiB', 2 ** (10 * 2)),
('1 GiB', 2 ** (10 * 3)),
- ('1 GiB', 2 ** (10 * 3)),
- ('1M', 10 ** 6),
- ('1MB', 10 ** 6),
- ('1 GB', 10 ** 9),
- ('1 TB', 10 ** 12),
- ]
-
- for human_size, byte_size in sizes:
- parsed = parse_human_size(human_size)
- self.assertEqual(
- parsed, byte_size, 'Mismatch when converting: %s' % human_size
- )
+ ('1 TiB', 2 ** (10 * 4)),
+ ('1M', 10**6),
+ ('1p', 10**15),
+ ('1MB', 10**6),
+ ('1 GB', 10**9),
+ ('1 TB', 10**12),
+ ],
+ )
+ def test_parse_human_size(self, human_size, expected):
+ parsed = parse_human_size(human_size)
+ assert parsed == expected, 'Mismatch when converting: %s' % human_size
def test_archive_files(self):
arc_filelist = [
@@ -171,10 +181,10 @@ class CommonTestCase(unittest.TestCase):
with tarfile.open(arc_filepath, 'r') as tar:
for tar_info in tar:
- self.assertTrue(tar_info.isfile())
- self.assertTrue(
- tar_info.name in [os.path.basename(arcf) for arcf in arc_filelist]
- )
+ assert tar_info.isfile()
+ assert tar_info.name in [
+ os.path.basename(arcf) for arcf in arc_filelist
+ ]
def test_archive_files_missing(self):
"""Archive exists even with file not found."""
@@ -185,8 +195,8 @@ class CommonTestCase(unittest.TestCase):
filelist.remove('missing.file')
with tarfile.open(arc_filepath, 'r') as tar:
- self.assertEqual(tar.getnames(), filelist)
- self.assertTrue(all(tarinfo.isfile() for tarinfo in tar))
+ assert tar.getnames() == filelist
+ assert all(tarinfo.isfile() for tarinfo in tar)
def test_archive_files_message(self):
filelist = ['test.torrent', 'deluge.png']
@@ -196,9 +206,22 @@ class CommonTestCase(unittest.TestCase):
result_files = filelist + ['archive_message.txt']
with tarfile.open(arc_filepath, 'r') as tar:
- self.assertEqual(tar.getnames(), result_files)
+ assert tar.getnames() == result_files
for tar_info in tar:
- self.assertTrue(tar_info.isfile())
+ assert tar_info.isfile()
if tar_info.name == 'archive_message.txt':
result = tar.extractfile(tar_info).read().decode()
- self.assertEqual(result, 'test')
+ assert result == 'test'
+
+ def test_get_magnet_info_tiers(self):
+ tracker1 = 'udp://tracker1.example.com'
+ tracker2 = 'udp://tracker2.example.com'
+ magnet = (
+ 'magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN'
+ f'&tr.1={quote_plus(tracker1)}'
+ f'&tr.2={quote_plus(tracker2)}'
+ )
+ result = get_magnet_info(magnet)
+ assert result['info_hash'] == '953bad769164e8482c7785a21d12166f94b9e14d'
+ assert result['trackers'][tracker1] == 1
+ assert result['trackers'][tracker2] == 2
diff --git a/deluge/tests/test_component.py b/deluge/tests/test_component.py
index 26f24ad00..907d50beb 100644
--- a/deluge/tests/test_component.py
+++ b/deluge/tests/test_component.py
@@ -1,267 +1,192 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
+import time
+from unittest.mock import Mock
-from __future__ import unicode_literals
-
+import pytest
+import pytest_twisted
from twisted.internet import defer, threads
-from twisted.trial.unittest import SkipTest
import deluge.component as component
-from .basetest import BaseTestCase
-
class ComponentTester(component.Component):
def __init__(self, name, depend=None):
- component.Component.__init__(self, name, depend=depend)
- self.start_count = 0
- self.stop_count = 0
+ super().__init__(name, depend=depend)
+ event_methods = ('start', 'update', 'pause', 'resume', 'stop', 'shutdown')
+ for event_method in event_methods:
+ setattr(self, event_method, Mock())
- def start(self):
- self.start_count += 1
- def stop(self):
- self.stop_count += 1
+class ComponentTesterDelayStart(ComponentTester):
+ def __init__(self, name, depend=None):
+ super().__init__(name, depend=depend)
+ self.start = Mock(side_effect=self.delay)
+ @pytest_twisted.inlineCallbacks
+ def delay(self):
+ yield threads.deferToThread(time.sleep, 0.5)
-class ComponentTesterDelayStart(ComponentTester):
- def start(self):
- def do_sleep():
- import time
- time.sleep(1)
+@pytest.mark.usefixtures('component')
+class TestComponent:
+ async def test_start_component(self):
+ c = ComponentTester('test_start')
+ await component.start(['test_start'])
- d = threads.deferToThread(do_sleep)
+ assert c._component_state == 'Started'
+ assert c.start.call_count == 1
- def on_done(result):
- self.start_count += 1
+ async def test_start_stop_depends(self):
+ c1 = ComponentTester('test_start_depends_c1')
+ c2 = ComponentTester('test_start_depends_c2', depend=['test_start_depends_c1'])
- return d.addCallback(on_done)
+ await component.start('test_start_depends_c2')
+ assert c1._component_state == 'Started'
+ assert c2._component_state == 'Started'
+ assert c1.start.call_count == 1
+ assert c2.start.call_count == 1
-class ComponentTesterUpdate(component.Component):
- def __init__(self, name):
- component.Component.__init__(self, name)
- self.counter = 0
- self.start_count = 0
- self.stop_count = 0
+ await component.stop(['test_start_depends_c1'])
- def update(self):
- self.counter += 1
+ assert c1._component_state == 'Stopped'
+ assert c2._component_state == 'Stopped'
+ assert c1.stop.call_count == 1
+ assert c2.stop.call_count == 1
- def stop(self):
- self.stop_count += 1
+ async def start_with_depends(self):
+ c1 = ComponentTesterDelayStart('test_start_all_c1')
+ c2 = ComponentTester('test_start_all_c2', depend=['test_start_all_c4'])
+ c3 = ComponentTesterDelayStart(
+ 'test_start_all_c3', depend=['test_start_all_c5', 'test_start_all_c1']
+ )
+ c4 = ComponentTester('test_start_all_c4', depend=['test_start_all_c3'])
+ c5 = ComponentTester('test_start_all_c5')
+ await component.start()
+ return c1, c2, c3, c4, c5
-class ComponentTesterShutdown(component.Component):
- def __init__(self, name):
- component.Component.__init__(self, name)
- self.shutdowned = False
- self.stop_count = 0
+ def finish_start_with_depends(self, *args):
+ for c in args[1:]:
+ component.deregister(c)
- def shutdown(self):
- self.shutdowned = True
+ async def test_start_all(self):
+ components = await self.start_with_depends()
+ for c in components:
+ assert c._component_state == 'Started'
+ assert c.start.call_count == 1
- def stop(self):
- self.stop_count += 1
+ self.finish_start_with_depends(components)
+ def test_register_exception(self):
+ ComponentTester('test_register_exception')
+ with pytest.raises(component.ComponentAlreadyRegistered):
+ ComponentTester(
+ 'test_register_exception',
+ )
-class ComponentTestClass(BaseTestCase):
- def tear_down(self):
- return component.shutdown()
+ async def test_stop(self):
+ c = ComponentTester('test_stop')
- def test_start_component(self):
- def on_start(result, c):
- self.assertEqual(c._component_state, 'Started')
- self.assertEqual(c.start_count, 1)
+ await component.start(['test_stop'])
- c = ComponentTester('test_start_c1')
- d = component.start(['test_start_c1'])
- d.addCallback(on_start, c)
- return d
+ assert c._component_state == 'Started'
- def test_start_stop_depends(self):
- def on_stop(result, c1, c2):
- self.assertEqual(c1._component_state, 'Stopped')
- self.assertEqual(c2._component_state, 'Stopped')
- self.assertEqual(c1.stop_count, 1)
- self.assertEqual(c2.stop_count, 1)
+ await component.stop(['test_stop'])
- def on_start(result, c1, c2):
- self.assertEqual(c1._component_state, 'Started')
- self.assertEqual(c2._component_state, 'Started')
- self.assertEqual(c1.start_count, 1)
- self.assertEqual(c2.start_count, 1)
- return component.stop(['test_start_depends_c1']).addCallback(
- on_stop, c1, c2
- )
+ assert c._component_state == 'Stopped'
+ assert not c._component_timer.running
+ assert c.stop.call_count == 1
- c1 = ComponentTester('test_start_depends_c1')
- c2 = ComponentTester('test_start_depends_c2', depend=['test_start_depends_c1'])
+ async def test_stop_all(self):
+ components = await self.start_with_depends()
+ assert all(c._component_state == 'Started' for c in components)
- d = component.start(['test_start_depends_c2'])
- d.addCallback(on_start, c1, c2)
- return d
+ component.stop()
+ for c in components:
+ assert c._component_state == 'Stopped'
+ assert c.stop.call_count == 1
- def start_with_depends(self):
- c1 = ComponentTesterDelayStart('test_start_all_c1')
- c2 = ComponentTester('test_start_all_c2', depend=['test_start_all_c4'])
- c3 = ComponentTesterDelayStart(
- 'test_start_all_c3', depend=['test_start_all_c5', 'test_start_all_c1']
- )
- c4 = ComponentTester('test_start_all_c4', depend=['test_start_all_c3'])
- c5 = ComponentTester('test_start_all_c5')
+ self.finish_start_with_depends(components)
- d = component.start()
- return (d, c1, c2, c3, c4, c5)
+ async def test_update(self):
+ c = ComponentTester('test_update')
+ init_update_count = int(c.update.call_count)
+ await component.start(['test_update'])
- def finish_start_with_depends(self, *args):
- for c in args[1:]:
- component.deregister(c)
+ assert c._component_timer
+ assert c._component_timer.running
+ assert c.update.call_count != init_update_count
+ await component.stop()
- def test_start_all(self):
- def on_start(*args):
- for c in args[1:]:
- self.assertEqual(c._component_state, 'Started')
- self.assertEqual(c.start_count, 1)
+ async def test_pause(self):
+ c = ComponentTester('test_pause')
+ init_update_count = int(c.update.call_count)
- ret = self.start_with_depends()
- ret[0].addCallback(on_start, *ret[1:])
- ret[0].addCallback(self.finish_start_with_depends, *ret[1:])
- return ret[0]
+ await component.start(['test_pause'])
- def test_register_exception(self):
- ComponentTester('test_register_exception_c1')
- self.assertRaises(
- component.ComponentAlreadyRegistered,
- ComponentTester,
- 'test_register_exception_c1',
- )
+ assert c._component_timer
+ assert c.update.call_count != init_update_count
- def test_stop_component(self):
- def on_stop(result, c):
- self.assertEqual(c._component_state, 'Stopped')
- self.assertFalse(c._component_timer.running)
- self.assertEqual(c.stop_count, 1)
-
- def on_start(result, c):
- self.assertEqual(c._component_state, 'Started')
- return component.stop(['test_stop_component_c1']).addCallback(on_stop, c)
-
- c = ComponentTesterUpdate('test_stop_component_c1')
- d = component.start(['test_stop_component_c1'])
- d.addCallback(on_start, c)
- return d
-
- def test_stop_all(self):
- def on_stop(result, *args):
- for c in args:
- self.assertEqual(c._component_state, 'Stopped')
- self.assertEqual(c.stop_count, 1)
-
- def on_start(result, *args):
- for c in args:
- self.assertEqual(c._component_state, 'Started')
- return component.stop().addCallback(on_stop, *args)
-
- ret = self.start_with_depends()
- ret[0].addCallback(on_start, *ret[1:])
- ret[0].addCallback(self.finish_start_with_depends, *ret[1:])
- return ret[0]
-
- def test_update(self):
- def on_start(result, c1, counter):
- self.assertTrue(c1._component_timer)
- self.assertTrue(c1._component_timer.running)
- self.assertNotEqual(c1.counter, counter)
- return component.stop()
-
- c1 = ComponentTesterUpdate('test_update_c1')
- cnt = int(c1.counter)
- d = component.start(['test_update_c1'])
-
- d.addCallback(on_start, c1, cnt)
- return d
-
- def test_pause(self):
- def on_pause(result, c1, counter):
- self.assertEqual(c1._component_state, 'Paused')
- self.assertNotEqual(c1.counter, counter)
- self.assertFalse(c1._component_timer.running)
-
- def on_start(result, c1, counter):
- self.assertTrue(c1._component_timer)
- self.assertNotEqual(c1.counter, counter)
- d = component.pause(['test_pause_c1'])
- d.addCallback(on_pause, c1, counter)
- return d
-
- c1 = ComponentTesterUpdate('test_pause_c1')
- cnt = int(c1.counter)
- d = component.start(['test_pause_c1'])
-
- d.addCallback(on_start, c1, cnt)
- return d
-
- @defer.inlineCallbacks
- def test_component_start_error(self):
- ComponentTesterUpdate('test_pause_c1')
- yield component.start(['test_pause_c1'])
- yield component.pause(['test_pause_c1'])
- test_comp = component.get('test_pause_c1')
- try:
- result = self.failureResultOf(test_comp._component_start())
- except AttributeError:
- raise SkipTest(
- 'This test requires trial failureResultOf() in Twisted version >= 13'
- )
- self.assertEqual(
- result.check(component.ComponentException), component.ComponentException
- )
+ await component.pause(['test_pause'])
- @defer.inlineCallbacks
- def test_start_paused_error(self):
- ComponentTesterUpdate('test_pause_c1')
- yield component.start(['test_pause_c1'])
- yield component.pause(['test_pause_c1'])
-
- # Deferreds that fail in component have to error handler which results in
- # twisted doing a log.err call which causes the test to fail.
- # Prevent failure by ignoring the exception
- self._observer._ignoreErrors(component.ComponentException)
-
- result = yield component.start()
- self.assertEqual(
- [(result[0][0], result[0][1].value)],
- [
- (
- defer.FAILURE,
- component.ComponentException(
- 'Trying to start component "%s" but it is '
- 'not in a stopped state. Current state: %s'
- % ('test_pause_c1', 'Paused'),
- '',
- ),
- )
- ],
+ assert c._component_state == 'Paused'
+ assert c.pause.call_count == 1
+ assert c.update.call_count != init_update_count
+ assert not c._component_timer.running
+
+ async def test_resume(self):
+ c = ComponentTester('test_resume')
+
+ await component.start(['test_resume'])
+
+ assert c._component_state == 'Started'
+
+ await component.pause(['test_resume'])
+
+ assert c._component_state == 'Paused'
+
+ await component.resume(['test_resume'])
+
+ assert c._component_state == 'Started'
+ assert c.resume.call_count == 1
+ assert c._component_timer.running
+
+ async def test_component_start_error(self):
+ ComponentTester('test_start_error')
+ await component.start(['test_start_error'])
+ await component.pause(['test_start_error'])
+ test_comp = component.get('test_start_error')
+ with pytest.raises(component.ComponentException, match='Current state: Paused'):
+ await test_comp._component_start()
+
+ async def test_start_paused_error(self):
+ name = 'test_pause_error'
+ ComponentTester(name)
+ await component.start([name])
+ await component.pause([name])
+
+ (failure, error), *_ = await component.start()
+ assert (failure, error.type, error.value.message) == (
+ defer.FAILURE,
+ component.ComponentException,
+ (
+ f'Trying to start component "{name}" but it is '
+ 'not in a stopped state. Current state: Paused'
+ ),
)
- def test_shutdown(self):
- def on_shutdown(result, c1):
- self.assertTrue(c1.shutdowned)
- self.assertEqual(c1._component_state, 'Stopped')
- self.assertEqual(c1.stop_count, 1)
-
- def on_start(result, c1):
- d = component.shutdown()
- d.addCallback(on_shutdown, c1)
- return d
-
- c1 = ComponentTesterShutdown('test_shutdown_c1')
- d = component.start(['test_shutdown_c1'])
- d.addCallback(on_start, c1)
- return d
+ async def test_shutdown(self):
+ c = ComponentTester('test_shutdown')
+
+ await component.start(['test_shutdown'])
+ await component.shutdown()
+
+ assert c.shutdown.call_count == 1
+ assert c._component_state == 'Stopped'
+ assert not c._component_timer.running
+ assert c.stop.call_count == 1
diff --git a/deluge/tests/test_config.py b/deluge/tests/test_config.py
index 0f6df3bb5..146a5c904 100644
--- a/deluge/tests/test_config.py
+++ b/deluge/tests/test_config.py
@@ -1,23 +1,20 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
+import json
+import logging
import os
from codecs import getwriter
+import pytest
from twisted.internet import task
-from twisted.trial import unittest
-import deluge.config
from deluge.common import JSON_FORMAT
from deluge.config import Config
-
-from .common import set_tmp_config_dir
+from deluge.ui.hostlist import mask_hosts_password
DEFAULTS = {
'string': 'foobar',
@@ -26,37 +23,42 @@ DEFAULTS = {
'bool': True,
'unicode': 'foobar',
'password': 'abc123*\\[!]?/<>#{@}=|"+$%(^)~',
+ 'hosts': [
+ ('host1', 'port', '', 'password1234'),
+ ('host2', 'port', '', 'password5678'),
+ ],
}
-class ConfigTestCase(unittest.TestCase):
- def setUp(self): # NOQA: N803
- self.config_dir = set_tmp_config_dir()
+LOGGER = logging.getLogger(__name__)
+
+class TestConfig:
def test_init(self):
config = Config('test.conf', defaults=DEFAULTS, config_dir=self.config_dir)
- self.assertEqual(DEFAULTS, config.config)
+ assert DEFAULTS == config.config
config = Config('test.conf', config_dir=self.config_dir)
- self.assertEqual({}, config.config)
+ assert {} == config.config
def test_set_get_item(self):
config = Config('test.conf', config_dir=self.config_dir)
config['foo'] = 1
- self.assertEqual(config['foo'], 1)
- self.assertRaises(ValueError, config.set_item, 'foo', 'bar')
+ assert config['foo'] == 1
+ with pytest.raises(ValueError):
+ config.set_item('foo', 'bar')
config['foo'] = 2
- self.assertEqual(config.get_item('foo'), 2)
+ assert config.get_item('foo') == 2
config['foo'] = '3'
- self.assertEqual(config.get_item('foo'), 3)
+ assert config.get_item('foo') == 3
config['unicode'] = 'ВИДЕОФИЛЬМЫ'
- self.assertEqual(config['unicode'], 'ВИДЕОФИЛЬМЫ')
+ assert config['unicode'] == 'ВИДЕОФИЛЬМЫ'
config['unicode'] = b'foostring'
- self.assertFalse(isinstance(config.get_item('unicode'), bytes))
+ assert not isinstance(config.get_item('unicode'), bytes)
config._save_timer.cancel()
@@ -64,43 +66,101 @@ class ConfigTestCase(unittest.TestCase):
config = Config('test.conf', config_dir=self.config_dir)
config['foo'] = None
- self.assertIsNone(config['foo'])
- self.assertIsInstance(config['foo'], type(None))
+ assert config['foo'] is None
+ assert isinstance(config['foo'], type(None))
config['foo'] = 1
- self.assertEqual(config.get('foo'), 1)
+ assert config.get('foo') == 1
config['foo'] = None
- self.assertIsNone(config['foo'])
+ assert config['foo'] is None
config['bar'] = None
- self.assertIsNone(config['bar'])
+ assert config['bar'] is None
config['bar'] = None
- self.assertIsNone(config['bar'])
+ assert config['bar'] is None
config._save_timer.cancel()
+ async def test_on_changed_callback(self, mock_callback):
+ config = Config('test.conf', config_dir=self.config_dir)
+ config.register_change_callback(mock_callback)
+ config['foo'] = 1
+ assert config['foo'] == 1
+ await mock_callback.deferred
+ mock_callback.assert_called_once_with('foo', 1)
+
+ async def test_key_function_callback(self, mock_callback):
+ config = Config(
+ 'test.conf', defaults={'foo': 1, 'bar': 1}, config_dir=self.config_dir
+ )
+
+ assert config['foo'] == 1
+ config.register_set_function('foo', mock_callback)
+ await mock_callback.deferred
+ mock_callback.assert_called_once_with('foo', 1)
+
+ mock_callback.reset_mock()
+ config.register_set_function('bar', mock_callback, apply_now=False)
+ mock_callback.assert_not_called()
+ config['bar'] = 2
+ await mock_callback.deferred
+ mock_callback.assert_called_once_with('bar', 2)
+
def test_get(self):
config = Config('test.conf', config_dir=self.config_dir)
config['foo'] = 1
- self.assertEqual(config.get('foo'), 1)
- self.assertEqual(config.get('foobar'), None)
- self.assertEqual(config.get('foobar', 2), 2)
+ assert config.get('foo') == 1
+ assert config.get('foobar') is None
+ assert config.get('foobar', 2) == 2
config['foobar'] = 5
- self.assertEqual(config.get('foobar', 2), 5)
+ assert config.get('foobar', 2) == 5
+
+ def test_set_log_mask_funcs(self, caplog):
+ """Test mask func masks key in log"""
+ caplog.set_level(logging.DEBUG)
+ config = Config(
+ 'test.conf',
+ config_dir=self.config_dir,
+ log_mask_funcs={'hosts': mask_hosts_password},
+ )
+ config['hosts'] = DEFAULTS['hosts']
+ assert isinstance(config['hosts'], list)
+ assert 'host1' in caplog.text
+ assert 'host2' in caplog.text
+ assert 'password1234' not in caplog.text
+ assert 'password5678' not in caplog.text
+ assert '*' * 10 in caplog.text
+
+ def test_load_log_mask_funcs(self, caplog):
+ """Test mask func masks key in log"""
+ with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
+ json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
+
+ config = Config(
+ 'test.conf',
+ config_dir=self.config_dir,
+ log_mask_funcs={'hosts': mask_hosts_password},
+ )
+ with caplog.at_level(logging.DEBUG):
+ config.load(os.path.join(self.config_dir, 'test.conf'))
+ assert 'host1' in caplog.text
+ assert 'host2' in caplog.text
+ assert 'foobar' in caplog.text
+ assert 'password1234' not in caplog.text
+ assert 'password5678' not in caplog.text
+ assert '*' * 10 in caplog.text
def test_load(self):
def check_config():
config = Config('test.conf', config_dir=self.config_dir)
- self.assertEqual(config['string'], 'foobar')
- self.assertEqual(config['float'], 0.435)
- self.assertEqual(config['password'], 'abc123*\\[!]?/<>#{@}=|"+$%(^)~')
+ assert config['string'] == 'foobar'
+ assert config['float'] == 0.435
+ assert config['password'] == 'abc123*\\[!]?/<>#{@}=|"+$%(^)~'
# Test opening a previous 1.2 config file of just a json object
- import json
-
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
@@ -128,38 +188,38 @@ class ConfigTestCase(unittest.TestCase):
# We do this twice because the first time we need to save the file to disk
# and the second time we do a compare and we should not write
ret = config.save()
- self.assertTrue(ret)
+ assert ret
ret = config.save()
- self.assertTrue(ret)
+ assert ret
config['string'] = 'baz'
config['int'] = 2
ret = config.save()
- self.assertTrue(ret)
+ assert ret
del config
config = Config('test.conf', defaults=DEFAULTS, config_dir=self.config_dir)
- self.assertEqual(config['string'], 'baz')
- self.assertEqual(config['int'], 2)
+ assert config['string'] == 'baz'
+ assert config['int'] == 2
def test_save_timer(self):
- self.clock = task.Clock()
- deluge.config.callLater = self.clock.callLater
+ clock = task.Clock()
config = Config('test.conf', defaults=DEFAULTS, config_dir=self.config_dir)
+ config.callLater = clock.callLater
config['string'] = 'baz'
config['int'] = 2
- self.assertTrue(config._save_timer.active())
+ assert config._save_timer.active()
# Timeout set for 5 seconds in config, so lets move clock by 5 seconds
- self.clock.advance(5)
+ clock.advance(5)
def check_config(config):
- self.assertTrue(not config._save_timer.active())
+ assert not config._save_timer.active()
del config
config = Config('test.conf', defaults=DEFAULTS, config_dir=self.config_dir)
- self.assertEqual(config['string'], 'baz')
- self.assertEqual(config['int'], 2)
+ assert config['string'] == 'baz'
+ assert config['int'] == 2
check_config(config)
@@ -176,7 +236,7 @@ class ConfigTestCase(unittest.TestCase):
from deluge.config import find_json_objects
objects = find_json_objects(s)
- self.assertEqual(len(objects), 2)
+ assert len(objects) == 2
def test_find_json_objects_curly_brace(self):
"""Test with string containing curly brace"""
@@ -193,7 +253,7 @@ class ConfigTestCase(unittest.TestCase):
from deluge.config import find_json_objects
objects = find_json_objects(s)
- self.assertEqual(len(objects), 2)
+ assert len(objects) == 2
def test_find_json_objects_double_quote(self):
"""Test with string containing double quote"""
@@ -211,4 +271,4 @@ class ConfigTestCase(unittest.TestCase):
from deluge.config import find_json_objects
objects = find_json_objects(s)
- self.assertEqual(len(objects), 2)
+ assert len(objects) == 2
diff --git a/deluge/tests/test_core.py b/deluge/tests/test_core.py
index 15fbc1bcf..28b590250 100644
--- a/deluge/tests/test_core.py
+++ b/deluge/tests/test_core.py
@@ -1,20 +1,17 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-
-from __future__ import unicode_literals
-
+import base64
+import os
from base64 import b64encode
from hashlib import sha1 as sha
import pytest
-from six import integer_types
+import pytest_twisted
from twisted.internet import defer, reactor, task
from twisted.internet.error import CannotListenError
-from twisted.python.failure import Failure
from twisted.web.http import FORBIDDEN
from twisted.web.resource import EncodingResourceWrapper, Resource
from twisted.web.server import GzipEncoderFactory, Site
@@ -24,12 +21,12 @@ import deluge.common
import deluge.component as component
import deluge.core.torrent
from deluge._libtorrent import lt
+from deluge.conftest import BaseTestCase
from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer
from deluge.error import AddTorrentError, InvalidTorrentError
from . import common
-from .basetest import BaseTestCase
common.disable_new_release_check()
@@ -80,14 +77,13 @@ class TopLevelResource(Resource):
)
-class CoreTestCase(BaseTestCase):
+class TestCore(BaseTestCase):
def set_up(self):
- common.set_tmp_config_dir()
self.rpcserver = RPCServer(listen=False)
- self.core = Core()
+ self.core: Core = Core()
self.core.config.config['lsd'] = False
self.clock = task.Clock()
- self.core.torrentmanager.callLater = self.clock.callLater
+ self.core.torrentmanager.clock = self.clock
self.listen_port = 51242
return component.start().addCallback(self.start_web_server)
@@ -120,9 +116,9 @@ class CoreTestCase(BaseTestCase):
self.patch(
deluge.core.torrentmanager,
'LT_DEFAULT_ADD_TORRENT_FLAGS',
- lt.add_torrent_params_flags_t.flag_auto_managed
- | lt.add_torrent_params_flags_t.flag_update_subscribe
- | lt.add_torrent_params_flags_t.flag_apply_ip_filter,
+ lt.torrent_flags.auto_managed
+ | lt.torrent_flags.update_subscribe
+ | lt.torrent_flags.apply_ip_filter,
)
options = {'add_paused': paused, 'auto_managed': False}
filepath = common.get_test_data_file(filename)
@@ -131,7 +127,7 @@ class CoreTestCase(BaseTestCase):
torrent_id = self.core.add_torrent_file(filename, filedump, options)
return torrent_id
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_add_torrent_files(self):
options = {}
filenames = ['test.torrent', 'test_torrent.file.torrent']
@@ -142,9 +138,9 @@ class CoreTestCase(BaseTestCase):
filedump = b64encode(_file.read())
files_to_add.append((filename, filedump, options))
errors = yield self.core.add_torrent_files(files_to_add)
- self.assertEqual(len(errors), 0)
+ assert len(errors) == 0
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_add_torrent_files_error_duplicate(self):
options = {}
filenames = ['test.torrent', 'test.torrent']
@@ -155,10 +151,10 @@ class CoreTestCase(BaseTestCase):
filedump = b64encode(_file.read())
files_to_add.append((filename, filedump, options))
errors = yield self.core.add_torrent_files(files_to_add)
- self.assertEqual(len(errors), 1)
- self.assertTrue(str(errors[0]).startswith('Torrent already in session'))
+ assert len(errors) == 1
+ assert str(errors[0]).startswith('Torrent already in session')
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_add_torrent_file(self):
options = {}
filename = common.get_test_data_file('test.torrent')
@@ -171,17 +167,16 @@ class CoreTestCase(BaseTestCase):
with open(filename, 'rb') as _file:
info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest()
- self.assertEqual(torrent_id, info_hash)
+ assert torrent_id == info_hash
def test_add_torrent_file_invalid_filedump(self):
options = {}
filename = common.get_test_data_file('test.torrent')
- self.assertRaises(
- AddTorrentError, self.core.add_torrent_file, filename, False, options
- )
+ with pytest.raises(AddTorrentError):
+ self.core.add_torrent_file(filename, False, options)
- @defer.inlineCallbacks
- def test_add_torrent_url(self):
+ @pytest_twisted.inlineCallbacks
+ def test_add_torrent_url(self, mock_mkstemp):
url = (
'http://localhost:%d/ubuntu-9.04-desktop-i386.iso.torrent'
% self.listen_port
@@ -190,78 +185,80 @@ class CoreTestCase(BaseTestCase):
info_hash = '60d5d82328b4547511fdeac9bf4d0112daa0ce00'
torrent_id = yield self.core.add_torrent_url(url, options)
- self.assertEqual(torrent_id, info_hash)
+ assert torrent_id == info_hash
+ assert not os.path.isfile(mock_mkstemp[1])
- def test_add_torrent_url_with_cookie(self):
+ async def test_add_torrent_url_with_cookie(self):
url = 'http://localhost:%d/cookie' % self.listen_port
options = {}
headers = {'Cookie': 'password=deluge'}
info_hash = '60d5d82328b4547511fdeac9bf4d0112daa0ce00'
- d = self.core.add_torrent_url(url, options)
- d.addCallbacks(self.fail, self.assertIsInstance, errbackArgs=(Failure,))
+ with pytest.raises(Exception):
+ await self.core.add_torrent_url(url, options)
- d = self.core.add_torrent_url(url, options, headers)
- d.addCallbacks(self.assertEqual, self.fail, callbackArgs=(info_hash,))
-
- return d
+ result = await self.core.add_torrent_url(url, options, headers)
+ assert result == info_hash
- def test_add_torrent_url_with_redirect(self):
+ async def test_add_torrent_url_with_redirect(self):
url = 'http://localhost:%d/redirect' % self.listen_port
options = {}
info_hash = '60d5d82328b4547511fdeac9bf4d0112daa0ce00'
- d = self.core.add_torrent_url(url, options)
- d.addCallback(self.assertEqual, info_hash)
- return d
+ result = await self.core.add_torrent_url(url, options)
+ assert result == info_hash
- def test_add_torrent_url_with_partial_download(self):
+ async def test_add_torrent_url_with_partial_download(self):
url = 'http://localhost:%d/partial' % self.listen_port
options = {}
info_hash = '60d5d82328b4547511fdeac9bf4d0112daa0ce00'
- d = self.core.add_torrent_url(url, options)
- d.addCallback(self.assertEqual, info_hash)
- return d
+ result = await self.core.add_torrent_url(url, options)
+ assert result == info_hash
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_add_torrent_magnet(self):
info_hash = '60d5d82328b4547511fdeac9bf4d0112daa0ce00'
- uri = deluge.common.create_magnet_uri(info_hash)
+ tracker = 'udp://tracker.example.com'
+ name = 'test magnet'
+ uri = deluge.common.create_magnet_uri(info_hash, name=name, trackers=[tracker])
options = {}
torrent_id = yield self.core.add_torrent_magnet(uri, options)
- self.assertEqual(torrent_id, info_hash)
+ assert torrent_id == info_hash
+ torrent_status = self.core.get_torrent_status(torrent_id, ['name', 'trackers'])
+ assert torrent_status['trackers'][0]['url'] == tracker
+ assert torrent_status['name'] == name
def test_resume_torrent(self):
tid1 = self.add_torrent('test.torrent', paused=True)
tid2 = self.add_torrent('test_torrent.file.torrent', paused=True)
# Assert paused
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertTrue(r1['paused'])
+ assert r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertTrue(r2['paused'])
+ assert r2['paused']
self.core.resume_torrent(tid2)
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertTrue(r1['paused'])
+ assert r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertFalse(r2['paused'])
+ assert not r2['paused']
def test_resume_torrent_list(self):
"""Backward compatibility for list of torrent_ids."""
torrent_id = self.add_torrent('test.torrent', paused=True)
self.core.resume_torrent([torrent_id])
result = self.core.get_torrent_status(torrent_id, ['paused'])
- self.assertFalse(result['paused'])
+ assert not result['paused']
def test_resume_torrents(self):
tid1 = self.add_torrent('test.torrent', paused=True)
tid2 = self.add_torrent('test_torrent.file.torrent', paused=True)
self.core.resume_torrents([tid1, tid2])
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertFalse(r1['paused'])
+ assert not r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertFalse(r2['paused'])
+ assert not r2['paused']
def test_resume_torrents_all(self):
"""With no torrent_ids param, resume all torrents"""
@@ -269,33 +266,33 @@ class CoreTestCase(BaseTestCase):
tid2 = self.add_torrent('test_torrent.file.torrent', paused=True)
self.core.resume_torrents()
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertFalse(r1['paused'])
+ assert not r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertFalse(r2['paused'])
+ assert not r2['paused']
def test_pause_torrent(self):
tid1 = self.add_torrent('test.torrent')
tid2 = self.add_torrent('test_torrent.file.torrent')
# Assert not paused
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertFalse(r1['paused'])
+ assert not r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertFalse(r2['paused'])
+ assert not r2['paused']
self.core.pause_torrent(tid2)
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertFalse(r1['paused'])
+ assert not r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertTrue(r2['paused'])
+ assert r2['paused']
def test_pause_torrent_list(self):
"""Backward compatibility for list of torrent_ids."""
torrent_id = self.add_torrent('test.torrent')
result = self.core.get_torrent_status(torrent_id, ['paused'])
- self.assertFalse(result['paused'])
+ assert not result['paused']
self.core.pause_torrent([torrent_id])
result = self.core.get_torrent_status(torrent_id, ['paused'])
- self.assertTrue(result['paused'])
+ assert result['paused']
def test_pause_torrents(self):
tid1 = self.add_torrent('test.torrent')
@@ -303,9 +300,9 @@ class CoreTestCase(BaseTestCase):
self.core.pause_torrents([tid1, tid2])
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertTrue(r1['paused'])
+ assert r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertTrue(r2['paused'])
+ assert r2['paused']
def test_pause_torrents_all(self):
"""With no torrent_ids param, pause all torrents"""
@@ -314,26 +311,24 @@ class CoreTestCase(BaseTestCase):
self.core.pause_torrents()
r1 = self.core.get_torrent_status(tid1, ['paused'])
- self.assertTrue(r1['paused'])
+ assert r1['paused']
r2 = self.core.get_torrent_status(tid2, ['paused'])
- self.assertTrue(r2['paused'])
+ assert r2['paused']
+ @pytest_twisted.inlineCallbacks
def test_prefetch_metadata_existing(self):
"""Check another call with same magnet returns existing deferred."""
magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173'
- expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', None)
+ expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', b'')
- def on_result(result):
- self.assertEqual(result, expected)
-
- d = self.core.prefetch_magnet_metadata(magnet)
- d.addCallback(on_result)
+ d1 = self.core.prefetch_magnet_metadata(magnet)
d2 = self.core.prefetch_magnet_metadata(magnet)
- d2.addCallback(on_result)
+ dg = defer.gatherResults([d1, d2], consumeErrors=True)
self.clock.advance(30)
- return defer.DeferredList([d, d2])
+ result = yield dg
+ assert result == [expected] * 2
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_remove_torrent(self):
options = {}
filename = common.get_test_data_file('test.torrent')
@@ -341,18 +336,17 @@ class CoreTestCase(BaseTestCase):
filedump = b64encode(_file.read())
torrent_id = yield self.core.add_torrent_file_async(filename, filedump, options)
removed = self.core.remove_torrent(torrent_id, True)
- self.assertTrue(removed)
- self.assertEqual(len(self.core.get_session_state()), 0)
+ assert removed
+ assert len(self.core.get_session_state()) == 0
def test_remove_torrent_invalid(self):
- self.assertRaises(
- InvalidTorrentError,
- self.core.remove_torrent,
- 'torrentidthatdoesntexist',
- True,
- )
+ with pytest.raises(InvalidTorrentError):
+ self.core.remove_torrent(
+ 'torrentidthatdoesntexist',
+ True,
+ )
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_remove_torrents(self):
options = {}
filename = common.get_test_data_file('test.torrent')
@@ -369,17 +363,17 @@ class CoreTestCase(BaseTestCase):
d = self.core.remove_torrents([torrent_id, torrent_id2], True)
def test_ret(val):
- self.assertTrue(val == [])
+ assert val == []
d.addCallback(test_ret)
def test_session_state(val):
- self.assertEqual(len(self.core.get_session_state()), 0)
+ assert len(self.core.get_session_state()) == 0
d.addCallback(test_session_state)
yield d
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_remove_torrents_invalid(self):
options = {}
filename = common.get_test_data_file('test.torrent')
@@ -391,58 +385,53 @@ class CoreTestCase(BaseTestCase):
val = yield self.core.remove_torrents(
['invalidid1', 'invalidid2', torrent_id], False
)
- self.assertEqual(len(val), 2)
- self.assertEqual(
- val[0], ('invalidid1', 'torrent_id invalidid1 not in session.')
- )
- self.assertEqual(
- val[1], ('invalidid2', 'torrent_id invalidid2 not in session.')
- )
+ assert len(val) == 2
+ assert val[0] == ('invalidid1', 'torrent_id invalidid1 not in session.')
+ assert val[1] == ('invalidid2', 'torrent_id invalidid2 not in session.')
def test_get_session_status(self):
status = self.core.get_session_status(
['net.recv_tracker_bytes', 'net.sent_tracker_bytes']
)
- self.assertIsInstance(status, dict)
- self.assertEqual(status['net.recv_tracker_bytes'], 0)
- self.assertEqual(status['net.sent_tracker_bytes'], 0)
+ assert isinstance(status, dict)
+ assert status['net.recv_tracker_bytes'] == 0
+ assert status['net.sent_tracker_bytes'] == 0
def test_get_session_status_all(self):
status = self.core.get_session_status([])
- self.assertIsInstance(status, dict)
- self.assertIn('upload_rate', status)
- self.assertIn('net.recv_bytes', status)
+ assert isinstance(status, dict)
+ assert 'upload_rate' in status
+ assert 'net.recv_bytes' in status
def test_get_session_status_depr(self):
status = self.core.get_session_status(['num_peers', 'num_unchoked'])
- self.assertIsInstance(status, dict)
- self.assertEqual(status['num_peers'], 0)
- self.assertEqual(status['num_unchoked'], 0)
+ assert isinstance(status, dict)
+ assert status['num_peers'] == 0
+ assert status['num_unchoked'] == 0
def test_get_session_status_rates(self):
status = self.core.get_session_status(['upload_rate', 'download_rate'])
- self.assertIsInstance(status, dict)
- self.assertEqual(status['upload_rate'], 0)
+ assert isinstance(status, dict)
+ assert status['upload_rate'] == 0
def test_get_session_status_ratio(self):
status = self.core.get_session_status(['write_hit_ratio', 'read_hit_ratio'])
- self.assertIsInstance(status, dict)
- self.assertEqual(status['write_hit_ratio'], 0.0)
- self.assertEqual(status['read_hit_ratio'], 0.0)
+ assert isinstance(status, dict)
+ assert status['write_hit_ratio'] == 0.0
+ assert status['read_hit_ratio'] == 0.0
def test_get_free_space(self):
space = self.core.get_free_space('.')
- # get_free_space returns long on Python 2 (32-bit).
- self.assertTrue(isinstance(space, integer_types))
- self.assertTrue(space >= 0)
- self.assertEqual(self.core.get_free_space('/someinvalidpath'), -1)
+ assert isinstance(space, int)
+ assert space >= 0
+ assert self.core.get_free_space('/someinvalidpath') == -1
@pytest.mark.slow
def test_test_listen_port(self):
d = self.core.test_listen_port()
def result(r):
- self.assertTrue(r in (True, False))
+ assert r in (True, False)
d.addCallback(result)
return d
@@ -460,24 +449,22 @@ class CoreTestCase(BaseTestCase):
}
for key in pathlist:
- self.assertEqual(
- deluge.core.torrent.sanitize_filepath(key, folder=False), pathlist[key]
+ assert (
+ deluge.core.torrent.sanitize_filepath(key, folder=False)
+ == pathlist[key]
)
- self.assertEqual(
- deluge.core.torrent.sanitize_filepath(key, folder=True),
- pathlist[key] + '/',
+
+ assert (
+ deluge.core.torrent.sanitize_filepath(key, folder=True)
+ == pathlist[key] + '/'
)
def test_get_set_config_values(self):
- self.assertEqual(
- self.core.get_config_values(['abc', 'foo']), {'foo': None, 'abc': None}
- )
- self.assertEqual(self.core.get_config_value('foobar'), None)
+ assert self.core.get_config_values(['abc', 'foo']) == {'foo': None, 'abc': None}
+ assert self.core.get_config_value('foobar') is None
self.core.set_config({'abc': 'def', 'foo': 10, 'foobar': 'barfoo'})
- self.assertEqual(
- self.core.get_config_values(['foo', 'abc']), {'foo': 10, 'abc': 'def'}
- )
- self.assertEqual(self.core.get_config_value('foobar'), 'barfoo')
+ assert self.core.get_config_values(['foo', 'abc']) == {'foo': 10, 'abc': 'def'}
+ assert self.core.get_config_value('foobar') == 'barfoo'
def test_read_only_config_keys(self):
key = 'max_upload_speed'
@@ -486,13 +473,39 @@ class CoreTestCase(BaseTestCase):
old_value = self.core.get_config_value(key)
self.core.set_config({key: old_value + 10})
new_value = self.core.get_config_value(key)
- self.assertEqual(old_value, new_value)
+ assert old_value == new_value
self.core.read_only_config_keys = None
def test__create_peer_id(self):
- self.assertEqual(self.core._create_peer_id('2.0.0'), '-DE200s-')
- self.assertEqual(self.core._create_peer_id('2.0.0.dev15'), '-DE200D-')
- self.assertEqual(self.core._create_peer_id('2.0.1rc1'), '-DE201r-')
- self.assertEqual(self.core._create_peer_id('2.11.0b2'), '-DE2B0b-')
- self.assertEqual(self.core._create_peer_id('2.4.12b2.dev3'), '-DE24CD-')
+ assert self.core._create_peer_id('2.0.0') == '-DE200s-'
+ assert self.core._create_peer_id('2.0.0.dev15') == '-DE200D-'
+ assert self.core._create_peer_id('2.0.1rc1') == '-DE201r-'
+ assert self.core._create_peer_id('2.11.0b2') == '-DE2B0b-'
+ assert self.core._create_peer_id('2.4.12b2.dev3') == '-DE24CD-'
+
+ @pytest.mark.parametrize(
+ 'path',
+ [
+ common.get_test_data_file('deluge.png'),
+ os.path.dirname(common.get_test_data_file('deluge.png')),
+ ],
+ )
+ @pytest.mark.parametrize('piece_length', [2**14, 2**16])
+ @pytest_twisted.inlineCallbacks
+ def test_create_torrent(self, path, tmp_path, piece_length):
+ target = tmp_path / 'test.torrent'
+
+ filename, filedump = yield self.core.create_torrent(
+ path=path,
+ tracker=None,
+ piece_length=piece_length,
+ target=target,
+ add_to_session=False,
+ )
+ filecontent = base64.b64decode(filedump)
+
+ with open(target, 'rb') as f:
+ assert f.read() == filecontent
+
+ lt.torrent_info(filecontent)
diff --git a/deluge/tests/test_decorators.py b/deluge/tests/test_decorators.py
index 7d4bd98c8..d2ecd1a2b 100644
--- a/deluge/tests/test_decorators.py
+++ b/deluge/tests/test_decorators.py
@@ -1,18 +1,14 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
-from twisted.trial import unittest
from deluge.decorators import proxy
-class DecoratorsTestCase(unittest.TestCase):
+class TestDecorators:
def test_proxy_with_simple_functions(self):
def negate(func, *args, **kwargs):
return not func(*args, **kwargs)
@@ -26,16 +22,16 @@ class DecoratorsTestCase(unittest.TestCase):
def double_nothing(_bool):
return _bool
- self.assertTrue(something(False))
- self.assertFalse(something(True))
- self.assertTrue(double_nothing(True))
- self.assertFalse(double_nothing(False))
+ assert something(False)
+ assert not something(True)
+ assert double_nothing(True)
+ assert not double_nothing(False)
def test_proxy_with_class_method(self):
def negate(func, *args, **kwargs):
return -func(*args, **kwargs)
- class Test(object):
+ class Test:
def __init__(self, number):
self.number = number
@@ -48,5 +44,5 @@ class DecoratorsTestCase(unittest.TestCase):
return self.diff(number)
t = Test(5)
- self.assertEqual(t.diff(1), -4)
- self.assertEqual(t.no_diff(1), 4)
+ assert t.diff(1) == -4
+ assert t.no_diff(1) == 4
diff --git a/deluge/tests/test_error.py b/deluge/tests/test_error.py
index c552e9422..a87d6a2d8 100644
--- a/deluge/tests/test_error.py
+++ b/deluge/tests/test_error.py
@@ -1,54 +1,39 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
-from twisted.trial import unittest
-
import deluge.error
-class ErrorTestCase(unittest.TestCase):
- def setUp(self): # NOQA: N803
- pass
-
- def tearDown(self): # NOQA: N803
- pass
-
+class TestError:
def test_deluge_error(self):
msg = 'Some message'
e = deluge.error.DelugeError(msg)
- self.assertEqual(str(e), msg)
+ assert str(e) == msg
from twisted.internet.defer import DebugInfo
del DebugInfo.__del__ # Hides all errors
- self.assertEqual(e._args, (msg,))
- self.assertEqual(e._kwargs, {})
+ assert e._args == (msg,)
+ assert e._kwargs == {}
def test_incompatible_client(self):
version = '1.3.6'
e = deluge.error.IncompatibleClient(version)
- self.assertEqual(
- str(e),
- 'Your deluge client is not compatible with the daemon. \
-Please upgrade your client to %s'
- % version,
+ assert (
+ str(e) == 'Your deluge client is not compatible with the daemon. '
+ 'Please upgrade your client to %s' % version
)
def test_not_authorized_error(self):
current_level = 5
required_level = 10
e = deluge.error.NotAuthorizedError(current_level, required_level)
- self.assertEqual(
- str(e), 'Auth level too low: %d < %d' % (current_level, required_level)
- )
+ assert str(e) == 'Auth level too low: %d < %d' % (current_level, required_level)
def test_bad_login_error(self):
message = 'Login failed'
username = 'deluge'
e = deluge.error.BadLoginError(message, username)
- self.assertEqual(str(e), message)
+ assert str(e) == message
diff --git a/deluge/tests/test_files_tab.py b/deluge/tests/test_files_tab.py
index 1ec8e18de..1e97cbbc3 100644
--- a/deluge/tests/test_files_tab.py
+++ b/deluge/tests/test_files_tab.py
@@ -1,23 +1,16 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# 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 pytest
-from twisted.trial import unittest
import deluge.component as component
-from deluge.common import windows_check
from deluge.configmanager import ConfigManager
+from deluge.conftest import BaseTestCase
from deluge.i18n import setup_translation
-from . import common
-from .basetest import BaseTestCase
-
libs_available = True
# Allow running other tests without GTKUI dependencies available
try:
@@ -32,12 +25,11 @@ setup_translation()
@pytest.mark.gtkui
-class FilesTabTestCase(BaseTestCase):
+class TestFilesTab(BaseTestCase):
def set_up(self):
if libs_available is False:
- raise unittest.SkipTest('GTKUI dependencies not available')
+ pytest.skip('GTKUI dependencies not available')
- common.set_tmp_config_dir()
ConfigManager('gtk3ui.conf', defaults=DEFAULT_PREFS)
self.mainwindow = MainWindow()
self.filestab = FilesTab()
@@ -52,8 +44,8 @@ class FilesTabTestCase(BaseTestCase):
root = treestore.get_iter_first()
level = 1
- def p_level(s, l):
- print('%s%s' % (' ' * l, s))
+ def p_level(s, lvl):
+ print('{}{}'.format(' ' * lvl, s))
def _print_treestore_children(i, lvl):
while i:
@@ -98,80 +90,74 @@ class FilesTabTestCase(BaseTestCase):
)
if not ret:
self.print_treestore('Treestore not expected:', self.filestab.treestore)
- self.assertTrue(ret)
+ assert ret
def test_files_tab2(self):
- if windows_check():
- raise unittest.SkipTest('on windows \\ != / for path names')
self.filestab.files_list[self.t_id] = (
- {'index': 0, 'path': '1/1/test_10.txt', 'offset': 0, 'size': 13},
- {'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
+ {'index': 0, 'path': '1/1/test_100.txt', 'offset': 0, 'size': 13},
+ {'index': 1, 'path': 'test_101.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files()
self.filestab._on_torrentfilerenamed_event(
- self.t_id, self.index, '1/1/test_100.txt'
+ self.t_id, self.index, '1/1/test_101.txt'
)
ret = self.verify_treestore(
self.filestab.treestore,
- [['1/', [['1/', [['test_100.txt'], ['test_10.txt']]]]]],
+ [['1/', [['1/', [['test_100.txt'], ['test_101.txt']]]]]],
)
if not ret:
self.print_treestore('Treestore not expected:', self.filestab.treestore)
- self.assertTrue(ret)
+ assert ret
def test_files_tab3(self):
- if windows_check():
- raise unittest.SkipTest('on windows \\ != / for path names')
self.filestab.files_list[self.t_id] = (
- {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
- {'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
+ {'index': 0, 'path': '1/test_100.txt', 'offset': 0, 'size': 13},
+ {'index': 1, 'path': 'test_101.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files()
self.filestab._on_torrentfilerenamed_event(
- self.t_id, self.index, '1/test_100.txt'
+ self.t_id, self.index, '1/test_101.txt'
)
ret = self.verify_treestore(
- self.filestab.treestore, [['1/', [['test_100.txt'], ['test_10.txt']]]]
+ self.filestab.treestore, [['1/', [['test_100.txt'], ['test_101.txt']]]]
)
if not ret:
self.print_treestore('Treestore not expected:', self.filestab.treestore)
- self.assertTrue(ret)
+ assert ret
def test_files_tab4(self):
self.filestab.files_list[self.t_id] = (
- {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
- {'index': 1, 'path': '1/test_100.txt', 'offset': 13, 'size': 14},
+ {'index': 0, 'path': '1/test_100.txt', 'offset': 0, 'size': 13},
+ {'index': 1, 'path': '1/test_101.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files()
self.filestab._on_torrentfilerenamed_event(
- self.t_id, self.index, '1/2/test_100.txt'
+ self.t_id, self.index, '1/2/test_101.txt'
)
ret = self.verify_treestore(
self.filestab.treestore,
- [['1/', [['2/', [['test_100.txt']]], ['test_10.txt']]]],
+ [['1/', [['2/', [['test_101.txt']]], ['test_100.txt']]]],
)
if not ret:
self.print_treestore('Treestore not expected:', self.filestab.treestore)
- self.assertTrue(ret)
+ assert ret
def test_files_tab5(self):
- if windows_check():
- raise unittest.SkipTest('on windows \\ != / for path names')
self.filestab.files_list[self.t_id] = (
- {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
- {'index': 1, 'path': '2/test_100.txt', 'offset': 13, 'size': 14},
+ {'index': 0, 'path': '1/test_100.txt', 'offset': 0, 'size': 13},
+ {'index': 1, 'path': '2/test_101.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files()
self.filestab._on_torrentfilerenamed_event(
- self.t_id, self.index, '1/test_100.txt'
+ self.t_id, self.index, '1/test_101.txt'
)
ret = self.verify_treestore(
- self.filestab.treestore, [['1/', [['test_100.txt'], ['test_10.txt']]]]
+ self.filestab.treestore, [['1/', [['test_100.txt'], ['test_101.txt']]]]
)
if not ret:
self.print_treestore('Treestore not expected:', self.filestab.treestore)
- self.assertTrue(ret)
+ assert ret
diff --git a/deluge/tests/test_httpdownloader.py b/deluge/tests/test_httpdownloader.py
index ad947a422..1c2704560 100644
--- a/deluge/tests/test_httpdownloader.py
+++ b/deluge/tests/test_httpdownloader.py
@@ -1,22 +1,18 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
import tempfile
from email.utils import formatdate
-from io import open
+import pytest
+import pytest_twisted
from twisted.internet import reactor
from twisted.internet.error import CannotListenError
-from twisted.python.failure import Failure
-from twisted.trial import unittest
-from twisted.web.error import PageRedirect
+from twisted.web.error import Error, PageRedirect
from twisted.web.http import NOT_MODIFIED
from twisted.web.resource import EncodingResourceWrapper, Resource
from twisted.web.server import GzipEncoderFactory, Site
@@ -71,7 +67,7 @@ class TorrentResource(Resource):
content_type += b'; charset=' + charset
request.setHeader(b'Content-Type', content_type)
request.setHeader(b'Content-Disposition', b'attachment; filename=test.torrent')
- return 'Binary attachment ignore charset 世丕且\n'.encode('utf8')
+ return 'Binary attachment ignore charset 世丕且\n'.encode()
class CookieResource(Resource):
@@ -138,11 +134,13 @@ class TopLevelResource(Resource):
return b'<h1>Deluge HTTP Downloader tests webserver here</h1>'
-class DownloadFileTestCase(unittest.TestCase):
+class TestDownloadFile:
def get_url(self, path=''):
return 'http://localhost:%d/%s' % (self.listen_port, path)
- def setUp(self): # NOQA
+ @pytest_twisted.async_yield_fixture(autouse=True)
+ async def setUp(self, request): # NOQA
+ self = request.instance
setup_logger('warning', fname('log_file'))
self.website = Site(TopLevelResource())
self.listen_port = 51242
@@ -158,140 +156,120 @@ class DownloadFileTestCase(unittest.TestCase):
else:
raise error
- def tearDown(self): # NOQA
- return self.webserver.stopListening()
+ yield
+
+ await self.webserver.stopListening()
- def assertContains(self, filename, contents): # NOQA
- with open(filename, 'r', encoding='utf8') as _file:
+ def assert_contains(self, filename, contents):
+ with open(filename, encoding='utf8') as _file:
try:
- self.assertEqual(_file.read(), contents)
+ assert _file.read() == contents
except Exception as ex:
- self.fail(ex)
+ pytest.fail(ex)
return filename
- def assertNotContains(self, filename, contents, file_mode=''): # NOQA
- with open(filename, 'r', encoding='utf8') as _file:
+ def assert_not_contains(self, filename, contents, file_mode=''):
+ with open(filename, encoding='utf8') as _file:
try:
- self.assertNotEqual(_file.read(), contents)
+ assert _file.read() != contents
except Exception as ex:
- self.fail(ex)
+ pytest.fail(ex)
return filename
- def test_download(self):
- d = download_file(self.get_url(), fname('index.html'))
- d.addCallback(self.assertEqual, fname('index.html'))
- return d
+ async def test_download(self):
+ filename = await download_file(self.get_url(), fname('index.html'))
+ assert filename == fname('index.html')
- def test_download_without_required_cookies(self):
+ async def test_download_without_required_cookies(self):
url = self.get_url('cookie')
- d = download_file(url, fname('none'))
- d.addCallback(self.fail)
- d.addErrback(self.assertIsInstance, Failure)
- return d
+ filename = await download_file(url, fname('none'))
+ self.assert_contains(filename, 'Password cookie not set!')
- def test_download_with_required_cookies(self):
+ async def test_download_with_required_cookies(self):
url = self.get_url('cookie')
cookie = {'cookie': 'password=deluge'}
- d = download_file(url, fname('monster'), headers=cookie)
- d.addCallback(self.assertEqual, fname('monster'))
- d.addCallback(self.assertContains, 'COOKIE MONSTER!')
- return d
+ filename = await download_file(url, fname('monster'), headers=cookie)
+ assert filename == fname('monster')
+ self.assert_contains(filename, 'COOKIE MONSTER!')
- def test_download_with_rename(self):
+ async def test_download_with_rename(self):
url = self.get_url('rename?filename=renamed')
- d = download_file(url, fname('original'))
- d.addCallback(self.assertEqual, fname('renamed'))
- d.addCallback(self.assertContains, 'This file should be called renamed')
- return d
+ filename = await download_file(url, fname('original'))
+ assert filename == fname('renamed')
+ self.assert_contains(filename, 'This file should be called renamed')
- def test_download_with_rename_exists(self):
+ async def test_download_with_rename_exists(self):
open(fname('renamed'), 'w').close()
url = self.get_url('rename?filename=renamed')
- d = download_file(url, fname('original'))
- d.addCallback(self.assertEqual, fname('renamed-1'))
- d.addCallback(self.assertContains, 'This file should be called renamed')
- return d
+ filename = await download_file(url, fname('original'))
+ assert filename == fname('renamed-1')
+ self.assert_contains(filename, 'This file should be called renamed')
- def test_download_with_rename_sanitised(self):
+ async def test_download_with_rename_sanitised(self):
url = self.get_url('rename?filename=/etc/passwd')
- d = download_file(url, fname('original'))
- d.addCallback(self.assertEqual, fname('passwd'))
- d.addCallback(self.assertContains, 'This file should be called /etc/passwd')
- return d
+ filename = await download_file(url, fname('original'))
+ assert filename == fname('passwd')
+ self.assert_contains(filename, 'This file should be called /etc/passwd')
- def test_download_with_attachment_no_filename(self):
+ async def test_download_with_attachment_no_filename(self):
url = self.get_url('attachment')
- d = download_file(url, fname('original'))
- d.addCallback(self.assertEqual, fname('original'))
- d.addCallback(self.assertContains, 'Attachment with no filename set')
- return d
+ filename = await download_file(url, fname('original'))
+ assert filename == fname('original')
+ self.assert_contains(filename, 'Attachment with no filename set')
- def test_download_with_rename_prevented(self):
+ async def test_download_with_rename_prevented(self):
url = self.get_url('rename?filename=spam')
- d = download_file(url, fname('forced'), force_filename=True)
- d.addCallback(self.assertEqual, fname('forced'))
- d.addCallback(self.assertContains, 'This file should be called spam')
- return d
+ filename = await download_file(url, fname('forced'), force_filename=True)
+ assert filename == fname('forced')
+ self.assert_contains(filename, 'This file should be called spam')
- def test_download_with_gzip_encoding(self):
+ async def test_download_with_gzip_encoding(self):
url = self.get_url('gzip?msg=success')
- d = download_file(url, fname('gzip_encoded'))
- d.addCallback(self.assertContains, 'success')
- return d
+ filename = await download_file(url, fname('gzip_encoded'))
+ self.assert_contains(filename, 'success')
- def test_download_with_gzip_encoding_disabled(self):
+ async def test_download_with_gzip_encoding_disabled(self):
url = self.get_url('gzip?msg=unzip')
- d = download_file(url, fname('gzip_encoded'), allow_compression=False)
- d.addCallback(self.assertContains, 'unzip')
- return d
+ filename = await download_file(
+ url, fname('gzip_encoded'), allow_compression=False
+ )
+ self.assert_contains(filename, 'unzip')
- def test_page_redirect_unhandled(self):
+ async def test_page_redirect_unhandled(self):
url = self.get_url('redirect')
- d = download_file(url, fname('none'))
- d.addCallback(self.fail)
+ with pytest.raises(PageRedirect):
+ await download_file(url, fname('none'), handle_redirects=False)
- def on_redirect(failure):
- self.assertTrue(type(failure), PageRedirect)
+ async def test_page_redirect(self):
+ url = self.get_url('redirect')
+ filename = await download_file(url, fname('none'), handle_redirects=True)
+ assert filename == fname('none')
- d.addErrback(on_redirect)
- return d
+ async def test_page_not_found(self):
+ with pytest.raises(Error):
+ await download_file(self.get_url('page/not/found'), fname('none'))
- def test_page_redirect(self):
- url = self.get_url('redirect')
- d = download_file(url, fname('none'), handle_redirects=True)
- d.addCallback(self.assertEqual, fname('none'))
- d.addErrback(self.fail)
- return d
-
- def test_page_not_found(self):
- d = download_file(self.get_url('page/not/found'), fname('none'))
- d.addCallback(self.fail)
- d.addErrback(self.assertIsInstance, Failure)
- return d
-
- def test_page_not_modified(self):
+ @pytest.mark.xfail(reason="Doesn't seem like httpdownloader ever implemented this.")
+ async def test_page_not_modified(self):
headers = {'If-Modified-Since': formatdate(usegmt=True)}
- d = download_file(self.get_url(), fname('index.html'), headers=headers)
- d.addCallback(self.fail)
- d.addErrback(self.assertIsInstance, Failure)
- return d
+ with pytest.raises(Error) as exc_info:
+ await download_file(self.get_url(), fname('index.html'), headers=headers)
+ assert exc_info.value.status == NOT_MODIFIED
- def test_download_text_reencode_charset(self):
+ async def test_download_text_reencode_charset(self):
"""Re-encode as UTF-8 specified charset for text content-type header"""
url = self.get_url('attachment')
filepath = fname('test.txt')
headers = {'content-charset': 'Windows-1251', 'content-append': 'бвгде'}
- d = download_file(url, filepath, headers=headers)
- d.addCallback(self.assertEqual, filepath)
- d.addCallback(self.assertContains, 'Attachment with no filename setбвгде')
- return d
+ filename = await download_file(url, filepath, headers=headers)
+ assert filename == filepath
+ self.assert_contains(filename, 'Attachment with no filename setбвгде')
- def test_download_binary_ignore_charset(self):
+ async def test_download_binary_ignore_charset(self):
"""Ignore charset for binary content-type header e.g. torrent files"""
url = self.get_url('torrent')
headers = {'content-charset': 'Windows-1251'}
filepath = fname('test.torrent')
- d = download_file(url, fname('test.torrent'), headers=headers)
- d.addCallback(self.assertEqual, filepath)
- d.addCallback(self.assertContains, 'Binary attachment ignore charset 世丕且\n')
- return d
+ filename = await download_file(url, fname('test.torrent'), headers=headers)
+ assert filename == filepath
+ self.assert_contains(filename, 'Binary attachment ignore charset 世丕且\n')
diff --git a/deluge/tests/test_json_api.py b/deluge/tests/test_json_api.py
index 1da64bf97..ef21e9410 100644
--- a/deluge/tests/test_json_api.py
+++ b/deluge/tests/test_json_api.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,64 +6,35 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import json as json_lib
+from unittest.mock import MagicMock
-from mock import MagicMock
-from twisted.internet import defer
+import pytest
+import pytest_twisted
+from twisted.internet.defer import Deferred
from twisted.web import server
from twisted.web.http import Request
import deluge.common
-import deluge.component as component
import deluge.ui.web.auth
import deluge.ui.web.json_api
from deluge.error import DelugeError
-from deluge.ui.client import client
from deluge.ui.web.auth import Auth
from deluge.ui.web.json_api import JSON, JSONException
from . import common
-from .basetest import BaseTestCase
from .common_web import WebServerMockBase
-from .daemon_base import DaemonBase
common.disable_new_release_check()
-class JSONBase(BaseTestCase, DaemonBase):
- def connect_client(self, *args, **kwargs):
- return client.connect(
- 'localhost',
- self.listen_port,
- username=kwargs.get('user', ''),
- password=kwargs.get('password', ''),
- )
-
- def disconnect_client(self, *args):
- return client.disconnect()
-
- def tear_down(self):
- d = component.shutdown()
- d.addCallback(self.disconnect_client)
- d.addCallback(self.terminate_core)
- return d
-
-
-class JSONTestCase(JSONBase):
- def set_up(self):
- d = self.common_set_up()
- d.addCallback(self.start_core)
- d.addCallbacks(self.connect_client, self.terminate_core)
- return d
-
- @defer.inlineCallbacks
- def test_get_remote_methods(self):
+@pytest.mark.usefixtures('daemon', 'client', 'component')
+class TestJSON:
+ async def test_get_remote_methods(self):
json = JSON()
- methods = yield json.get_remote_methods()
- self.assertEqual(type(methods), tuple)
- self.assertTrue(len(methods) > 0)
+ methods = await json.get_remote_methods()
+ assert type(methods) == tuple
+ assert len(methods) > 0
def test_render_fail_disconnected(self):
json = JSON()
@@ -72,7 +42,7 @@ class JSONTestCase(JSONBase):
request.method = b'POST'
request._disconnected = True
# When disconnected, returns empty string
- self.assertEqual(json.render(request), '')
+ assert json.render(request) == ''
def test_render_fail(self):
json = JSON()
@@ -82,19 +52,17 @@ class JSONTestCase(JSONBase):
def write(response_str):
request.write_was_called = True
response = json_lib.loads(response_str.decode())
- self.assertEqual(response['result'], None)
- self.assertEqual(response['id'], None)
- self.assertEqual(
- response['error']['message'], 'JSONException: JSON not decodable'
- )
- self.assertEqual(response['error']['code'], 5)
+ assert response['result'] is None
+ assert response['id'] is None
+ assert response['error']['message'] == 'JSONException: JSON not decodable'
+ assert response['error']['code'] == 5
request.write = write
request.write_was_called = False
request._disconnected = False
request.getHeader.return_value = b'application/json'
- self.assertEqual(json.render(request), server.NOT_DONE_YET)
- self.assertTrue(request.write_was_called)
+ assert json.render(request) == server.NOT_DONE_YET
+ assert request.write_was_called
def test_handle_request_invalid_method(self):
json = JSON()
@@ -102,20 +70,23 @@ class JSONTestCase(JSONBase):
json_data = {'method': 'no-existing-module.test', 'id': 0, 'params': []}
request.json = json_lib.dumps(json_data).encode()
request_id, result, error = json._handle_request(request)
- self.assertEqual(error, {'message': 'Unknown method', 'code': 2})
+ assert error == {'message': 'Unknown method', 'code': 2}
def test_handle_request_invalid_json_request(self):
json = JSON()
request = MagicMock()
json_data = {'id': 0, 'params': []}
request.json = json_lib.dumps(json_data).encode()
- self.assertRaises(JSONException, json._handle_request, request)
+ with pytest.raises(JSONException):
+ json._handle_request(request)
json_data = {'method': 'some.method', 'params': []}
request.json = json_lib.dumps(json_data).encode()
- self.assertRaises(JSONException, json._handle_request, request)
+ with pytest.raises(JSONException):
+ json._handle_request(request)
json_data = {'method': 'some.method', 'id': 0}
request.json = json_lib.dumps(json_data).encode()
- self.assertRaises(JSONException, json._handle_request, request)
+ with pytest.raises(JSONException):
+ json._handle_request(request)
def test_on_json_request_invalid_content_type(self):
"""Test for exception with content type not application/json"""
@@ -124,18 +95,32 @@ class JSONTestCase(JSONBase):
request.getHeader.return_value = b'text/plain'
json_data = {'method': 'some.method', 'id': 0, 'params': []}
request.json = json_lib.dumps(json_data).encode()
- self.assertRaises(JSONException, json._on_json_request, request)
+ with pytest.raises(JSONException):
+ json._on_json_request(request)
+
+ def test_on_json_request_valid_content_type(self):
+ """Ensure content-type application/json is accepted"""
+ json = JSON()
+ request = MagicMock()
+ request.getHeader.return_value = b'application/json'
+ json_data = {'method': 'some.method', 'id': 0, 'params': []}
+ request.json = json_lib.dumps(json_data).encode()
+ json._on_json_request(request)
+ def test_on_json_request_valid_content_type_with_charset(self):
+ """Ensure content-type parameters such as charset are ignored"""
+ json = JSON()
+ request = MagicMock()
+ request.getHeader.return_value = b'application/json;charset=utf-8'
+ json_data = {'method': 'some.method', 'id': 0, 'params': []}
+ request.json = json_lib.dumps(json_data).encode()
+ json._on_json_request(request)
-class JSONCustomUserTestCase(JSONBase):
- def set_up(self):
- d = self.common_set_up()
- d.addCallback(self.start_core)
- return d
- @defer.inlineCallbacks
+@pytest.mark.usefixtures('daemon', 'client', 'component')
+class TestJSONCustomUserTestCase:
+ @pytest_twisted.inlineCallbacks
def test_handle_request_auth_error(self):
- yield self.connect_client()
json = JSON()
auth_conf = {'session_timeout': 10, 'sessions': {}}
Auth(auth_conf) # Must create the component
@@ -148,13 +133,12 @@ class JSONCustomUserTestCase(JSONBase):
json_data = {'method': 'core.get_libtorrent_version', 'id': 0, 'params': []}
request.json = json_lib.dumps(json_data).encode()
request_id, result, error = json._handle_request(request)
- self.assertEqual(error, {'message': 'Not authenticated', 'code': 1})
+ assert error == {'message': 'Not authenticated', 'code': 1}
-class RPCRaiseDelugeErrorJSONTestCase(JSONBase):
- def set_up(self):
- d = self.common_set_up()
- custom_script = """
+@pytest.mark.usefixtures('daemon', 'client', 'component')
+class TestRPCRaiseDelugeErrorJSON:
+ daemon_custom_script = """
from deluge.error import DelugeError
from deluge.core.rpcserver import export
class TestClass(object):
@@ -165,12 +149,8 @@ class RPCRaiseDelugeErrorJSONTestCase(JSONBase):
test = TestClass()
daemon.rpcserver.register_object(test)
"""
- d.addCallback(self.start_core, custom_script=custom_script)
- d.addCallbacks(self.connect_client, self.terminate_core)
- return d
- @defer.inlineCallbacks
- def test_handle_request_method_raise_delugeerror(self):
+ async def test_handle_request_method_raise_delugeerror(self):
json = JSON()
def get_session_id(s_id):
@@ -182,9 +162,9 @@ class RPCRaiseDelugeErrorJSONTestCase(JSONBase):
request = Request(MagicMock(), False)
request.base = b''
auth._create_session(request)
- methods = yield json.get_remote_methods()
+ methods = await json.get_remote_methods()
# Verify the function has been registered
- self.assertTrue('testclass.test' in methods)
+ assert 'testclass.test' in methods
request = MagicMock()
session_id = list(auth.config['sessions'])[0]
@@ -192,18 +172,13 @@ class RPCRaiseDelugeErrorJSONTestCase(JSONBase):
json_data = {'method': 'testclass.test', 'id': 0, 'params': []}
request.json = json_lib.dumps(json_data).encode()
request_id, result, error = json._handle_request(request)
- result.addCallback(self.fail)
+ with pytest.raises(DelugeError):
+ await result
- def on_error(error):
- self.assertEqual(error.type, DelugeError)
- result.addErrback(on_error)
- yield result
-
-
-class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
- def set_up(self):
- d = self.common_set_up()
+class TestJSONRequestFailed(WebServerMockBase):
+ @pytest_twisted.async_yield_fixture(autouse=True)
+ async def set_up(self, config_dir):
custom_script = """
from deluge.error import DelugeError
from deluge.core.rpcserver import export
@@ -219,7 +194,6 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
test = TestClass()
daemon.rpcserver.register_object(test)
"""
- from twisted.internet.defer import Deferred
extra_callback = {
'deferred': Deferred(),
@@ -234,28 +208,30 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
}
def on_test_raise(*args):
- self.assertTrue('Unhandled error in Deferred:' in self.core.stderr_out)
- self.assertTrue('in test_raise_error' in self.core.stderr_out)
+ assert 'Unhandled error in Deferred:' in daemon.stderr_out
+ assert 'in test_raise_error' in daemon.stderr_out
- extra_callback['deferred'].addCallback(on_test_raise)
- d.addCallback(
- self.start_core,
+ d, daemon = common.start_core(
custom_script=custom_script,
- print_stdout=False,
+ print_stdout=True,
print_stderr=False,
timeout=5,
extra_callbacks=[extra_callback],
+ config_directory=config_dir,
)
- d.addCallbacks(self.connect_client, self.terminate_core)
- return d
+ extra_callback['deferred'].addCallback(on_test_raise, daemon)
+
+ await d
+ yield
+ await daemon.kill()
- @defer.inlineCallbacks
- def test_render_on_rpc_request_failed(self):
+ @pytest_twisted.inlineCallbacks
+ def test_render_on_rpc_request_failed(self, component, client):
json = JSON()
methods = yield json.get_remote_methods()
# Verify the function has been registered
- self.assertTrue('testclass.test' in methods)
+ assert 'testclass.test' in methods
request = MagicMock()
@@ -266,14 +242,14 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
def write(response_str):
request.write_was_called = True
response = json_lib.loads(response_str.decode())
- self.assertEqual(response['result'], None, 'BAD RESULT')
- self.assertEqual(response['id'], 0)
- self.assertEqual(
- response['error']['message'],
- 'Failure: [Failure instance: Traceback (failure with no frames):'
- " <class 'deluge.error.DelugeError'>: DelugeERROR\n]",
+ assert response['result'] is None, 'BAD RESULT'
+ assert response['id'] == 0
+ assert (
+ response['error']['message']
+ == 'Failure: [Failure instance: Traceback (failure with no frames):'
+ " <class 'deluge.error.DelugeError'>: DelugeERROR\n]"
)
- self.assertEqual(response['error']['code'], 4)
+ assert response['error']['code'] == 4
request.write = write
request.write_was_called = False
@@ -284,8 +260,8 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
d = json._on_json_request(request)
def on_success(arg):
- self.assertEqual(arg, server.NOT_DONE_YET)
+ assert arg == server.NOT_DONE_YET
return True
- d.addCallbacks(on_success, self.fail)
+ d.addCallbacks(on_success, pytest.fail)
yield d
diff --git a/deluge/tests/test_log.py b/deluge/tests/test_log.py
index 572693b7c..f0dcbee86 100644
--- a/deluge/tests/test_log.py
+++ b/deluge/tests/test_log.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 Calum Lind <calumlind@gmail.com>
# Copyright (C) 2010 Pedro Algarvio <ufs@ufsoft.org>
@@ -8,17 +7,14 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import warnings
+from deluge.conftest import BaseTestCase
from deluge.log import setup_logger
-from .basetest import BaseTestCase
-
-class LogTestCase(BaseTestCase):
+class TestLog(BaseTestCase):
def set_up(self):
setup_logger(logging.DEBUG)
@@ -32,7 +28,7 @@ class LogTestCase(BaseTestCase):
# Cause all warnings to always be triggered.
warnings.simplefilter('always')
LOG.debug('foo')
- self.assertEqual(w[-1].category, DeprecationWarning)
+ assert w[-1].category == DeprecationWarning
# def test_twisted_error_log(self):
# from twisted.internet import defer
diff --git a/deluge/tests/test_maketorrent.py b/deluge/tests/test_maketorrent.py
index 4e0099653..a2e473f00 100644
--- a/deluge/tests/test_maketorrent.py
+++ b/deluge/tests/test_maketorrent.py
@@ -1,19 +1,13 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
import tempfile
-from twisted.trial import unittest
-
from deluge import maketorrent
-from deluge.common import windows_check
def check_torrent(filename):
@@ -28,7 +22,7 @@ def check_torrent(filename):
TorrentInfo(filename)
-class MakeTorrentTestCase(unittest.TestCase):
+class TestMakeTorrent:
def test_save_multifile(self):
# Create a temporary folder for torrent creation
tmp_path = tempfile.mkdtemp()
@@ -54,21 +48,16 @@ class MakeTorrentTestCase(unittest.TestCase):
os.remove(tmp_file)
def test_save_singlefile(self):
- if windows_check():
- raise unittest.SkipTest('on windows file not released')
- tmp_data = tempfile.mkstemp('testdata')[1]
- with open(tmp_data, 'wb') as _file:
- _file.write(b'a' * (2314 * 1024))
- t = maketorrent.TorrentMetadata()
- t.data_path = tmp_data
- tmp_fd, tmp_file = tempfile.mkstemp('.torrent')
- t.save(tmp_file)
-
- check_torrent(tmp_file)
-
- os.remove(tmp_data)
- os.close(tmp_fd)
- os.remove(tmp_file)
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ tmp_data = tmp_dir + '/data'
+ with open(tmp_data, 'wb') as _file:
+ _file.write(b'a' * (2314 * 1024))
+ t = maketorrent.TorrentMetadata()
+ t.data_path = tmp_data
+ tmp_file = tmp_dir + '/.torrent'
+ t.save(tmp_file)
+
+ check_torrent(tmp_file)
def test_save_multifile_padded(self):
# Create a temporary folder for torrent creation
diff --git a/deluge/tests/test_maybe_coroutine.py b/deluge/tests/test_maybe_coroutine.py
new file mode 100644
index 000000000..afaf171ba
--- /dev/null
+++ b/deluge/tests/test_maybe_coroutine.py
@@ -0,0 +1,207 @@
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+import pytest
+import pytest_twisted
+import twisted.python.failure
+from twisted.internet import defer, reactor, task
+from twisted.internet.defer import maybeDeferred
+
+from deluge.decorators import maybe_coroutine
+
+
+@defer.inlineCallbacks
+def inline_func():
+ result = yield task.deferLater(reactor, 0, lambda: 'function_result')
+ return result
+
+
+@defer.inlineCallbacks
+def inline_error():
+ raise Exception('function_error')
+ yield
+
+
+@maybe_coroutine
+async def coro_func():
+ result = await task.deferLater(reactor, 0, lambda: 'function_result')
+ return result
+
+
+@maybe_coroutine
+async def coro_error():
+ raise Exception('function_error')
+
+
+@defer.inlineCallbacks
+def coro_func_from_inline():
+ result = yield coro_func()
+ return result
+
+
+@defer.inlineCallbacks
+def coro_error_from_inline():
+ result = yield coro_error()
+ return result
+
+
+@maybe_coroutine
+async def coro_func_from_coro():
+ return await coro_func()
+
+
+@maybe_coroutine
+async def coro_error_from_coro():
+ return await coro_error()
+
+
+@maybe_coroutine
+async def inline_func_from_coro():
+ return await inline_func()
+
+
+@maybe_coroutine
+async def inline_error_from_coro():
+ return await inline_error()
+
+
+@pytest_twisted.inlineCallbacks
+def test_standard_twisted():
+ """Sanity check that twisted tests work how we expect.
+
+ Not really testing deluge code at all.
+ """
+ result = yield inline_func()
+ assert result == 'function_result'
+
+ with pytest.raises(Exception, match='function_error'):
+ yield inline_error()
+
+
+@pytest.mark.parametrize(
+ 'function',
+ [
+ inline_func,
+ coro_func,
+ coro_func_from_coro,
+ coro_func_from_inline,
+ inline_func_from_coro,
+ ],
+)
+@pytest_twisted.inlineCallbacks
+def test_from_inline(function):
+ """Test our coroutines wrapped with maybe_coroutine as if they returned plain twisted deferreds."""
+ result = yield function()
+ assert result == 'function_result'
+
+ def cb(result):
+ assert result == 'function_result'
+
+ d = function()
+ d.addCallback(cb)
+ yield d
+
+
+@pytest.mark.parametrize(
+ 'function',
+ [
+ inline_error,
+ coro_error,
+ coro_error_from_coro,
+ coro_error_from_inline,
+ inline_error_from_coro,
+ ],
+)
+@pytest_twisted.inlineCallbacks
+def test_error_from_inline(function):
+ """Test our coroutines wrapped with maybe_coroutine as if they returned plain twisted deferreds that raise."""
+ with pytest.raises(Exception, match='function_error'):
+ yield function()
+
+ def eb(result):
+ assert isinstance(result, twisted.python.failure.Failure)
+ assert result.getErrorMessage() == 'function_error'
+
+ d = function()
+ d.addErrback(eb)
+ yield d
+
+
+@pytest.mark.parametrize(
+ 'function',
+ [
+ inline_func,
+ coro_func,
+ coro_func_from_coro,
+ coro_func_from_inline,
+ inline_func_from_coro,
+ ],
+)
+async def test_from_coro(function):
+ """Test our coroutines wrapped with maybe_coroutine work from another coroutine."""
+ result = await function()
+ assert result == 'function_result'
+
+
+@pytest.mark.parametrize(
+ 'function',
+ [
+ inline_error,
+ coro_error,
+ coro_error_from_coro,
+ coro_error_from_inline,
+ inline_error_from_coro,
+ ],
+)
+async def test_error_from_coro(function):
+ """Test our coroutines wrapped with maybe_coroutine work from another coroutine with errors."""
+ with pytest.raises(Exception, match='function_error'):
+ await function()
+
+
+async def test_tracebacks_preserved():
+ with pytest.raises(Exception) as exc:
+ await coro_error_from_coro()
+ traceback_lines = [
+ 'await coro_error_from_coro()',
+ 'return await coro_error()',
+ "raise Exception('function_error')",
+ ]
+ # If each coroutine got wrapped with ensureDeferred, the traceback will be mangled
+ # verify the coroutines passed through by checking the traceback.
+ for expected, actual in zip(traceback_lines, exc.traceback):
+ assert expected in str(actual)
+
+
+async def test_maybe_deferred_coroutine():
+ result = await maybeDeferred(coro_func)
+ assert result == 'function_result'
+
+
+async def test_callback_before_await():
+ def cb(res):
+ assert res == 'function_result'
+ return res
+
+ d = coro_func()
+ d.addCallback(cb)
+ result = await d
+ assert result == 'function_result'
+
+
+async def test_callback_after_await():
+ """If it has already been used as a coroutine, can't be retroactively turned into a Deferred.
+ This limitation could be fixed, but the extra complication doesn't feel worth it.
+ """
+
+ def cb(res):
+ pass
+
+ d = coro_func()
+ await d
+ with pytest.raises(
+ Exception, match='Cannot add callbacks to an already awaited coroutine'
+ ):
+ d.addCallback(cb)
diff --git a/deluge/tests/test_metafile.py b/deluge/tests/test_metafile.py
index fc6507cb8..1b1675052 100644
--- a/deluge/tests/test_metafile.py
+++ b/deluge/tests/test_metafile.py
@@ -1,19 +1,19 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
import tempfile
-from twisted.trial import unittest
+import pytest
from deluge import metafile
-from deluge.common import windows_check
+from deluge._libtorrent import LT_VERSION
+from deluge.common import VersionSplit
+
+from . import common
def check_torrent(filename):
@@ -28,7 +28,7 @@ def check_torrent(filename):
TorrentInfo(filename)
-class MetafileTestCase(unittest.TestCase):
+class TestMetafile:
def test_save_multifile(self):
# Create a temporary folder for torrent creation
tmp_path = tempfile.mkdtemp()
@@ -52,17 +52,61 @@ class MetafileTestCase(unittest.TestCase):
os.remove(tmp_file)
def test_save_singlefile(self):
- if windows_check():
- raise unittest.SkipTest('on windows \\ != / for path names')
- tmp_path = tempfile.mkstemp('testdata')[1]
- with open(tmp_path, 'wb') as tmp_file:
- tmp_file.write(b'a' * (2314 * 1024))
-
- tmp_fd, tmp_file = tempfile.mkstemp('.torrent')
- metafile.make_meta_file(tmp_path, '', 32768, target=tmp_file)
-
- check_torrent(tmp_file)
-
- os.remove(tmp_path)
- os.close(tmp_fd)
- os.remove(tmp_file)
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ tmp_data = tmp_dir + '/testdata'
+ with open(tmp_data, 'wb') as tmp_file:
+ tmp_file.write(b'a' * (2314 * 1024))
+
+ tmp_torrent = tmp_dir + '/.torrent'
+ metafile.make_meta_file(tmp_data, '', 32768, target=tmp_torrent)
+
+ check_torrent(tmp_torrent)
+
+ @pytest.mark.parametrize(
+ 'path',
+ [
+ common.get_test_data_file('deluge.png'),
+ common.get_test_data_file('unicode_filenames.torrent'),
+ os.path.dirname(common.get_test_data_file('deluge.png')),
+ ],
+ )
+ @pytest.mark.parametrize(
+ 'torrent_format',
+ [
+ metafile.TorrentFormat.V1,
+ metafile.TorrentFormat.V2,
+ metafile.TorrentFormat.HYBRID,
+ ],
+ )
+ @pytest.mark.parametrize('piece_length', [2**14, 2**15, 2**16])
+ @pytest.mark.parametrize('private', [True, False])
+ def test_create_info(self, path, torrent_format, piece_length, private):
+ our_info, our_piece_layers = metafile.makeinfo(
+ path,
+ piece_length,
+ metafile.dummy,
+ private=private,
+ torrent_format=torrent_format,
+ )
+ lt_info, lt_piece_layers = metafile.makeinfo_lt(
+ path,
+ piece_length,
+ private=private,
+ torrent_format=torrent_format,
+ )
+
+ if (
+ torrent_format == metafile.TorrentFormat.HYBRID
+ and os.path.isdir(path)
+ and VersionSplit(LT_VERSION) <= VersionSplit('2.0.7.0')
+ ):
+ # Libtorrent didn't correctly follow the standard until version 2.0.7 included
+ # https://github.com/arvidn/libtorrent/commit/74d82a0cd7c2e9e3c4294901d7eb65e247050df4
+ # If last file is a padding, ignore that file and the last piece.
+ if our_info[b'files'][-1][b'path'][0] == b'.pad':
+ our_info[b'files'] = our_info[b'files'][:-1]
+ our_info[b'pieces'] = our_info[b'pieces'][:-32]
+ lt_info[b'pieces'] = lt_info[b'pieces'][:-32]
+
+ assert our_info == lt_info
+ assert our_piece_layers == lt_piece_layers
diff --git a/deluge/tests/test_plugin_metadata.py b/deluge/tests/test_plugin_metadata.py
index 436fc2c50..adf115d1b 100644
--- a/deluge/tests/test_plugin_metadata.py
+++ b/deluge/tests/test_plugin_metadata.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 Calum Lind <calumlind@gmail.com>
#
@@ -7,25 +6,38 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.pluginmanagerbase import PluginManagerBase
-from . import common
-from .basetest import BaseTestCase
-
-
-class PluginManagerBaseTestCase(BaseTestCase):
- def set_up(self):
- common.set_tmp_config_dir()
+class TestPluginManagerBase:
def test_get_plugin_info(self):
pm = PluginManagerBase('core.conf', 'deluge.plugin.core')
for p in pm.get_available_plugins():
for key, value in pm.get_plugin_info(p).items():
- self.assertTrue(isinstance('%s: %s' % (key, value), ''.__class__))
+ assert isinstance(key, str)
+ assert isinstance(value, str)
def test_get_plugin_info_invalid_name(self):
pm = PluginManagerBase('core.conf', 'deluge.plugin.core')
for key, value in pm.get_plugin_info('random').items():
- self.assertEqual(value, 'not available')
+ result = 'not available' if key in ('Name', 'Version') else ''
+ assert value == result
+
+ def test_parse_pkg_info_metadata_2_1(self):
+ pkg_info = """Metadata-Version: 2.1
+Name: AutoAdd
+Version: 1.8
+Summary: Monitors folders for .torrent files.
+Home-page: http://dev.deluge-torrent.org/wiki/Plugins/AutoAdd
+Author: Chase Sterling, Pedro Algarvio
+Author-email: chase.sterling@gmail.com, pedro@algarvio.me
+License: GPLv3
+Platform: UNKNOWN
+
+Monitors folders for .torrent files.
+ """
+ plugin_info = PluginManagerBase.parse_pkg_info(pkg_info)
+ for value in plugin_info.values():
+ assert value != ''
+ result = 'Monitors folders for .torrent files.'
+ assert plugin_info['Description'] == result
diff --git a/deluge/tests/test_rpcserver.py b/deluge/tests/test_rpcserver.py
index 02f9af023..77c9f1e9b 100644
--- a/deluge/tests/test_rpcserver.py
+++ b/deluge/tests/test_rpcserver.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 Bro <bro.development@gmail.com>
#
@@ -7,30 +6,26 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
import deluge.error
from deluge.common import get_localhost_auth
+from deluge.conftest import BaseTestCase
from deluge.core import rpcserver
from deluge.core.authmanager import AuthManager
from deluge.core.rpcserver import DelugeRPCProtocol, RPCServer
from deluge.log import setup_logger
-from .basetest import BaseTestCase
-
setup_logger('none')
class DelugeRPCProtocolTester(DelugeRPCProtocol):
-
messages = []
def transfer_message(self, data):
self.messages.append(data)
-class RPCServerTestCase(BaseTestCase):
+class TestRPCServer(BaseTestCase):
def set_up(self):
self.rpcserver = RPCServer(listen=False)
self.rpcserver.factory.protocol = DelugeRPCProtocolTester
@@ -60,15 +55,15 @@ class RPCServerTestCase(BaseTestCase):
e = TorrentFolderRenamedEvent(*data)
self.rpcserver.emit_event_for_session_id(self.session_id, e)
msg = self.protocol.messages.pop()
- self.assertEqual(msg[0], rpcserver.RPC_EVENT, str(msg))
- self.assertEqual(msg[1], 'TorrentFolderRenamedEvent', str(msg))
- self.assertEqual(msg[2], data, str(msg))
+ assert msg[0] == rpcserver.RPC_EVENT, str(msg)
+ assert msg[1] == 'TorrentFolderRenamedEvent', str(msg)
+ assert msg[2] == data, str(msg)
def test_invalid_client_login(self):
self.protocol.dispatch(self.request_id, 'daemon.login', [1], {})
msg = self.protocol.messages.pop()
- self.assertEqual(msg[0], rpcserver.RPC_ERROR)
- self.assertEqual(msg[1], self.request_id)
+ assert msg[0] == rpcserver.RPC_ERROR
+ assert msg[1] == self.request_id
def test_valid_client_login(self):
self.authmanager = AuthManager()
@@ -77,9 +72,9 @@ class RPCServerTestCase(BaseTestCase):
self.request_id, 'daemon.login', auth, {'client_version': 'Test'}
)
msg = self.protocol.messages.pop()
- self.assertEqual(msg[0], rpcserver.RPC_RESPONSE, str(msg))
- self.assertEqual(msg[1], self.request_id, str(msg))
- self.assertEqual(msg[2], rpcserver.AUTH_LEVEL_ADMIN, str(msg))
+ assert msg[0] == rpcserver.RPC_RESPONSE, str(msg)
+ assert msg[1] == self.request_id, str(msg)
+ assert msg[2] == rpcserver.AUTH_LEVEL_ADMIN, str(msg)
def test_client_login_error(self):
# This test causes error log prints while running the test...
@@ -90,24 +85,24 @@ class RPCServerTestCase(BaseTestCase):
self.request_id, 'daemon.login', auth, {'client_version': 'Test'}
)
msg = self.protocol.messages.pop()
- self.assertEqual(msg[0], rpcserver.RPC_ERROR)
- self.assertEqual(msg[1], self.request_id)
- self.assertEqual(msg[2], 'WrappedException')
- self.assertEqual(msg[3][1], 'AttributeError')
+ assert msg[0] == rpcserver.RPC_ERROR
+ assert msg[1] == self.request_id
+ assert msg[2] == 'WrappedException'
+ assert msg[3][1] == 'AttributeError'
def test_client_invalid_method_call(self):
self.authmanager = AuthManager()
auth = get_localhost_auth()
self.protocol.dispatch(self.request_id, 'invalid_function', auth, {})
msg = self.protocol.messages.pop()
- self.assertEqual(msg[0], rpcserver.RPC_ERROR)
- self.assertEqual(msg[1], self.request_id)
- self.assertEqual(msg[2], 'WrappedException')
- self.assertEqual(msg[3][1], 'AttributeError')
+ assert msg[0] == rpcserver.RPC_ERROR
+ assert msg[1] == self.request_id
+ assert msg[2] == 'WrappedException'
+ assert msg[3][1] == 'AttributeError'
def test_daemon_info(self):
self.protocol.dispatch(self.request_id, 'daemon.info', [], {})
msg = self.protocol.messages.pop()
- self.assertEqual(msg[0], rpcserver.RPC_RESPONSE, str(msg))
- self.assertEqual(msg[1], self.request_id, str(msg))
- self.assertEqual(msg[2], deluge.common.get_version(), str(msg))
+ assert msg[0] == rpcserver.RPC_RESPONSE, str(msg)
+ assert msg[1] == self.request_id, str(msg)
+ assert msg[2] == deluge.common.get_version(), str(msg)
diff --git a/deluge/tests/test_security.py b/deluge/tests/test_security.py
index 700fc9967..c472d1630 100644
--- a/deluge/tests/test_security.py
+++ b/deluge/tests/test_security.py
@@ -1,12 +1,9 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# 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 pytest
@@ -16,9 +13,9 @@ import deluge.component as component
import deluge.ui.web.server
from deluge import configmanager
from deluge.common import windows_check
+from deluge.conftest import BaseTestCase
from deluge.ui.web.server import DelugeWeb
-from .basetest import BaseTestCase
from .common import get_test_data_file
from .common_web import WebServerTestBase
from .daemon_base import DaemonBase
@@ -26,15 +23,10 @@ from .daemon_base import DaemonBase
SECURITY_TESTS = bool(os.getenv('SECURITY_TESTS', False))
-class SecurityBaseTestCase(object):
- if windows_check():
- skip = 'windows cannot run .sh files'
- elif not SECURITY_TESTS:
- skip = 'Skipping security tests'
-
- http_err = 'cannot run http tests on daemon'
-
- def __init__(self):
+# TODO: This whole module has not been tested since migrating tests fully to pytest
+class SecurityBaseTestCase:
+ @pytest.fixture(autouse=True)
+ def setvars(self):
self.home_dir = os.path.expanduser('~')
self.port = 8112
@@ -45,6 +37,7 @@ class SecurityBaseTestCase(object):
get_test_data_file('testssl.sh'),
'--quiet',
'--nodns',
+ 'none',
'--color',
'0',
test,
@@ -53,13 +46,12 @@ class SecurityBaseTestCase(object):
)
def on_result(results):
-
if test == '-e':
- results = results[0].split('\n')[7:-6]
- self.assertTrue(len(results) > 3)
+ results = results[0].split(b'\n')[7:-6]
+ assert len(results) > 3
else:
- self.assertIn('OK', results[0])
- self.assertNotIn('NOT ok', results[0])
+ assert b'OK' in results[0]
+ assert b'NOT ok' not in results[0]
d.addCallback(on_result)
return d
@@ -76,18 +68,12 @@ class SecurityBaseTestCase(object):
def test_secured_webserver_css_injection_vulnerability(self):
return self._run_test('-I')
- def test_secured_webserver_ticketbleed_vulnerability(self):
- return self._run_test('-T')
-
def test_secured_webserver_renegotiation_vulnerabilities(self):
return self._run_test('-R')
def test_secured_webserver_crime_vulnerability(self):
return self._run_test('-C')
- def test_secured_webserver_breach_vulnerability(self):
- return self._run_test('-B')
-
def test_secured_webserver_poodle_vulnerability(self):
return self._run_test('-O')
@@ -121,33 +107,14 @@ class SecurityBaseTestCase(object):
def test_secured_webserver_preference(self):
return self._run_test('-P')
- def test_secured_webserver_headers(self):
- return self._run_test('-h')
-
def test_secured_webserver_ciphers(self):
return self._run_test('-e')
+@pytest.mark.skipif(windows_check(), reason='windows cannot run .sh files')
+@pytest.mark.skipif(not SECURITY_TESTS, reason='skipping security tests')
@pytest.mark.security
-class DaemonSecurityTestCase(BaseTestCase, DaemonBase, SecurityBaseTestCase):
-
- if windows_check():
- skip = 'windows cannot start_core not enough arguments for format string'
-
- def __init__(self, testname):
- super(DaemonSecurityTestCase, self).__init__(testname)
- DaemonBase.__init__(self)
- SecurityBaseTestCase.__init__(self)
-
- def setUp(self):
- skip = False
- for not_http_test in ('breach', 'headers', 'ticketbleed'):
- if not_http_test in self.id().split('.')[-1]:
- self.skipTest(SecurityBaseTestCase.http_err)
- skip = True
- if not skip:
- super(DaemonSecurityTestCase, self).setUp()
-
+class TestDaemonSecurity(BaseTestCase, DaemonBase, SecurityBaseTestCase):
def set_up(self):
d = self.common_set_up()
self.port = self.listen_port
@@ -161,17 +128,15 @@ class DaemonSecurityTestCase(BaseTestCase, DaemonBase, SecurityBaseTestCase):
return d
+@pytest.mark.skipif(windows_check(), reason='windows cannot run .sh files')
+@pytest.mark.skipif(not SECURITY_TESTS, reason='skipping security tests')
@pytest.mark.security
-class WebUISecurityTestBase(WebServerTestBase, SecurityBaseTestCase):
- def __init__(self, testname):
- super(WebUISecurityTestBase, self).__init__(testname)
- SecurityBaseTestCase.__init__(self)
-
+class TestWebUISecurity(WebServerTestBase, SecurityBaseTestCase):
def start_webapi(self, arg):
- self.port = self.webserver_listen_port = 8999
+ self.port = self.deluge_web.port = 8999
config_defaults = deluge.ui.web.server.CONFIG_DEFAULTS.copy()
- config_defaults['port'] = self.webserver_listen_port
+ config_defaults['port'] = self.deluge_web.port
config_defaults['https'] = True
self.config = configmanager.ConfigManager('web.conf', config_defaults)
@@ -182,3 +147,12 @@ class WebUISecurityTestBase(WebServerTestBase, SecurityBaseTestCase):
self.deluge_web.web_api.hostlist.config['hosts'][0] = tuple(host)
self.host_id = host[0]
self.deluge_web.start()
+
+ def test_secured_webserver_headers(self):
+ return self._run_test('-h')
+
+ def test_secured_webserver_breach_vulnerability(self):
+ return self._run_test('-B')
+
+ def test_secured_webserver_ticketbleed_vulnerability(self):
+ return self._run_test('-T')
diff --git a/deluge/tests/test_sessionproxy.py b/deluge/tests/test_sessionproxy.py
index 03f3cc27e..86289ccb8 100644
--- a/deluge/tests/test_sessionproxy.py
+++ b/deluge/tests/test_sessionproxy.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -6,19 +5,15 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-
-from __future__ import unicode_literals
-
from twisted.internet.defer import maybeDeferred, succeed
from twisted.internet.task import Clock
import deluge.component as component
import deluge.ui.sessionproxy
+from deluge.conftest import BaseTestCase
-from .basetest import BaseTestCase
-
-class Core(object):
+class Core:
def __init__(self):
self.reset()
@@ -91,7 +86,7 @@ class Core(object):
return succeed(ret)
-class Client(object):
+class Client:
def __init__(self):
self.core = Core()
@@ -105,7 +100,7 @@ class Client(object):
client = Client()
-class SessionProxyTestCase(BaseTestCase):
+class TestSessionProxy(BaseTestCase):
def set_up(self):
self.clock = Clock()
self.patch(deluge.ui.sessionproxy, 'time', self.clock.seconds)
@@ -127,38 +122,33 @@ class SessionProxyTestCase(BaseTestCase):
return component.deregister(self.sp)
def test_startup(self):
- self.assertEqual(client.core.torrents['a'], self.sp.torrents['a'][1])
+ assert client.core.torrents['a'] == self.sp.torrents['a'][1]
- def test_get_torrent_status_no_change(self):
- d = self.sp.get_torrent_status('a', [])
- d.addCallback(self.assertEqual, client.core.torrents['a'])
- return d
+ async def test_get_torrent_status_no_change(self):
+ result = await self.sp.get_torrent_status('a', [])
+ assert result == client.core.torrents['a']
- def test_get_torrent_status_change_with_cache(self):
+ async def test_get_torrent_status_change_with_cache(self):
client.core.torrents['a']['key1'] = 2
- d = self.sp.get_torrent_status('a', ['key1'])
- d.addCallback(self.assertEqual, {'key1': 1})
- return d
+ result = await self.sp.get_torrent_status('a', ['key1'])
+ assert result == {'key1': 1}
- def test_get_torrent_status_change_without_cache(self):
+ async def test_get_torrent_status_change_without_cache(self):
client.core.torrents['a']['key1'] = 2
self.clock.advance(self.sp.cache_time + 0.1)
- d = self.sp.get_torrent_status('a', [])
- d.addCallback(self.assertEqual, client.core.torrents['a'])
- return d
+ result = await self.sp.get_torrent_status('a', [])
+ assert result == client.core.torrents['a']
- def test_get_torrent_status_key_not_updated(self):
+ async def test_get_torrent_status_key_not_updated(self):
self.clock.advance(self.sp.cache_time + 0.1)
self.sp.get_torrent_status('a', ['key1'])
client.core.torrents['a']['key2'] = 99
- d = self.sp.get_torrent_status('a', ['key2'])
- d.addCallback(self.assertEqual, {'key2': 99})
- return d
+ result = await self.sp.get_torrent_status('a', ['key2'])
+ assert result == {'key2': 99}
- def test_get_torrents_status_key_not_updated(self):
+ async def test_get_torrents_status_key_not_updated(self):
self.clock.advance(self.sp.cache_time + 0.1)
self.sp.get_torrents_status({'id': ['a']}, ['key1'])
client.core.torrents['a']['key2'] = 99
- d = self.sp.get_torrents_status({'id': ['a']}, ['key2'])
- d.addCallback(self.assertEqual, {'a': {'key2': 99}})
- return d
+ result = await self.sp.get_torrents_status({'id': ['a']}, ['key2'])
+ assert result == {'a': {'key2': 99}}
diff --git a/deluge/tests/test_torrent.py b/deluge/tests/test_torrent.py
index 5da817924..62886159e 100644
--- a/deluge/tests/test_torrent.py
+++ b/deluge/tests/test_torrent.py
@@ -1,41 +1,41 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# 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 itertools
import os
import time
from base64 import b64encode
+from unittest import mock
-import mock
+import pytest
from twisted.internet import defer, reactor
from twisted.internet.task import deferLater
-from twisted.trial import unittest
import deluge.component as component
import deluge.core.torrent
import deluge.tests.common as common
from deluge._libtorrent import lt
-from deluge.common import VersionSplit, utf8_encode_structure, windows_check
+from deluge.common import VersionSplit, utf8_encode_structure
+from deluge.conftest import BaseTestCase
from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer
from deluge.core.torrent import Torrent
from deluge.core.torrentmanager import TorrentManager, TorrentState
-from .basetest import BaseTestCase
+try:
+ from unittest.mock import AsyncMock
+except ImportError:
+ from mock import AsyncMock
-class TorrentTestCase(BaseTestCase):
+class TestTorrent(BaseTestCase):
def setup_config(self):
- config_dir = common.set_tmp_config_dir()
core_config = deluge.config.Config(
'core.conf',
defaults=deluge.core.preferencesmanager.DEFAULT_PREFS,
- config_dir=config_dir,
+ config_dir=self.config_dir,
)
core_config.save()
@@ -66,8 +66,21 @@ class TorrentTestCase(BaseTestCase):
print(tmp)
def assert_state(self, torrent, state):
+ """Assert torrent state matches expected state"""
torrent.update_state()
- self.assertEqual(torrent.state, state)
+ assert torrent.state == state
+
+ def assert_state_wait(self, torrent, expected, timeout=1, interval=0.2):
+ """Assert state but retry with timeout e.g. Allow for async lt alerts"""
+ start = time.time()
+
+ while time.time() - start < timeout:
+ torrent.update_state()
+ time.sleep(interval)
+ if torrent.state == expected:
+ break
+ else:
+ assert torrent.state == expected
def get_torrent_atp(self, filename):
filename = common.get_test_data_file(filename)
@@ -78,32 +91,42 @@ class TorrentTestCase(BaseTestCase):
'save_path': os.getcwd(),
'storage_mode': lt.storage_mode_t.storage_mode_sparse,
'flags': (
- lt.add_torrent_params_flags_t.flag_auto_managed
- | lt.add_torrent_params_flags_t.flag_duplicate_is_error
- & ~lt.add_torrent_params_flags_t.flag_paused
+ lt.torrent_flags.auto_managed
+ | lt.torrent_flags.duplicate_is_error & ~lt.torrent_flags.paused
),
}
return atp
- def test_set_file_priorities(self):
+ async def test_set_file_priorities(self):
+ if getattr(lt, 'file_prio_alert', None):
+ # Libtorrent 2.0.3 and later has a file_prio_alert
+ prios_set = defer.Deferred()
+ prios_set.addTimeout(1.5, reactor)
+ component.get('AlertManager').register_handler(
+ 'file_prio_alert', lambda a: prios_set.callback(True)
+ )
+ else:
+ # On older libtorrent, we just wait a while
+ prios_set = deferLater(reactor, 0.8)
+
atp = self.get_torrent_atp('dir_with_6_files.torrent')
handle = self.session.add_torrent(atp)
torrent = Torrent(handle, {})
result = torrent.get_file_priorities()
- self.assertTrue(all(x == 4 for x in result))
+ assert all(x == 4 for x in result)
new_priorities = [3, 1, 2, 0, 5, 6, 7]
torrent.set_file_priorities(new_priorities)
- self.assertEqual(torrent.get_file_priorities(), new_priorities)
+ assert torrent.get_file_priorities() == new_priorities
# Test with handle.piece_priorities as handle.file_priorities async
# updates and will return old value. Also need to remove a priority
# value as one file is much smaller than piece size so doesn't show.
- time.sleep(0.6) # Delay to wait for alert from lt
- piece_prio = handle.piece_priorities()
+ await prios_set # Delay to wait for alert from lt
+ piece_prio = handle.get_piece_priorities()
result = all(p in piece_prio for p in [3, 2, 0, 5, 6, 7])
- self.assertTrue(result)
+ assert result
def test_set_prioritize_first_last_pieces(self):
piece_indexes = [
@@ -143,19 +166,19 @@ class TorrentTestCase(BaseTestCase):
handle = self.session.add_torrent(atp)
self.torrent = Torrent(handle, {})
- priorities_original = handle.piece_priorities()
+ priorities_original = handle.get_piece_priorities()
self.torrent.set_prioritize_first_last_pieces(True)
- priorities = handle.piece_priorities()
+ priorities = handle.get_piece_priorities()
# The length of the list of new priorites is the same as the original
- self.assertEqual(len(priorities_original), len(priorities))
+ assert len(priorities_original) == len(priorities)
# Test the priority of all the pieces against the calculated indexes.
for idx, priority in enumerate(priorities):
if idx in prioritized_piece_indexes:
- self.assertEqual(priorities[idx], 7)
+ assert priorities[idx] == 7
else:
- self.assertEqual(priorities[idx], 4)
+ assert priorities[idx] == 4
# self.print_priority_list(priorities)
@@ -167,17 +190,15 @@ class TorrentTestCase(BaseTestCase):
self.torrent.set_prioritize_first_last_pieces(True)
# Reset pirorities
self.torrent.set_prioritize_first_last_pieces(False)
- priorities = handle.piece_priorities()
+ priorities = handle.get_piece_priorities()
# Test the priority of the prioritized pieces
for i in priorities:
- self.assertEqual(priorities[i], 4)
+ assert priorities[i] == 4
# self.print_priority_list(priorities)
def test_torrent_error_data_missing(self):
- if windows_check():
- raise unittest.SkipTest('unexpected end of file in bencoded string')
options = {'seed_mode': True}
filename = common.get_test_data_file('test_torrent.file.torrent')
with open(filename, 'rb') as _file:
@@ -185,17 +206,14 @@ class TorrentTestCase(BaseTestCase):
torrent_id = self.core.add_torrent_file(filename, filedump, options)
torrent = self.core.torrentmanager.torrents[torrent_id]
- # time.sleep(0.5) # Delay to wait for lt to finish check on Travis.
- # self.assert_state(torrent, 'Seeding')
+ # Inital check will fail and return to download state
+ self.assert_state_wait(torrent, 'Downloading')
# Force an error by reading (non-existant) piece from disk
torrent.handle.read_piece(0)
- time.sleep(0.2) # Delay to wait for alert from lt
- self.assert_state(torrent, 'Error')
+ self.assert_state_wait(torrent, 'Error')
def test_torrent_error_resume_original_state(self):
- if windows_check():
- raise unittest.SkipTest('unexpected end of file in bencoded string')
options = {'seed_mode': True, 'add_paused': True}
filename = common.get_test_data_file('test_torrent.file.torrent')
with open(filename, 'rb') as _file:
@@ -208,17 +226,14 @@ class TorrentTestCase(BaseTestCase):
# Force an error by reading (non-existant) piece from disk
torrent.handle.read_piece(0)
- time.sleep(0.2) # Delay to wait for alert from lt
- self.assert_state(torrent, 'Error')
+ self.assert_state_wait(torrent, 'Error')
# Clear error and verify returned to original state
torrent.force_recheck()
def test_torrent_error_resume_data_unaltered(self):
- if windows_check():
- raise unittest.SkipTest('unexpected end of file in bencoded string')
if VersionSplit(lt.__version__) >= VersionSplit('1.2.0.0'):
- raise unittest.SkipTest('Test not working as expected on lt 1.2 or greater')
+ pytest.skip('Test not working as expected on lt 1.2 or greater')
resume_data = {
'active_time': 13399,
@@ -286,7 +301,7 @@ class TorrentTestCase(BaseTestCase):
tm_resume_data = lt.bdecode(
self.core.torrentmanager.resume_data[torrent.torrent_id]
)
- self.assertEqual(tm_resume_data, resume_data)
+ assert tm_resume_data == resume_data
return deferLater(reactor, 0.5, assert_resume_data)
@@ -294,7 +309,7 @@ class TorrentTestCase(BaseTestCase):
atp = self.get_torrent_atp('test_torrent.file.torrent')
handle = self.session.add_torrent(atp)
self.torrent = Torrent(handle, {})
- self.assertEqual(self.torrent.get_eta(), 0)
+ assert self.torrent.get_eta() == 0
self.torrent.status = mock.MagicMock()
self.torrent.status.upload_payload_rate = 5000
@@ -304,18 +319,18 @@ class TorrentTestCase(BaseTestCase):
self.torrent.is_finished = True
self.torrent.options = {'stop_at_ratio': False}
# Test finished and uploading but no stop_at_ratio set.
- self.assertEqual(self.torrent.get_eta(), 0)
+ assert self.torrent.get_eta() == 0
self.torrent.options = {'stop_at_ratio': True, 'stop_ratio': 1.5}
result = self.torrent.get_eta()
- self.assertEqual(result, 2)
- self.assertIsInstance(result, int)
+ assert result == 2
+ assert isinstance(result, int)
def test_get_eta_downloading(self):
atp = self.get_torrent_atp('test_torrent.file.torrent')
handle = self.session.add_torrent(atp)
self.torrent = Torrent(handle, {})
- self.assertEqual(self.torrent.get_eta(), 0)
+ assert self.torrent.get_eta() == 0
self.torrent.status = mock.MagicMock()
self.torrent.status.download_payload_rate = 50
@@ -323,15 +338,15 @@ class TorrentTestCase(BaseTestCase):
self.torrent.status.total_wanted_done = 5000
result = self.torrent.get_eta()
- self.assertEqual(result, 100)
- self.assertIsInstance(result, int)
+ assert result == 100
+ assert isinstance(result, int)
def test_get_name_unicode(self):
"""Test retrieving a unicode torrent name from libtorrent."""
atp = self.get_torrent_atp('unicode_file.torrent')
handle = self.session.add_torrent(atp)
self.torrent = Torrent(handle, {})
- self.assertEqual(self.torrent.get_name(), 'সুকুমার রায়.txt')
+ assert self.torrent.get_name() == 'সুকুমার রায়.txt'
def test_rename_unicode(self):
"""Test renaming file/folders with unicode filenames."""
@@ -339,18 +354,35 @@ class TorrentTestCase(BaseTestCase):
handle = self.session.add_torrent(atp)
self.torrent = Torrent(handle, {})
# Ignore TorrentManager method call
- TorrentManager.save_resume_data = mock.MagicMock
+ TorrentManager.save_resume_data = AsyncMock()
result = self.torrent.rename_folder('unicode_filenames', 'Горбачёв')
- self.assertIsInstance(result, defer.DeferredList)
+ assert isinstance(result, defer.DeferredList)
result = self.torrent.rename_files([[0, 'new_рбачёв']])
- self.assertIsNone(result)
+ assert result is None
def test_connect_peer_port(self):
"""Test to ensure port is int for libtorrent"""
atp = self.get_torrent_atp('test_torrent.file.torrent')
handle = self.session.add_torrent(atp)
self.torrent = Torrent(handle, {})
- self.assertFalse(self.torrent.connect_peer('127.0.0.1', 'text'))
- self.assertTrue(self.torrent.connect_peer('127.0.0.1', '1234'))
+ assert not self.torrent.connect_peer('127.0.0.1', 'text')
+ assert self.torrent.connect_peer('127.0.0.1', '1234')
+
+ def test_status_cache(self):
+ atp = self.get_torrent_atp('test_torrent.file.torrent')
+ handle = self.session.add_torrent(atp)
+ mock_time = mock.Mock(return_value=time.time())
+ with mock.patch('time.time', mock_time):
+ torrent = Torrent(handle, {})
+ counter = itertools.count()
+ handle.status = mock.Mock(side_effect=counter.__next__)
+ first_status = torrent.get_lt_status()
+ assert first_status == 0, 'sanity check'
+ assert first_status == torrent.status, 'cached status should be used'
+ assert torrent.get_lt_status() == 1, 'status should update'
+ assert torrent.status == 1
+ # Advance time and verify cache expires and updates
+ mock_time.return_value += 10
+ assert torrent.status == 2
diff --git a/deluge/tests/test_torrentmanager.py b/deluge/tests/test_torrentmanager.py
index e0ff09efc..1a5e3a930 100644
--- a/deluge/tests/test_torrentmanager.py
+++ b/deluge/tests/test_torrentmanager.py
@@ -1,38 +1,34 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
import shutil
import warnings
from base64 import b64encode
+from unittest import mock
-import mock
import pytest
-from twisted.internet import defer, task
-from twisted.trial import unittest
+import pytest_twisted
+from twisted.internet import reactor, task
from deluge import component
-from deluge.common import windows_check
+from deluge.bencode import bencode
+from deluge.conftest import BaseTestCase
from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer
from deluge.error import InvalidTorrentError
from . import common
-from .basetest import BaseTestCase
warnings.filterwarnings('ignore', category=RuntimeWarning)
warnings.resetwarnings()
-class TorrentmanagerTestCase(BaseTestCase):
+class TestTorrentmanager(BaseTestCase):
def set_up(self):
- self.config_dir = common.set_tmp_config_dir()
self.rpcserver = RPCServer(listen=False)
self.core = Core()
self.core.config.config['lsd'] = False
@@ -48,7 +44,7 @@ class TorrentmanagerTestCase(BaseTestCase):
return component.shutdown().addCallback(on_shutdown)
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_remove_torrent(self):
filename = common.get_test_data_file('test.torrent')
with open(filename, 'rb') as _file:
@@ -56,9 +52,9 @@ class TorrentmanagerTestCase(BaseTestCase):
torrent_id = yield self.core.add_torrent_file_async(
filename, b64encode(filedump), {}
)
- self.assertTrue(self.tm.remove(torrent_id, False))
+ assert self.tm.remove(torrent_id, False)
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_remove_magnet(self):
"""Test remove magnet before received metadata and delete_copies is True"""
magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173'
@@ -66,9 +62,9 @@ class TorrentmanagerTestCase(BaseTestCase):
self.core.config.config['copy_torrent_file'] = True
self.core.config.config['del_copy_torrent_file'] = True
torrent_id = yield self.core.add_torrent_magnet(magnet, options)
- self.assertTrue(self.tm.remove(torrent_id, False))
+ assert self.tm.remove(torrent_id, False)
- def test_prefetch_metadata(self):
+ async def test_prefetch_metadata(self):
from deluge._libtorrent import lt
with open(common.get_test_data_file('test.torrent'), 'rb') as _file:
@@ -81,47 +77,54 @@ class TorrentmanagerTestCase(BaseTestCase):
magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173'
d = self.tm.prefetch_metadata(magnet, 30)
- self.tm.on_alert_metadata_received(mock_alert)
+ # Make sure to use calllater, because the above prefetch call won't
+ # actually start running until we await it.
+ reactor.callLater(0, self.tm.on_alert_metadata_received, mock_alert)
expected = (
'ab570cdd5a17ea1b61e970bb72047de141bce173',
- {
- b'piece length': 32768,
- b'sha1': (
- b'2\xce\xb6\xa8"\xd7\xf0\xd4\xbf\xdc^K\xba\x1bh'
- b'\x9d\xc5\xb7\xac\xdd'
- ),
- b'name': b'azcvsupdater_2.6.2.jar',
- b'private': 0,
- b'pieces': (
- b"\xdb\x04B\x05\xc3'\xdab\xb8su97\xa9u"
- b'\xca<w\\\x1ef\xd4\x9b\x16\xa9}\xc0\x9f:\xfd'
- b'\x97qv\x83\xa2"\xef\x9d7\x0by!\rl\xe5v\xb7'
- b'\x18{\xf7/"P\xe9\x8d\x01D\x9e8\xbd\x16\xe3'
- b'\xfb-\x9d\xaa\xbcM\x11\xba\x92\xfc\x13F\xf0'
- b'\x1c\x86x+\xc8\xd0S\xa9\x90`\xa1\xe4\x82\xe8'
- b'\xfc\x08\xf7\xe3\xe5\xf6\x85\x1c%\xe7%\n\xed'
- b'\xc0\x1f\xa1;\x9a\xea\xcf\x90\x0c/F>\xdf\xdagA'
- b'\xc42|\xda\x82\xf5\xa6b\xa1\xb8#\x80wI\xd8f'
- b'\xf8\xbd\xacW\xab\xc3s\xe0\xbbw\xf2K\xbe\xee'
- b'\xa8rG\xe1W\xe8\xb7\xc2i\xf3\xd8\xaf\x9d\xdc'
- b'\xd0#\xf4\xc1\x12u\xcd\x0bE?:\xe8\x9c\x1cu'
- b'\xabb(oj\r^\xd5\xd5A\x83\x88\x9a\xa1J\x1c?'
- b'\xa1\xd6\x8c\x83\x9e&'
- ),
- b'length': 307949,
- b'name.utf-8': b'azcvsupdater_2.6.2.jar',
- b'ed2k': b'>p\xefl\xfa]\x95K\x1b^\xc2\\;;e\xb7',
- },
+ b64encode(
+ bencode(
+ {
+ b'piece length': 32768,
+ b'sha1': (
+ b'2\xce\xb6\xa8"\xd7\xf0\xd4\xbf\xdc^K\xba\x1bh'
+ b'\x9d\xc5\xb7\xac\xdd'
+ ),
+ b'name': b'azcvsupdater_2.6.2.jar',
+ b'private': 0,
+ b'pieces': (
+ b"\xdb\x04B\x05\xc3'\xdab\xb8su97\xa9u"
+ b'\xca<w\\\x1ef\xd4\x9b\x16\xa9}\xc0\x9f:\xfd'
+ b'\x97qv\x83\xa2"\xef\x9d7\x0by!\rl\xe5v\xb7'
+ b'\x18{\xf7/"P\xe9\x8d\x01D\x9e8\xbd\x16\xe3'
+ b'\xfb-\x9d\xaa\xbcM\x11\xba\x92\xfc\x13F\xf0'
+ b'\x1c\x86x+\xc8\xd0S\xa9\x90`\xa1\xe4\x82\xe8'
+ b'\xfc\x08\xf7\xe3\xe5\xf6\x85\x1c%\xe7%\n\xed'
+ b'\xc0\x1f\xa1;\x9a\xea\xcf\x90\x0c/F>\xdf\xdagA'
+ b'\xc42|\xda\x82\xf5\xa6b\xa1\xb8#\x80wI\xd8f'
+ b'\xf8\xbd\xacW\xab\xc3s\xe0\xbbw\xf2K\xbe\xee'
+ b'\xa8rG\xe1W\xe8\xb7\xc2i\xf3\xd8\xaf\x9d\xdc'
+ b'\xd0#\xf4\xc1\x12u\xcd\x0bE?:\xe8\x9c\x1cu'
+ b'\xabb(oj\r^\xd5\xd5A\x83\x88\x9a\xa1J\x1c?'
+ b'\xa1\xd6\x8c\x83\x9e&'
+ ),
+ b'length': 307949,
+ b'name.utf-8': b'azcvsupdater_2.6.2.jar',
+ b'ed2k': b'>p\xefl\xfa]\x95K\x1b^\xc2\\;;e\xb7',
+ }
+ )
+ ),
)
- self.assertEqual(expected, self.successResultOf(d))
+ assert expected == await d
- def test_prefetch_metadata_timeout(self):
+ async def test_prefetch_metadata_timeout(self):
magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173'
d = self.tm.prefetch_metadata(magnet, 30)
self.clock.advance(30)
- expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', None)
- return d.addCallback(self.assertEqual, expected)
+ result = await d
+ expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', b'')
+ assert result == expected
@pytest.mark.todo
def test_remove_torrent_false(self):
@@ -129,20 +132,15 @@ class TorrentmanagerTestCase(BaseTestCase):
common.todo_test(self)
def test_remove_invalid_torrent(self):
- self.assertRaises(
- InvalidTorrentError, self.tm.remove, 'torrentidthatdoesntexist'
- )
+ with pytest.raises(InvalidTorrentError):
+ self.tm.remove('torrentidthatdoesntexist')
- def test_open_state_from_python2(self):
- """Open a Python2 state with a UTF-8 encoded torrent filename."""
+ def test_open_state(self):
+ """Open a state with a UTF-8 encoded torrent filename."""
shutil.copy(
common.get_test_data_file('utf8_filename_torrents.state'),
os.path.join(self.config_dir, 'state', 'torrents.state'),
)
- if windows_check():
- raise unittest.SkipTest(
- 'Windows ModuleNotFoundError due to Linux line ending'
- )
state = self.tm.open_state()
- self.assertEqual(len(state.torrents), 1)
+ assert len(state.torrents) == 1
diff --git a/deluge/tests/test_torrentview.py b/deluge/tests/test_torrentview.py
index 590760d1e..9da99d849 100644
--- a/deluge/tests/test_torrentview.py
+++ b/deluge/tests/test_torrentview.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Bro <bro.development@gmail.com>
# Copyright (C) 2014 Calum Lind <calumlind@gmail.com>
@@ -8,18 +7,13 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import pytest
-from twisted.trial import unittest
import deluge.component as component
from deluge.configmanager import ConfigManager
+from deluge.conftest import BaseTestCase
from deluge.i18n import setup_translation
-from . import common
-from .basetest import BaseTestCase
-
# Allow running other tests without GTKUI dependencies available
try:
# pylint: disable=ungrouped-imports
@@ -40,8 +34,7 @@ setup_translation()
@pytest.mark.gtkui
-class TorrentviewTestCase(BaseTestCase):
-
+class TestTorrentview(BaseTestCase):
default_column_index = [
'filter',
'torrent_id',
@@ -66,6 +59,7 @@ class TorrentviewTestCase(BaseTestCase):
'Added',
'Completed',
'Complete Seen',
+ 'Last Transfer',
'Tracker',
'Download Folder',
'Owner',
@@ -99,6 +93,7 @@ class TorrentviewTestCase(BaseTestCase):
int,
int,
int,
+ int,
str,
str, # Tracker
str,
@@ -108,9 +103,8 @@ class TorrentviewTestCase(BaseTestCase):
def set_up(self):
if libs_available is False:
- raise unittest.SkipTest('GTKUI dependencies not available')
+ pytest.skip('GTKUI dependencies not available')
- common.set_tmp_config_dir()
# MainWindow loads this config file, so lets make sure it contains the defaults
ConfigManager('gtk3ui.conf', defaults=DEFAULT_PREFS)
self.mainwindow = MainWindow()
@@ -122,36 +116,23 @@ class TorrentviewTestCase(BaseTestCase):
return component.shutdown()
def test_torrentview_columns(self):
-
- self.assertEqual(
- self.torrentview.column_index, TorrentviewTestCase.default_column_index
- )
- self.assertEqual(
- self.torrentview.liststore_columns,
- TorrentviewTestCase.default_liststore_columns,
- )
- self.assertEqual(
- self.torrentview.columns['Download Folder'].column_indices, [29]
- )
+ assert self.torrentview.column_index == self.default_column_index
+ assert self.torrentview.liststore_columns == self.default_liststore_columns
+ assert self.torrentview.columns['Download Folder'].column_indices == [30]
def test_add_column(self):
-
# Add a text column
test_col = 'Test column'
self.torrentview.add_text_column(test_col, status_field=['label'])
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns) + 1,
- )
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index) + 1,
+ assert (
+ len(self.torrentview.liststore_columns)
+ == len(self.default_liststore_columns) + 1
)
- self.assertEqual(self.torrentview.column_index[-1], test_col)
- self.assertEqual(self.torrentview.columns[test_col].column_indices, [32])
+ assert len(self.torrentview.column_index) == len(self.default_column_index) + 1
+ assert self.torrentview.column_index[-1] == test_col
+ assert self.torrentview.columns[test_col].column_indices == [33]
def test_add_columns(self):
-
# Add a text column
test_col = 'Test column'
self.torrentview.add_text_column(test_col, status_field=['label'])
@@ -160,50 +141,35 @@ class TorrentviewTestCase(BaseTestCase):
test_col2 = 'Test column2'
self.torrentview.add_text_column(test_col2, status_field=['label2'])
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns) + 2,
- )
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index) + 2,
+ assert (
+ len(self.torrentview.liststore_columns)
+ == len(self.default_liststore_columns) + 2
)
+ assert len(self.torrentview.column_index) == len(self.default_column_index) + 2
# test_col
- self.assertEqual(self.torrentview.column_index[-2], test_col)
- self.assertEqual(self.torrentview.columns[test_col].column_indices, [32])
+ assert self.torrentview.column_index[-2] == test_col
+ assert self.torrentview.columns[test_col].column_indices == [33]
# test_col2
- self.assertEqual(self.torrentview.column_index[-1], test_col2)
- self.assertEqual(self.torrentview.columns[test_col2].column_indices, [33])
+ assert self.torrentview.column_index[-1] == test_col2
+ assert self.torrentview.columns[test_col2].column_indices == [34]
def test_remove_column(self):
-
# Add and remove text column
test_col = 'Test column'
self.torrentview.add_text_column(test_col, status_field=['label'])
self.torrentview.remove_column(test_col)
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns),
- )
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index),
- )
- self.assertEqual(
- self.torrentview.column_index[-1],
- TorrentviewTestCase.default_column_index[-1],
- )
- self.assertEqual(
- self.torrentview.columns[
- TorrentviewTestCase.default_column_index[-1]
- ].column_indices,
- [31],
+ assert len(self.torrentview.liststore_columns) == len(
+ self.default_liststore_columns
)
+ assert len(self.torrentview.column_index) == len(self.default_column_index)
+ assert self.torrentview.column_index[-1] == self.default_column_index[-1]
+ assert self.torrentview.columns[
+ self.default_column_index[-1]
+ ].column_indices == [32]
def test_remove_columns(self):
-
# Add two columns
test_col = 'Test column'
self.torrentview.add_text_column(test_col, status_field=['label'])
@@ -212,74 +178,47 @@ class TorrentviewTestCase(BaseTestCase):
# Remove test_col
self.torrentview.remove_column(test_col)
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns) + 1,
+ assert (
+ len(self.torrentview.liststore_columns)
+ == len(self.default_liststore_columns) + 1
)
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index) + 1,
- )
- self.assertEqual(self.torrentview.column_index[-1], test_col2)
- self.assertEqual(self.torrentview.columns[test_col2].column_indices, [32])
+ assert len(self.torrentview.column_index) == len(self.default_column_index) + 1
+ assert self.torrentview.column_index[-1] == test_col2
+ assert self.torrentview.columns[test_col2].column_indices == [33]
# Remove test_col2
self.torrentview.remove_column(test_col2)
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns),
- )
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index),
- )
- self.assertEqual(
- self.torrentview.column_index[-1],
- TorrentviewTestCase.default_column_index[-1],
- )
- self.assertEqual(
- self.torrentview.columns[
- TorrentviewTestCase.default_column_index[-1]
- ].column_indices,
- [31],
+ assert len(self.torrentview.liststore_columns) == len(
+ self.default_liststore_columns
)
+ assert len(self.torrentview.column_index) == len(self.default_column_index)
+ assert self.torrentview.column_index[-1] == self.default_column_index[-1]
+ assert self.torrentview.columns[
+ self.default_column_index[-1]
+ ].column_indices == [32]
def test_add_remove_column_multiple_types(self):
-
# Add a column with multiple column types
test_col3 = 'Test column3'
self.torrentview.add_progress_column(
test_col3, status_field=['progress', 'label3'], col_types=[float, str]
)
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns) + 2,
- )
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index) + 1,
+ assert (
+ len(self.torrentview.liststore_columns)
+ == len(self.default_liststore_columns) + 2
)
- self.assertEqual(self.torrentview.column_index[-1], test_col3)
- self.assertEqual(self.torrentview.columns[test_col3].column_indices, [32, 33])
+ assert len(self.torrentview.column_index) == len(self.default_column_index) + 1
+ assert self.torrentview.column_index[-1] == test_col3
+ assert self.torrentview.columns[test_col3].column_indices == [33, 34]
# Remove multiple column-types column
self.torrentview.remove_column(test_col3)
- self.assertEqual(
- len(self.torrentview.liststore_columns),
- len(TorrentviewTestCase.default_liststore_columns),
- )
- self.assertEqual(
- len(self.torrentview.column_index),
- len(TorrentviewTestCase.default_column_index),
- )
- self.assertEqual(
- self.torrentview.column_index[-1],
- TorrentviewTestCase.default_column_index[-1],
- )
- self.assertEqual(
- self.torrentview.columns[
- TorrentviewTestCase.default_column_index[-1]
- ].column_indices,
- [31],
+ assert len(self.torrentview.liststore_columns) == len(
+ self.default_liststore_columns
)
+ assert len(self.torrentview.column_index) == len(self.default_column_index)
+ assert self.torrentview.column_index[-1] == self.default_column_index[-1]
+ assert self.torrentview.columns[
+ self.default_column_index[-1]
+ ].column_indices == [32]
diff --git a/deluge/tests/test_tracker_icons.py b/deluge/tests/test_tracker_icons.py
index e18d33987..57cc13822 100644
--- a/deluge/tests/test_tracker_icons.py
+++ b/deluge/tests/test_tracker_icons.py
@@ -1,34 +1,24 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-
-from __future__ import unicode_literals
+import os.path
import pytest
-from twisted.trial.unittest import SkipTest
import deluge.component as component
import deluge.ui.tracker_icons
-from deluge.common import windows_check
+from deluge.conftest import BaseTestCase
from deluge.ui.tracker_icons import TrackerIcon, TrackerIcons
from . import common
-from .basetest import BaseTestCase
-common.set_tmp_config_dir()
-deluge.ui.tracker_icons.PIL_INSTALLED = False
common.disable_new_release_check()
@pytest.mark.internet
-class TrackerIconsTestCase(BaseTestCase):
-
- if windows_check():
- skip = 'cannot use os.path.samefile to compair on windows(unix only)'
-
+class TestTrackerIcons(BaseTestCase):
def set_up(self):
# Disable resizing with Pillow for consistency.
self.patch(deluge.ui.tracker_icons, 'Image', None)
@@ -37,41 +27,45 @@ class TrackerIconsTestCase(BaseTestCase):
def tear_down(self):
return component.shutdown()
- def test_get_deluge_png(self):
+ async def test_get_deluge_png(self, mock_mkstemp):
# Deluge has a png favicon link
icon = TrackerIcon(common.get_test_data_file('deluge.png'))
- d = self.icons.fetch('deluge-torrent.org')
- d.addCallback(self.assertNotIdentical, None)
- d.addCallback(self.assertEqual, icon)
- return d
+ result = await self.icons.fetch('deluge-torrent.org')
+ assert result == icon
+ assert not os.path.isfile(mock_mkstemp[1])
- def test_get_google_ico(self):
+ async def test_get_google_ico(self):
# Google doesn't have any icon links
# So instead we'll grab its favicon.ico
icon = TrackerIcon(common.get_test_data_file('google.ico'))
- d = self.icons.fetch('www.google.com')
- d.addCallback(self.assertNotIdentical, None)
- d.addCallback(self.assertEqual, icon)
- return d
+ result = await self.icons.fetch('www.google.com')
+ assert result == icon
- def test_get_google_ico_with_redirect(self):
+ async def test_get_google_ico_hebrew(self):
+ """Test that Google.co.il page is read as UTF-8"""
+ icon = TrackerIcon(common.get_test_data_file('google.ico'))
+ result = await self.icons.fetch('www.google.co.il')
+ assert result == icon
+
+ async def test_get_google_ico_with_redirect(self):
# google.com redirects to www.google.com
icon = TrackerIcon(common.get_test_data_file('google.ico'))
- d = self.icons.fetch('google.com')
- d.addCallback(self.assertNotIdentical, None)
- d.addCallback(self.assertEqual, icon)
- return d
+ result = await self.icons.fetch('google.com')
+ assert result == icon
- def test_get_seo_ico_with_sni(self):
+ @pytest.mark.skip(reason='Site removed favicon, new SNI test will be needed')
+ async def test_get_seo_svg_with_sni(self):
# seo using certificates with SNI support only
- raise SkipTest('Site certificate expired')
- icon = TrackerIcon(common.get_test_data_file('seo.ico'))
- d = self.icons.fetch('www.seo.com')
- d.addCallback(self.assertNotIdentical, None)
- d.addCallback(self.assertEqual, icon)
- return d
+ icon = TrackerIcon(common.get_test_data_file('seo.svg'))
+ result = await self.icons.fetch('www.seo.com')
+ assert result == icon
+
+ async def test_get_empty_string_tracker(self):
+ result = await self.icons.fetch('')
+ assert result is None
- def test_get_empty_string_tracker(self):
- d = self.icons.fetch('')
- d.addCallback(self.assertIdentical, None)
- return d
+ async def test_invalid_host(self, mock_mkstemp):
+ """Test that TrackerIcon can handle invalid hostname"""
+ result = await self.icons.fetch('deluge.example.com')
+ assert not result
+ assert not os.path.isfile(mock_mkstemp[1])
diff --git a/deluge/tests/test_transfer.py b/deluge/tests/test_transfer.py
index a04830325..92e349b5d 100644
--- a/deluge/tests/test_transfer.py
+++ b/deluge/tests/test_transfer.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2012 Bro <bro.development@gmail.com>
#
@@ -7,12 +6,10 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import base64
+import pytest
import rencode
-from twisted.trial import unittest
import deluge.log
from deluge.transfer import DelugeTransferProtocol
@@ -112,8 +109,9 @@ class TransferTestClass(DelugeTransferProtocol):
self.message_received(request)
-class DelugeTransferProtocolTestCase(unittest.TestCase):
- def setUp(self): # NOQA: N803
+class TestDelugeTransferProtocol:
+ @pytest.fixture(autouse=True)
+ def set_up(self):
"""
The expected messages corresponds to the test messages (msg1, msg2) after they've been processed
by DelugeTransferProtocol.send, which means that they've first been encoded with rencode,
@@ -160,7 +158,7 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
# Get the data as sent by DelugeTransferProtocol
messages = self.transfer.get_messages_out_joined()
base64_encoded = base64.b64encode(messages)
- self.assertEqual(base64_encoded, self.msg1_expected_compressed_base64)
+ assert base64_encoded == self.msg1_expected_compressed_base64
def test_receive_one_message(self):
"""
@@ -173,7 +171,7 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
)
# Get the data as sent by DelugeTransferProtocol
messages = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(messages))
+ assert rencode.dumps(self.msg1) == rencode.dumps(messages)
def test_receive_old_message(self):
"""
@@ -181,9 +179,9 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
"""
self.transfer.dataReceived(rencode.dumps(self.msg1))
- self.assertEqual(len(self.transfer.get_messages_in()), 0)
- self.assertEqual(self.transfer._message_length, 0)
- self.assertEqual(len(self.transfer._buffer), 0)
+ assert len(self.transfer.get_messages_in()) == 0
+ assert self.transfer._message_length == 0
+ assert len(self.transfer._buffer) == 0
def test_receive_two_concatenated_messages(self):
"""
@@ -198,9 +196,9 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
# Get the data as sent by DelugeTransferProtocol
message1 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(message1))
+ assert rencode.dumps(self.msg1) == rencode.dumps(message1)
message2 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg2), rencode.dumps(message2))
+ assert rencode.dumps(self.msg2) == rencode.dumps(message2)
def test_receive_three_messages_in_parts(self):
"""
@@ -237,20 +235,17 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
else:
expected_msgs_received_count = 0
# Verify that the expected number of complete messages has arrived
- self.assertEqual(
- expected_msgs_received_count, len(self.transfer.get_messages_in())
- )
+ assert expected_msgs_received_count == len(self.transfer.get_messages_in())
# Get the data as received by DelugeTransferProtocol
message1 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(message1))
+ assert rencode.dumps(self.msg1) == rencode.dumps(message1)
message2 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg2), rencode.dumps(message2))
+ assert rencode.dumps(self.msg2) == rencode.dumps(message2)
message3 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(message3))
+ assert rencode.dumps(self.msg1) == rencode.dumps(message3)
# Remove underscore to enable test, or run the test directly:
- # tests $ trial test_transfer.DelugeTransferProtocolTestCase._test_rencode_fail_protocol
def _test_rencode_fail_protocol(self):
"""
This test tries to test the protocol that relies on errors from rencode.
@@ -317,11 +312,11 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
# Get the data as received by DelugeTransferProtocol
message1 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(message1))
+ assert rencode.dumps(self.msg1) == rencode.dumps(message1)
message2 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg2), rencode.dumps(message2))
+ assert rencode.dumps(self.msg2) == rencode.dumps(message2)
message3 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(message3))
+ assert rencode.dumps(self.msg1) == rencode.dumps(message3)
def test_receive_middle_of_header(self):
"""
@@ -344,19 +339,19 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
self.transfer.dataReceived(two_concatenated[: first_len + 2])
# Should be 1 message in the list
- self.assertEqual(1, len(self.transfer.get_messages_in()))
+ assert 1 == len(self.transfer.get_messages_in())
# Send the rest
self.transfer.dataReceived(two_concatenated[first_len + 2 :])
# Should be 2 messages in the list
- self.assertEqual(2, len(self.transfer.get_messages_in()))
+ assert 2 == len(self.transfer.get_messages_in())
# Get the data as sent by DelugeTransferProtocol
message1 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(message1))
+ assert rencode.dumps(self.msg1) == rencode.dumps(message1)
message2 = self.transfer.get_messages_in().pop(0)
- self.assertEqual(rencode.dumps(self.msg2), rencode.dumps(message2))
+ assert rencode.dumps(self.msg2) == rencode.dumps(message2)
# Needs file containing big data structure e.g. like thetorrent list as it is transfered by the daemon
# def test_simulate_big_transfer(self):
diff --git a/deluge/tests/test_ui_common.py b/deluge/tests/test_ui_common.py
index b0c311183..87a4a2c04 100644
--- a/deluge/tests/test_ui_common.py
+++ b/deluge/tests/test_ui_common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -6,30 +5,19 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-from six import assertCountEqual
-from twisted.trial import unittest
-
-from deluge.common import windows_check
from deluge.ui.common import TorrentInfo
from . import common
-class UICommonTestCase(unittest.TestCase):
- def setUp(self): # NOQA: N803
- pass
-
- def tearDown(self): # NOQA: N803
- pass
-
+class TestUICommon:
def test_hash_optional_single_file(self):
"""Ensure single file with `ed2k` and `sha1` keys are not in filetree output."""
filename = common.get_test_data_file('test.torrent')
files_tree = {'azcvsupdater_2.6.2.jar': (0, 307949, True)}
ti = TorrentInfo(filename, filetree=1)
- self.assertEqual(ti.files_tree, files_tree)
+ assert ti.files_tree == files_tree
files_tree2 = {
'contents': {
@@ -42,7 +30,7 @@ class UICommonTestCase(unittest.TestCase):
}
}
ti = TorrentInfo(filename, filetree=2)
- self.assertEqual(ti.files_tree, files_tree2)
+ assert ti.files_tree == files_tree2
def test_hash_optional_multi_file(self):
"""Ensure multi-file with `filehash` and `ed2k` are keys not in filetree output."""
@@ -54,9 +42,9 @@ class UICommonTestCase(unittest.TestCase):
}
}
ti = TorrentInfo(filename, filetree=1)
- self.assertEqual(ti.files_tree, files_tree)
+ assert ti.files_tree == files_tree
- filestree2 = {
+ files_tree2 = {
'contents': {
'torrent_filehash': {
'type': 'dir',
@@ -83,14 +71,14 @@ class UICommonTestCase(unittest.TestCase):
'type': 'dir',
}
ti = TorrentInfo(filename, filetree=2)
- self.assertEqual(ti.files_tree, filestree2)
+ assert ti.files_tree == files_tree2
def test_hash_optional_md5sum(self):
# Ensure `md5sum` key is not included in filetree output
filename = common.get_test_data_file('md5sum.torrent')
files_tree = {'test': {'lol': (0, 4, True), 'rofl': (1, 5, True)}}
ti = TorrentInfo(filename, filetree=1)
- self.assertEqual(ti.files_tree, files_tree)
+ assert ti.files_tree == files_tree
ti = TorrentInfo(filename, filetree=2)
files_tree2 = {
'contents': {
@@ -118,16 +106,14 @@ class UICommonTestCase(unittest.TestCase):
},
'type': 'dir',
}
- self.assertEqual(ti.files_tree, files_tree2)
+ assert ti.files_tree == files_tree2
def test_utf8_encoded_paths(self):
filename = common.get_test_data_file('test.torrent')
ti = TorrentInfo(filename)
- self.assertTrue('azcvsupdater_2.6.2.jar' in ti.files_tree)
+ assert 'azcvsupdater_2.6.2.jar' in ti.files_tree
def test_utf8_encoded_paths2(self):
- if windows_check():
- raise unittest.SkipTest('on windows KeyError: unicode_filenames')
filename = common.get_test_data_file('unicode_filenames.torrent')
filepath1 = '\u30c6\u30af\u30b9\u30fb\u30c6\u30af\u30b5\u30f3.mkv'
filepath2 = (
@@ -140,11 +126,11 @@ class UICommonTestCase(unittest.TestCase):
ti = TorrentInfo(filename)
files_tree = ti.files_tree['unicode_filenames']
- self.assertIn(filepath1, files_tree)
- self.assertIn(filepath2, files_tree)
- self.assertIn(filepath3, files_tree)
- self.assertIn(filepath4, files_tree)
- self.assertIn(filepath5, files_tree)
+ assert filepath1 in files_tree
+ assert filepath2 in files_tree
+ assert filepath3 in files_tree
+ assert filepath4 in files_tree
+ assert filepath5 in files_tree
result_files = [
{
@@ -170,4 +156,135 @@ class UICommonTestCase(unittest.TestCase):
{'download': True, 'path': 'unicode_filenames/' + filepath1, 'size': 1771},
]
- assertCountEqual(self, ti.files, result_files)
+ assert len(ti.files) == len(result_files)
+
+ def test_directory_with_single_file(self):
+ filename = common.get_test_data_file('dir_with_single_file.torrent')
+
+ ti = TorrentInfo(filename)
+ expected_file_tree = {'dir_with_single_file': {'single_file.txt': (0, 9, True)}}
+ assert ti.files_tree == expected_file_tree
+
+ result_files = [
+ {
+ 'path': 'dir_with_single_file/single_file.txt',
+ 'size': 9,
+ 'download': True,
+ }
+ ]
+ assert ti.files == result_files
+
+ def test_bittorrent_v2_path(self):
+ filename = common.get_test_data_file('v2_test.torrent')
+ files_tree = {
+ 'torrent_test': {
+ 'small.txt': (0, 22, True),
+ '還在一個人無聊嗎~還不趕緊上來聊天美.txt': (1, 32, True),
+ }
+ }
+ ti = TorrentInfo(filename, filetree=1)
+ assert ti.files_tree == files_tree
+
+ files_tree2 = {
+ 'contents': {
+ 'torrent_test': {
+ 'type': 'dir',
+ 'contents': {
+ 'small.txt': {
+ 'type': 'file',
+ 'path': 'torrent_test/small.txt',
+ 'length': 22,
+ 'index': 0,
+ 'download': True,
+ },
+ '還在一個人無聊嗎~還不趕緊上來聊天美.txt': {
+ 'type': 'file',
+ 'path': 'torrent_test/還在一個人無聊嗎~還不趕緊上來聊天美.txt',
+ 'length': 32,
+ 'index': 1,
+ 'download': True,
+ },
+ },
+ 'length': 54,
+ 'download': True,
+ }
+ },
+ 'type': 'dir',
+ }
+ ti = TorrentInfo(filename, filetree=2)
+ assert ti.files_tree == files_tree2
+
+ def test_bittorrent_v2_hybrid_path(self):
+ filename = common.get_test_data_file('v2_hybrid.torrent')
+ files_tree = {
+ 'torrent_test': {
+ 'small.txt': (0, 22, True),
+ '還在一個人無聊嗎~還不趕緊上來聊天美.txt': (2, 32, True),
+ '.pad': {
+ '16362': (1, 16362, True),
+ '16352': (3, 16352, True),
+ },
+ }
+ }
+ ti = TorrentInfo(filename, filetree=1, force_bt_version=1)
+ assert ti.files_tree == files_tree
+ del files_tree['torrent_test']['.pad']
+ files_tree['torrent_test']['還在一個人無聊嗎~還不趕緊上來聊天美.txt'] = (1, 32, True)
+ ti = TorrentInfo(filename, filetree=1, force_bt_version=2)
+ assert ti.files_tree == files_tree
+
+ files_tree2 = {
+ 'contents': {
+ 'torrent_test': {
+ 'type': 'dir',
+ 'contents': {
+ 'small.txt': {
+ 'type': 'file',
+ 'path': 'torrent_test/small.txt',
+ 'length': 22,
+ 'index': 0,
+ 'download': True,
+ },
+ '還在一個人無聊嗎~還不趕緊上來聊天美.txt': {
+ 'type': 'file',
+ 'path': 'torrent_test/還在一個人無聊嗎~還不趕緊上來聊天美.txt',
+ 'length': 32,
+ 'index': 2,
+ 'download': True,
+ },
+ '.pad': {
+ 'type': 'dir',
+ 'contents': {
+ '16362': {
+ 'type': 'file',
+ 'path': 'torrent_test/.pad/16362',
+ 'length': 16362,
+ 'index': 1,
+ 'download': True,
+ },
+ '16352': {
+ 'type': 'file',
+ 'path': 'torrent_test/.pad/16352',
+ 'length': 16352,
+ 'index': 3,
+ 'download': True,
+ },
+ },
+ 'length': 32714,
+ 'download': True,
+ },
+ },
+ 'length': 32768,
+ 'download': True,
+ }
+ },
+ 'type': 'dir',
+ }
+ ti = TorrentInfo(filename, filetree=2, force_bt_version=1)
+ assert ti.files_tree == files_tree2
+ torrent_test = files_tree2['contents']['torrent_test']
+ torrent_test['length'] -= torrent_test['contents']['.pad']['length']
+ del torrent_test['contents']['.pad']
+ torrent_test['contents']['還在一個人無聊嗎~還不趕緊上來聊天美.txt']['index'] = 1
+ ti = TorrentInfo(filename, filetree=2, force_bt_version=2)
+ assert ti.files_tree == files_tree2
diff --git a/deluge/tests/test_ui_console.py b/deluge/tests/test_ui_console.py
index 3667c608e..34398ee19 100644
--- a/deluge/tests/test_ui_console.py
+++ b/deluge/tests/test_ui_console.py
@@ -1,35 +1,30 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import argparse
+import pytest
+
from deluge.ui.console.cmdline.commands.add import Command
from deluge.ui.console.cmdline.commands.config import json_eval
from deluge.ui.console.widgets.fields import TextInput
-from .basetest import BaseTestCase
-
-class MockParent(object):
+class MockParent:
def __init__(self):
self.border_off_x = 1
self.pane_width = 20
self.encoding = 'utf8'
-class UIConsoleFieldTestCase(BaseTestCase):
- def setUp(self): # NOQA: N803
+class TestUIConsoleField:
+ @pytest.fixture(autouse=True)
+ def set_up(self):
self.parent = MockParent()
- def tearDown(self): # NOQA: N803
- pass
-
def test_text_input(self):
def move_func(self, r, c):
self._cursor_row = r
@@ -44,48 +39,42 @@ class UIConsoleFieldTestCase(BaseTestCase):
'/text/field/file/path',
complete=False,
)
- self.assertTrue(t)
- self.assertTrue(t.handle_read(33))
-
-
-class UIConsoleCommandsTestCase(BaseTestCase):
- def setUp(self):
- pass
+ assert t
+ assert t.handle_read(33)
- def tearDown(self):
- pass
+class TestUIConsoleCommands:
def test_add_move_completed(self):
completed_path = 'completed_path'
parser = argparse.ArgumentParser()
cmd = Command()
cmd.add_arguments(parser)
args = parser.parse_args(['torrent', '-m', completed_path])
- self.assertEqual(args.move_completed_path, completed_path)
+ assert args.move_completed_path == completed_path
args = parser.parse_args(['torrent', '--move-path', completed_path])
- self.assertEqual(args.move_completed_path, completed_path)
+ assert args.move_completed_path == completed_path
def test_config_json_eval(self):
- self.assertEqual(json_eval('/downloads'), '/downloads')
- self.assertEqual(json_eval('/dir/with space'), '/dir/with space')
- self.assertEqual(json_eval('c:\\\\downloads'), 'c:\\\\downloads')
- self.assertEqual(json_eval('c:/downloads'), 'c:/downloads')
+ assert json_eval('/downloads') == '/downloads'
+ assert json_eval('/dir/with space') == '/dir/with space'
+ assert json_eval('c:\\\\downloads') == 'c:\\\\downloads'
+ assert json_eval('c:/downloads') == 'c:/downloads'
# Ensure newlines are split and only first setting is used.
- self.assertEqual(json_eval('setting\nwithneline'), 'setting')
+ assert json_eval('setting\nwithneline') == 'setting'
# Allow both parentheses and square brackets.
- self.assertEqual(json_eval('(8000, 8001)'), [8000, 8001])
- self.assertEqual(json_eval('[8000, 8001]'), [8000, 8001])
- self.assertEqual(json_eval('["abc", "def"]'), ['abc', 'def'])
- self.assertEqual(json_eval('{"foo": "bar"}'), {'foo': 'bar'})
- self.assertEqual(json_eval('{"number": 1234}'), {'number': 1234})
+ assert json_eval('(8000, 8001)') == [8000, 8001]
+ assert json_eval('[8000, 8001]') == [8000, 8001]
+ assert json_eval('["abc", "def"]') == ['abc', 'def']
+ assert json_eval('{"foo": "bar"}') == {'foo': 'bar'}
+ assert json_eval('{"number": 1234}') == {'number': 1234}
# Hex string for peer_tos.
- self.assertEqual(json_eval('0x00'), '0x00')
- self.assertEqual(json_eval('1000'), 1000)
- self.assertEqual(json_eval('-6'), -6)
- self.assertEqual(json_eval('10.5'), 10.5)
- self.assertEqual(json_eval('True'), True)
- self.assertEqual(json_eval('false'), False)
- self.assertEqual(json_eval('none'), None)
+ assert json_eval('0x00') == '0x00'
+ assert json_eval('1000') == 1000
+ assert json_eval('-6') == -6
+ assert json_eval('10.5') == 10.5
+ assert json_eval('True')
+ assert not json_eval('false')
+ assert json_eval('none') is None
# Empty values to clear config key.
- self.assertEqual(json_eval('[]'), [])
- self.assertEqual(json_eval(''), '')
+ assert json_eval('[]') == []
+ assert json_eval('') == ''
diff --git a/deluge/tests/test_ui_entry.py b/deluge/tests/test_ui_entry.py
index f85bc7d7d..9a1330ed5 100644
--- a/deluge/tests/test_ui_entry.py
+++ b/deluge/tests/test_ui_entry.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,14 +6,13 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import argparse
import sys
from io import StringIO
+from unittest import mock
-import mock
import pytest
+import pytest_twisted
from twisted.internet import defer
import deluge
@@ -23,12 +21,12 @@ import deluge.ui.console
import deluge.ui.console.cmdline.commands.quit
import deluge.ui.console.main
import deluge.ui.web.server
-from deluge.common import PY2, get_localhost_auth, windows_check
+from deluge.common import get_localhost_auth, windows_check
+from deluge.conftest import BaseTestCase
from deluge.ui import ui_entry
from deluge.ui.web.server import DelugeWeb
from . import common
-from .basetest import BaseTestCase
from .daemon_base import DaemonBase
DEBUG_COMMAND = False
@@ -41,7 +39,7 @@ sys_stdout = sys.stdout
# To print to terminal from the tests, use: print('Message...', file=sys_stdout)
-class StringFileDescriptor(object):
+class StringFileDescriptor:
"""File descriptor that writes to string buffer"""
def __init__(self, fd):
@@ -51,23 +49,16 @@ class StringFileDescriptor(object):
setattr(self, a, getattr(sys_stdout, a))
def write(self, *data, **kwargs):
- # io.StringIO requires unicode strings.
data_string = str(*data)
- if PY2:
- data_string = data_string.decode()
print(data_string, file=self.out, end='')
def flush(self):
self.out.flush()
-class UIBaseTestCase(object):
- def __init__(self):
- self.var = {}
-
+class UIBaseTestCase:
def set_up(self):
- common.set_tmp_config_dir()
- common.setup_test_logger(level='info', prefix=self.id())
+ common.setup_test_logger(level='info', prefix=self.config_dir / self.id())
return component.start()
def tear_down(self):
@@ -82,28 +73,14 @@ class UIBaseTestCase(object):
class UIWithDaemonBaseTestCase(UIBaseTestCase, DaemonBase):
"""Subclass for test that require a deluged daemon"""
- def __init__(self):
- UIBaseTestCase.__init__(self)
-
def set_up(self):
d = self.common_set_up()
- common.setup_test_logger(level='info', prefix=self.id())
- d.addCallback(self.start_core)
- return d
-
- def tear_down(self):
- d = UIBaseTestCase.tear_down(self)
- d.addCallback(self.terminate_core)
+ common.setup_test_logger(level='info', prefix=self.config_dir / self.id())
return d
-class DelugeEntryTestCase(BaseTestCase):
-
- if windows_check():
- skip = 'Console ui test on Windows broken due to sys args issue'
-
+class TestDelugeEntry(BaseTestCase):
def set_up(self):
- common.set_tmp_config_dir()
return component.start()
def tear_down(self):
@@ -119,10 +96,11 @@ class DelugeEntryTestCase(BaseTestCase):
self.patch(argparse._sys, 'stdout', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
- self.assertRaises(SystemExit, ui_entry.start_ui)
- self.assertTrue('usage: deluge' in fd.out.getvalue())
- self.assertTrue('UI Options:' in fd.out.getvalue())
- self.assertTrue('* console' in fd.out.getvalue())
+ with pytest.raises(SystemExit):
+ ui_entry.start_ui()
+ assert 'usage: deluge' in fd.out.getvalue()
+ assert 'UI Options:' in fd.out.getvalue()
+ assert '* console' in fd.out.getvalue()
def test_start_default(self):
self.patch(sys, 'argv', ['./deluge'])
@@ -157,7 +135,7 @@ class DelugeEntryTestCase(BaseTestCase):
# Just test that no exception is raised
ui_entry.start_ui()
- self.assertEqual(_level[0], 'info')
+ assert _level[0] == 'info'
class GtkUIBaseTestCase(UIBaseTestCase):
@@ -173,38 +151,27 @@ class GtkUIBaseTestCase(UIBaseTestCase):
@pytest.mark.gtkui
-class GtkUIDelugeScriptEntryTestCase(BaseTestCase, GtkUIBaseTestCase):
- def __init__(self, testname):
- super(GtkUIDelugeScriptEntryTestCase, self).__init__(testname)
- GtkUIBaseTestCase.__init__(self)
-
- self.var['cmd_name'] = 'deluge gtk'
- self.var['start_cmd'] = ui_entry.start_ui
- self.var['sys_arg_cmd'] = ['./deluge', 'gtk']
-
- def set_up(self):
- return GtkUIBaseTestCase.set_up(self)
-
- def tear_down(self):
- return GtkUIBaseTestCase.tear_down(self)
+class TestGtkUIDelugeScriptEntry(BaseTestCase, GtkUIBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
+ request.cls.var = {
+ 'cmd_name': 'deluge gtk',
+ 'start_cmd': ui_entry.start_ui,
+ 'sys_arg_cmd': ['./deluge', 'gtk'],
+ }
@pytest.mark.gtkui
-class GtkUIScriptEntryTestCase(BaseTestCase, GtkUIBaseTestCase):
- def __init__(self, testname):
- super(GtkUIScriptEntryTestCase, self).__init__(testname)
- GtkUIBaseTestCase.__init__(self)
+class TestGtkUIScriptEntry(BaseTestCase, GtkUIBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
from deluge.ui import gtk3
- self.var['cmd_name'] = 'deluge-gtk'
- self.var['start_cmd'] = gtk3.start
- self.var['sys_arg_cmd'] = ['./deluge-gtk']
-
- def set_up(self):
- return GtkUIBaseTestCase.set_up(self)
-
- def tear_down(self):
- return GtkUIBaseTestCase.tear_down(self)
+ request.cls.var = {
+ 'cmd_name': 'deluge-gtk',
+ 'start_cmd': gtk3.start,
+ 'sys_arg_cmd': ['./deluge-gtk'],
+ }
class DelugeWebMock(DelugeWeb):
@@ -242,45 +209,31 @@ class WebUIBaseTestCase(UIBaseTestCase):
self.patch(deluge.ui.web.server, 'DelugeWeb', DelugeWebMock)
self.exec_command()
- self.assertEqual(_level[0], 'info')
-
-
-class WebUIScriptEntryTestCase(BaseTestCase, WebUIBaseTestCase):
-
- if windows_check():
- skip = 'Console ui test on Windows broken due to sys args issue'
-
- def __init__(self, testname):
- super(WebUIScriptEntryTestCase, self).__init__(testname)
- WebUIBaseTestCase.__init__(self)
- self.var['cmd_name'] = 'deluge-web'
- self.var['start_cmd'] = deluge.ui.web.start
- self.var['sys_arg_cmd'] = ['./deluge-web', '--do-not-daemonize']
-
- def set_up(self):
- return WebUIBaseTestCase.set_up(self)
-
- def tear_down(self):
- return WebUIBaseTestCase.tear_down(self)
-
+ assert _level[0] == 'info'
-class WebUIDelugeScriptEntryTestCase(BaseTestCase, WebUIBaseTestCase):
- if windows_check():
- skip = 'Console ui test on Windows broken due to sys args issue'
+class TestWebUIScriptEntry(BaseTestCase, WebUIBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
+ request.cls.var = {
+ 'cmd_name': 'deluge-web',
+ 'start_cmd': deluge.ui.web.start,
+ 'sys_arg_cmd': ['./deluge-web'],
+ }
+ if not windows_check():
+ request.cls.var['sys_arg_cmd'].append('--do-not-daemonize')
- def __init__(self, testname):
- super(WebUIDelugeScriptEntryTestCase, self).__init__(testname)
- WebUIBaseTestCase.__init__(self)
- self.var['cmd_name'] = 'deluge web'
- self.var['start_cmd'] = ui_entry.start_ui
- self.var['sys_arg_cmd'] = ['./deluge', 'web', '--do-not-daemonize']
-
- def set_up(self):
- return WebUIBaseTestCase.set_up(self)
- def tear_down(self):
- return WebUIBaseTestCase.tear_down(self)
+class TestWebUIDelugeScriptEntry(BaseTestCase, WebUIBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
+ request.cls.var = {
+ 'cmd_name': 'deluge web',
+ 'start_cmd': ui_entry.start_ui,
+ 'sys_arg_cmd': ['./deluge', 'web'],
+ }
+ if not windows_check():
+ request.cls.var['sys_arg_cmd'].append('--do-not-daemonize')
class ConsoleUIBaseTestCase(UIBaseTestCase):
@@ -291,7 +244,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
with mock.patch('deluge.ui.console.main.ConsoleUI'):
self.exec_command()
- def test_start_console_with_log_level(self):
+ def test_start_console_with_log_level(self, request):
_level = []
def setup_logger(
@@ -314,7 +267,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
# Just test that no exception is raised
self.exec_command()
- self.assertEqual(_level[0], 'info')
+ assert _level[0] == 'info'
def test_console_help(self):
self.patch(sys, 'argv', self.var['sys_arg_cmd'] + ['-h'])
@@ -322,18 +275,19 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
self.patch(argparse._sys, 'stdout', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
- self.assertRaises(SystemExit, self.exec_command)
+ with pytest.raises(SystemExit):
+ self.exec_command()
std_output = fd.out.getvalue()
- self.assertTrue(
- ('usage: %s' % self.var['cmd_name']) in std_output
- ) # Check command name
- self.assertTrue('Common Options:' in std_output)
- self.assertTrue('Console Options:' in std_output)
- self.assertIn(
- 'Console Commands:\n The following console commands are available:',
- std_output,
+ assert (
+ 'usage: %s' % self.var['cmd_name']
+ ) in std_output # Check command name
+ assert 'Common Options:' in std_output
+ assert 'Console Options:' in std_output
+ assert (
+ 'Console Commands:\n The following console commands are available:'
+ in std_output
)
- self.assertIn('The following console commands are available:', std_output)
+ assert 'The following console commands are available:' in std_output
def test_console_command_info(self):
self.patch(sys, 'argv', self.var['sys_arg_cmd'] + ['info'])
@@ -349,10 +303,11 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
self.patch(argparse._sys, 'stdout', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
- self.assertRaises(SystemExit, self.exec_command)
+ with pytest.raises(SystemExit):
+ self.exec_command()
std_output = fd.out.getvalue()
- self.assertIn('usage: info', std_output)
- self.assertIn('Show information about the torrents', std_output)
+ assert 'usage: info' in std_output
+ assert 'Show information about the torrents' in std_output
def test_console_unrecognized_arguments(self):
self.patch(
@@ -361,8 +316,9 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
fd = StringFileDescriptor(sys.stdout)
self.patch(argparse._sys, 'stderr', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
- self.assertRaises(SystemExit, self.exec_command)
- self.assertIn('unrecognized arguments: --ui', fd.out.getvalue())
+ with pytest.raises(SystemExit):
+ self.exec_command()
+ assert 'unrecognized arguments: --ui' in fd.out.getvalue()
class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
@@ -390,26 +346,28 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
+ command,
)
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_console_command_add(self):
filename = common.get_test_data_file('test.torrent')
- self.patch_arg_command(['add ' + filename])
+ self.patch_arg_command([f'add "{filename}"'])
fd = StringFileDescriptor(sys.stdout)
self.patch(sys, 'stdout', fd)
yield self.exec_command()
std_output = fd.out.getvalue()
- self.assertEqual(
- std_output, 'Attempting to add torrent: ' + filename + '\nTorrent added!\n'
+ assert (
+ std_output
+ == 'Attempting to add torrent: ' + filename + '\nTorrent added!\n'
)
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_console_command_add_move_completed(self):
filename = common.get_test_data_file('test.torrent')
+ tmp_path = 'c:\\tmp' if windows_check() else '/tmp'
self.patch_arg_command(
[
- 'add --move-path /tmp ' + filename + ' ; status'
+ f'add --move-path "{tmp_path}" "{filename}" ; status'
' ; manage'
' ab570cdd5a17ea1b61e970bb72047de141bce173'
' move_completed'
@@ -422,22 +380,22 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
yield self.exec_command()
std_output = fd.out.getvalue()
- self.assertTrue(
- std_output.endswith('move_completed: True\nmove_completed_path: /tmp\n')
- or std_output.endswith('move_completed_path: /tmp\nmove_completed: True\n')
+ assert std_output.endswith(
+ f'move_completed: True\nmove_completed_path: {tmp_path}\n'
+ ) or std_output.endswith(
+ f'move_completed_path: {tmp_path}\nmove_completed: True\n'
)
- @defer.inlineCallbacks
- def test_console_command_status(self):
+ async def test_console_command_status(self):
fd = StringFileDescriptor(sys.stdout)
self.patch_arg_command(['status'])
self.patch(sys, 'stdout', fd)
- yield self.exec_command()
+ await self.exec_command()
std_output = fd.out.getvalue()
- self.assertTrue(std_output.startswith('Total upload: '))
- self.assertTrue(std_output.endswith(' Moving: 0\n'))
+ assert std_output.startswith('Total upload: ')
+ assert std_output.endswith(' Moving: 0\n')
@defer.inlineCallbacks
def test_console_command_config_set_download_location(self):
@@ -447,79 +405,36 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
yield self.exec_command()
std_output = fd.out.getvalue()
- self.assertTrue(
- std_output.startswith(
- 'Setting "download_location" to: {}\'/downloads\''.format(
- 'u' if PY2 else ''
- )
- )
- )
- self.assertTrue(
- std_output.endswith('Configuration value successfully updated.\n')
- )
-
-
-class ConsoleScriptEntryWithDaemonTestCase(
- BaseTestCase, ConsoleUIWithDaemonBaseTestCase
-):
-
- if windows_check():
- skip = 'Console ui test on Windows broken due to sys args issue'
-
- def __init__(self, testname):
- super(ConsoleScriptEntryWithDaemonTestCase, self).__init__(testname)
- ConsoleUIWithDaemonBaseTestCase.__init__(self)
- self.var['cmd_name'] = 'deluge-console'
- self.var['sys_arg_cmd'] = ['./deluge-console']
-
- def set_up(self):
- from deluge.ui.console.console import Console
-
- def start_console():
- return Console().start()
-
- self.patch(deluge.ui.console, 'start', start_console)
- self.var['start_cmd'] = deluge.ui.console.start
-
- return ConsoleUIWithDaemonBaseTestCase.set_up(self)
-
- def tear_down(self):
- return ConsoleUIWithDaemonBaseTestCase.tear_down(self)
-
-
-class ConsoleScriptEntryTestCase(BaseTestCase, ConsoleUIBaseTestCase):
-
- if windows_check():
- skip = 'Console ui test on Windows broken due to sys args issue'
-
- def __init__(self, testname):
- super(ConsoleScriptEntryTestCase, self).__init__(testname)
- ConsoleUIBaseTestCase.__init__(self)
- self.var['cmd_name'] = 'deluge-console'
- self.var['start_cmd'] = deluge.ui.console.start
- self.var['sys_arg_cmd'] = ['./deluge-console']
-
- def set_up(self):
- return ConsoleUIBaseTestCase.set_up(self)
-
- def tear_down(self):
- return ConsoleUIBaseTestCase.tear_down(self)
-
-
-class ConsoleDelugeScriptEntryTestCase(BaseTestCase, ConsoleUIBaseTestCase):
-
- if windows_check():
- skip = 'cannot test console ui on windows'
-
- def __init__(self, testname):
- super(ConsoleDelugeScriptEntryTestCase, self).__init__(testname)
- ConsoleUIBaseTestCase.__init__(self)
- self.var['cmd_name'] = 'deluge console'
- self.var['start_cmd'] = ui_entry.start_ui
- self.var['sys_arg_cmd'] = ['./deluge', 'console']
-
- def set_up(self):
- return ConsoleUIBaseTestCase.set_up(self)
-
- def tear_down(self):
- return ConsoleUIBaseTestCase.tear_down(self)
+ assert std_output.startswith('Setting "download_location" to: \'/downloads\'')
+ assert std_output.endswith('Configuration value successfully updated.\n')
+
+
+@pytest.mark.usefixtures('daemon', 'client')
+class TestConsoleScriptEntryWithDaemon(BaseTestCase, ConsoleUIWithDaemonBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
+ request.cls.var = {
+ 'cmd_name': 'deluge-console',
+ 'start_cmd': deluge.ui.console.test_start,
+ 'sys_arg_cmd': ['./deluge-console'],
+ }
+
+
+class TestConsoleScriptEntry(BaseTestCase, ConsoleUIBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
+ request.cls.var = {
+ 'cmd_name': 'deluge-console',
+ 'start_cmd': deluge.ui.console.start,
+ 'sys_arg_cmd': ['./deluge-console'],
+ }
+
+
+class TestConsoleDelugeScriptEntry(BaseTestCase, ConsoleUIBaseTestCase):
+ @pytest.fixture(autouse=True)
+ def set_var(self, request):
+ request.cls.var = {
+ 'cmd_name': 'deluge console',
+ 'start_cmd': ui_entry.start_ui,
+ 'sys_arg_cmd': ['./deluge', 'console'],
+ }
diff --git a/deluge/tests/test_ui_gtk3.py b/deluge/tests/test_ui_gtk3.py
index a208bb494..e6d025c7c 100644
--- a/deluge/tests/test_ui_gtk3.py
+++ b/deluge/tests/test_ui_gtk3.py
@@ -1,21 +1,17 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import sys
+from unittest import mock
-import mock
import pytest
-from twisted.trial import unittest
@pytest.mark.gtkui
-class GTK3CommonTestCase(unittest.TestCase):
+class TestGTK3Common:
def setUp(self):
sys.modules['gi.repository'] = mock.MagicMock()
@@ -25,10 +21,10 @@ class GTK3CommonTestCase(unittest.TestCase):
def test_cmp(self):
from deluge.ui.gtk3.common import cmp
- self.assertEqual(cmp(None, None), 0)
- self.assertEqual(cmp(1, None), 1)
- self.assertEqual(cmp(0, None), 1)
- self.assertEqual(cmp(None, 7), -1)
- self.assertEqual(cmp(None, 'bar'), -1)
- self.assertEqual(cmp('foo', None), 1)
- self.assertEqual(cmp('', None), 1)
+ assert cmp(None, None) == 0
+ assert cmp(1, None) == 1
+ assert cmp(0, None) == 1
+ assert cmp(None, 7) == -1
+ assert cmp(None, 'bar') == -1
+ assert cmp('foo', None) == 1
+ assert cmp('', None) == 1
diff --git a/deluge/tests/test_web_api.py b/deluge/tests/test_web_api.py
index 0180e0bda..814fecf8c 100644
--- a/deluge/tests/test_web_api.py
+++ b/deluge/tests/test_web_api.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,19 +6,17 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import json
from io import BytesIO
+import pytest
+import pytest_twisted
from twisted.internet import defer, reactor
-from twisted.python.failure import Failure
from twisted.web.client import Agent, FileBodyProducer
from twisted.web.http_headers import Headers
from twisted.web.static import File
import deluge.component as component
-from deluge.ui.client import client
from . import common
from .common_web import WebServerTestBase
@@ -27,20 +24,18 @@ from .common_web import WebServerTestBase
common.disable_new_release_check()
-class WebAPITestCase(WebServerTestBase):
- def test_connect_invalid_host(self):
- d = self.deluge_web.web_api.connect('id')
- d.addCallback(self.fail)
- d.addErrback(self.assertIsInstance, Failure)
- return d
+class TestWebAPI(WebServerTestBase):
+ @pytest.mark.xfail(reason='This just logs an error at the moment.')
+ async def test_connect_invalid_host(self):
+ with pytest.raises(Exception):
+ await self.deluge_web.web_api.connect('id')
- def test_connect(self):
+ def test_connect(self, client):
d = self.deluge_web.web_api.connect(self.host_id)
def on_connect(result):
- self.assertEqual(type(result), tuple)
- self.assertTrue(len(result) > 0)
- self.addCleanup(client.disconnect)
+ assert type(result) == tuple
+ assert len(result) > 0
return result
d.addCallback(on_connect)
@@ -52,9 +47,9 @@ class WebAPITestCase(WebServerTestBase):
@defer.inlineCallbacks
def on_connect(result):
- self.assertTrue(self.deluge_web.web_api.connected())
+ assert self.deluge_web.web_api.connected()
yield self.deluge_web.web_api.disconnect()
- self.assertFalse(self.deluge_web.web_api.connected())
+ assert not self.deluge_web.web_api.connected()
d.addCallback(on_connect)
d.addErrback(self.fail)
@@ -62,7 +57,7 @@ class WebAPITestCase(WebServerTestBase):
def test_get_config(self):
config = self.deluge_web.web_api.get_config()
- self.assertEqual(self.webserver_listen_port, config['port'])
+ assert self.deluge_web.port == config['port']
def test_set_config(self):
config = self.deluge_web.web_api.get_config()
@@ -77,9 +72,9 @@ class WebAPITestCase(WebServerTestBase):
}
self.deluge_web.web_api.set_config(config)
web_config = component.get('DelugeWeb').config.config
- self.assertNotEquals(config['pwd_salt'], web_config['pwd_salt'])
- self.assertNotEquals(config['pwd_sha1'], web_config['pwd_sha1'])
- self.assertNotEquals(config['sessions'], web_config['sessions'])
+ assert config['pwd_salt'] != web_config['pwd_salt']
+ assert config['pwd_sha1'] != web_config['pwd_sha1']
+ assert config['sessions'] != web_config['sessions']
@defer.inlineCallbacks
def get_host_status(self):
@@ -87,49 +82,49 @@ class WebAPITestCase(WebServerTestBase):
host[3] = 'Online'
host[4] = '2.0.0.dev562'
status = yield self.deluge_web.web_api.get_host_status(self.host_id)
- self.assertEqual(status, tuple(status))
+ assert status == tuple(status)
def test_get_host(self):
- self.assertFalse(self.deluge_web.web_api._get_host('invalid_id'))
+ assert not self.deluge_web.web_api._get_host('invalid_id')
conn = list(self.deluge_web.web_api.hostlist.get_hosts_info()[0])
- self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4])
+ assert self.deluge_web.web_api._get_host(conn[0]) == conn[0:4]
def test_add_host(self):
conn = ['abcdef', '10.0.0.1', 0, 'user123', 'pass123']
- self.assertFalse(self.deluge_web.web_api._get_host(conn[0]))
+ assert not self.deluge_web.web_api._get_host(conn[0])
# Add valid host
result, host_id = self.deluge_web.web_api.add_host(
conn[1], conn[2], conn[3], conn[4]
)
- self.assertEqual(result, True)
+ assert result
conn[0] = host_id
- self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4])
+ assert self.deluge_web.web_api._get_host(conn[0]) == conn[0:4]
# Add already existing host
ret = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
- self.assertEqual(ret, (False, 'Host details already in hostlist'))
+ assert ret == (False, 'Host details already in hostlist')
# Add invalid port
conn[2] = 'bad port'
ret = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
- self.assertEqual(ret, (False, 'Invalid port. Must be an integer'))
+ assert ret == (False, 'Invalid port. Must be an integer')
def test_remove_host(self):
conn = ['connection_id', '', 0, '', '']
self.deluge_web.web_api.hostlist.config['hosts'].append(conn)
- self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4])
+ assert self.deluge_web.web_api._get_host(conn[0]) == conn[0:4]
# Remove valid host
- self.assertTrue(self.deluge_web.web_api.remove_host(conn[0]))
- self.assertFalse(self.deluge_web.web_api._get_host(conn[0]))
+ assert self.deluge_web.web_api.remove_host(conn[0])
+ assert not self.deluge_web.web_api._get_host(conn[0])
# Remove non-existing host
- self.assertFalse(self.deluge_web.web_api.remove_host(conn[0]))
+ assert not self.deluge_web.web_api.remove_host(conn[0])
def test_get_torrent_info(self):
filename = common.get_test_data_file('test.torrent')
ret = self.deluge_web.web_api.get_torrent_info(filename)
- self.assertEqual(ret['name'], 'azcvsupdater_2.6.2.jar')
- self.assertEqual(ret['info_hash'], 'ab570cdd5a17ea1b61e970bb72047de141bce173')
- self.assertTrue('files_tree' in ret)
+ assert ret['name'] == 'azcvsupdater_2.6.2.jar'
+ assert ret['info_hash'] == 'ab570cdd5a17ea1b61e970bb72047de141bce173'
+ assert 'files_tree' in ret
def test_get_torrent_info_with_md5(self):
filename = common.get_test_data_file('md5sum.torrent')
@@ -137,19 +132,19 @@ class WebAPITestCase(WebServerTestBase):
# JSON dumping happens during response creation in normal usage
# JSON serialization may fail if any of the dictionary items are byte arrays rather than strings
ret = json.loads(json.dumps(ret))
- self.assertEqual(ret['name'], 'test')
- self.assertEqual(ret['info_hash'], 'f6408ba9944cf9fe01b547b28f336b3ee6ec32c5')
- self.assertTrue('files_tree' in ret)
+ assert ret['name'] == 'test'
+ assert ret['info_hash'] == 'f6408ba9944cf9fe01b547b28f336b3ee6ec32c5'
+ assert 'files_tree' in ret
def test_get_magnet_info(self):
ret = self.deluge_web.web_api.get_magnet_info(
'magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN'
)
- self.assertEqual(ret['name'], '953bad769164e8482c7785a21d12166f94b9e14d')
- self.assertEqual(ret['info_hash'], '953bad769164e8482c7785a21d12166f94b9e14d')
- self.assertTrue('files_tree' in ret)
+ assert ret['name'] == '953bad769164e8482c7785a21d12166f94b9e14d'
+ assert ret['info_hash'] == '953bad769164e8482c7785a21d12166f94b9e14d'
+ assert 'files_tree' in ret
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_get_torrent_files(self):
yield self.deluge_web.web_api.connect(self.host_id)
filename = common.get_test_data_file('test.torrent')
@@ -160,33 +155,30 @@ class WebAPITestCase(WebServerTestBase):
ret = yield self.deluge_web.web_api.get_torrent_files(
'ab570cdd5a17ea1b61e970bb72047de141bce173'
)
- self.assertEqual(ret['type'], 'dir')
- self.assertEqual(
- ret['contents'],
- {
- 'azcvsupdater_2.6.2.jar': {
- 'priority': 4,
- 'index': 0,
- 'offset': 0,
- 'progress': 0.0,
- 'path': 'azcvsupdater_2.6.2.jar',
- 'type': 'file',
- 'size': 307949,
- }
- },
- )
+ assert ret['type'] == 'dir'
+ assert ret['contents'] == {
+ 'azcvsupdater_2.6.2.jar': {
+ 'priority': 4,
+ 'index': 0,
+ 'offset': 0,
+ 'progress': 0.0,
+ 'path': 'azcvsupdater_2.6.2.jar',
+ 'type': 'file',
+ 'size': 307949,
+ }
+ }
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_download_torrent_from_url(self):
filename = 'ubuntu-9.04-desktop-i386.iso.torrent'
self.deluge_web.top_level.putChild(
filename.encode(), File(common.get_test_data_file(filename))
)
- url = 'http://localhost:%d/%s' % (self.webserver_listen_port, filename)
+ url = 'http://localhost:%d/%s' % (self.deluge_web.port, filename)
res = yield self.deluge_web.web_api.download_torrent_from_url(url)
- self.assertTrue(res.endswith(filename))
+ assert res.endswith(filename)
- @defer.inlineCallbacks
+ @pytest_twisted.inlineCallbacks
def test_invalid_json(self):
"""
If json_api._send_response does not return server.NOT_DONE_YET
@@ -198,7 +190,7 @@ class WebAPITestCase(WebServerTestBase):
bad_body = b'{ method": "auth.login" }'
d = yield agent.request(
b'POST',
- b'http://127.0.0.1:%i/json' % self.webserver_listen_port,
+ b'http://127.0.0.1:%i/json' % self.deluge_web.port,
Headers(
{
b'User-Agent': [b'Twisted Web Client Example'],
diff --git a/deluge/tests/test_web_auth.py b/deluge/tests/test_web_auth.py
index a5185737c..39d66c1c1 100644
--- a/deluge/tests/test_web_auth.py
+++ b/deluge/tests/test_web_auth.py
@@ -1,18 +1,15 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-from mock import patch
-from twisted.trial import unittest
+from unittest.mock import patch
from deluge.ui.web import auth
-class MockConfig(object):
+class MockConfig:
def __init__(self, config):
self.config = config
@@ -23,7 +20,7 @@ class MockConfig(object):
self.config[key] = value
-class WebAuthTestCase(unittest.TestCase):
+class TestWebAuth:
@patch('deluge.ui.web.auth.JSONComponent.__init__', return_value=None)
def test_change_password(self, mock_json):
config = MockConfig(
@@ -33,4 +30,4 @@ class WebAuthTestCase(unittest.TestCase):
}
)
webauth = auth.Auth(config)
- self.assertTrue(webauth.change_password('deluge', 'deluge_new'))
+ assert webauth.change_password('deluge', 'deluge_new')
diff --git a/deluge/tests/test_webserver.py b/deluge/tests/test_webserver.py
index d9684bacd..9503f506e 100644
--- a/deluge/tests/test_webserver.py
+++ b/deluge/tests/test_webserver.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,13 +6,12 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import json as json_lib
from io import BytesIO
+import pytest
import twisted.web.client
-from twisted.internet import defer, reactor
+from twisted.internet import reactor
from twisted.web.client import Agent, FileBodyProducer
from twisted.web.http_headers import Headers
@@ -24,10 +22,8 @@ from .common_web import WebServerMockBase, WebServerTestBase
common.disable_new_release_check()
-class WebServerTestCase(WebServerTestBase, WebServerMockBase):
- @defer.inlineCallbacks
- def test_get_torrent_info(self):
-
+class TestWebServer(WebServerTestBase, WebServerMockBase):
+ async def test_get_torrent_info(self):
agent = Agent(reactor)
self.mock_authentication_ignore(self.deluge_web.auth)
@@ -37,23 +33,76 @@ class WebServerTestCase(WebServerTestBase, WebServerMockBase):
# UnicodeDecodeError: 'utf8' codec can't decode byte 0xe5 in position 0: invalid continuation byte
filename = get_test_data_file('filehash_field.torrent')
input_file = (
- '{"params": ["%s"], "method": "web.get_torrent_info", "id": 22}' % filename
+ '{"params": ["%s"], "method": "web.get_torrent_info", "id": 22}'
+ % filename.replace('\\', '\\\\')
)
headers = {
b'User-Agent': ['Twisted Web Client Example'],
b'Content-Type': ['application/json'],
}
- url = 'http://127.0.0.1:%s/json' % self.webserver_listen_port
+ url = 'http://127.0.0.1:%s/json' % self.deluge_web.port
- d = yield agent.request(
+ response = await agent.request(
b'POST',
- url.encode('utf-8'),
+ url.encode(),
Headers(headers),
- FileBodyProducer(BytesIO(input_file.encode('utf-8'))),
+ FileBodyProducer(BytesIO(input_file.encode())),
)
+ body = await twisted.web.client.readBody(response)
+
+ try:
+ json = json_lib.loads(body.decode())
+ except Exception:
+ print('aoeu')
+ assert json['error'] is None
+ assert 'torrent_filehash' == json['result']['name']
+
+ @pytest.mark.parametrize('base', ['', '/', 'deluge'])
+ async def test_base_with_config(self, base):
+ agent = Agent(reactor)
+ root_url = f'http://127.0.0.1:{self.deluge_web.port}'
+ base_url = f'{root_url}/{base}'
+
+ self.deluge_web.base = base
+
+ response = await agent.request(b'GET', root_url.encode())
+ assert response.code == 200
+ body = await twisted.web.client.readBody(response)
+ assert 'Deluge WebUI' in body.decode()
+
+ response = await agent.request(b'GET', base_url.encode())
+ assert response.code == 200
+
+ @pytest.mark.parametrize('base', ['/', 'deluge'])
+ async def test_base_with_config_recurring_basepath(self, base):
+ agent = Agent(reactor)
+ base_url = f'http://127.0.0.1:{self.deluge_web.port}/{base}'
+
+ self.deluge_web.base = base
+
+ response = await agent.request(b'GET', base_url.encode())
+ assert response.code == 200
+
+ recursive_url = f'{base_url}/{base}'
+ response = await agent.request(b'GET', recursive_url.encode())
+ assert response.code == 404 if base.strip('/') else 200
+
+ recursive_url = f'{recursive_url}/{base}'
+ response = await agent.request(b'GET', recursive_url.encode())
+ assert response.code == 404 if base.strip('/') else 200
+
+ async def test_base_with_deluge_header(self):
+ """Ensure base path is set and HTML contains path"""
+ agent = Agent(reactor)
+ base = 'deluge'
+ url = f'http://127.0.0.1:{self.deluge_web.port}'
+ headers = Headers({'X-Deluge-Base': [base]})
- body = yield twisted.web.client.readBody(d)
+ response = await agent.request(b'GET', url.encode(), headers)
+ body = await twisted.web.client.readBody(response)
+ assert f'href="/{base}/' in body.decode()
- json = json_lib.loads(body.decode())
- self.assertEqual(None, json['error'])
- self.assertEqual('torrent_filehash', json['result']['name'])
+ # Header only changes HTML base path so ensure no resource at server path
+ url = f'{url}/{base}'
+ response = await agent.request(b'GET', url.encode(), headers)
+ assert response.code == 404
diff --git a/deluge/tests/twisted/plugins/delugereporter.py b/deluge/tests/twisted/plugins/delugereporter.py
deleted file mode 100644
index c2a7b52b5..000000000
--- a/deluge/tests/twisted/plugins/delugereporter.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
-# the additional special exception to link portions of this program with the OpenSSL library.
-# See LICENSE for more details.
-#
-
-from __future__ import unicode_literals
-
-import os
-
-from twisted.plugin import IPlugin
-from twisted.trial.itrial import IReporter
-from twisted.trial.reporter import TreeReporter
-from zope.interface import implements
-
-
-class _Reporter(object):
- implements(IPlugin, IReporter)
-
- def __init__(
- self, name, module, description, longOpt, shortOpt, klass # noqa: N803
- ):
- self.name = name
- self.module = module
- self.description = description
- self.longOpt = longOpt
- self.shortOpt = shortOpt
- self.klass = klass
-
-
-deluge = _Reporter(
- 'Deluge reporter that suppresses Stacktrace from TODO tests',
- 'twisted.plugins.delugereporter',
- description='Deluge Reporter',
- longOpt='deluge-reporter',
- shortOpt=None,
- klass='DelugeReporter',
-)
-
-
-class DelugeReporter(TreeReporter):
- def __init__(self, *args, **kwargs):
- os.environ['DELUGE_REPORTER'] = 'true'
- TreeReporter.__init__(self, *args, **kwargs)
-
- def addExpectedFailure(self, *args): # NOQA: N802
- # super(TreeReporter, self).addExpectedFailure(*args)
- self.endLine('[TODO]', self.TODO)
diff --git a/deluge/transfer.py b/deluge/transfer.py
index 6b26549e2..ed7d6dd9a 100644
--- a/deluge/transfer.py
+++ b/deluge/transfer.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2012 Bro <bro.development@gmail.com>
# Copyright (C) 2018 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import struct
import zlib
@@ -24,12 +21,12 @@ MESSAGE_HEADER_FORMAT = '!BI'
MESSAGE_HEADER_SIZE = struct.calcsize(MESSAGE_HEADER_FORMAT)
-class DelugeTransferProtocol(Protocol, object):
+class DelugeTransferProtocol(Protocol):
"""
Deluge RPC wire protocol.
- Data messages are transfered with a header containing a protocol version
- and the length of the data to be transfered (payload).
+ Data messages are transferred with a header containing a protocol version
+ and the length of the data to be transferred (payload).
The format is::
@@ -51,12 +48,12 @@ class DelugeTransferProtocol(Protocol, object):
"""
Transfer the data.
- :param data: data to be transfered in a data structure serializable by rencode.
+ :param data: data to be transferred in a data structure serializable by rencode.
"""
body = zlib.compress(rencode.dumps(data))
body_len = len(body)
message = struct.pack(
- '{}{}s'.format(MESSAGE_HEADER_FORMAT, body_len),
+ f'{MESSAGE_HEADER_FORMAT}{body_len}s',
PROTOCOL_VERSION,
body_len,
body,
@@ -68,8 +65,8 @@ class DelugeTransferProtocol(Protocol, object):
"""
This method is called whenever data is received.
- :param data: a message as transfered by transfer_message, or a part of such
- a messsage.
+ :param data: a message as transferred by transfer_message, or a part of such
+ a message.
Global variables:
_buffer - contains the data received
@@ -120,7 +117,7 @@ class DelugeTransferProtocol(Protocol, object):
def _handle_complete_message(self, data):
"""
- Handles a complete message as it is transfered on the network.
+ Handles a complete message as it is transferred on the network.
:param data: a zlib compressed string encoded with rencode.
diff --git a/deluge/ui/client.py b/deluge/ui/client.py
index 686f962b3..0fef66767 100644
--- a/deluge/ui/client.py
+++ b/deluge/ui/client.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import subprocess
import sys
@@ -18,7 +15,7 @@ from twisted.internet import defer, reactor, ssl
from twisted.internet.protocol import ClientFactory
from deluge import error
-from deluge.common import get_localhost_auth, get_version
+from deluge.common import VersionSplit, get_localhost_auth, get_version
from deluge.decorators import deprecated
from deluge.transfer import DelugeTransferProtocol
@@ -33,7 +30,7 @@ def format_kwargs(kwargs):
return ', '.join([key + '=' + str(value) for key, value in kwargs.items()])
-class DelugeRPCRequest(object):
+class DelugeRPCRequest:
"""
This object is created whenever there is a RPCRequest to be sent to the
daemon. It is generally only used by the DaemonProxy's call method.
@@ -65,7 +62,7 @@ class DelugeRPCRequest(object):
Returns a properly formatted RPCRequest based on the properties. Will
raise a TypeError if the properties haven't been set yet.
- :returns: a properly formated RPCRequest
+ :returns: a properly formatted RPCRequest
"""
if (
self.request_id is None
@@ -150,7 +147,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
# so it could pass back to the 2nd deferred on the chain. But,
# that does not always happen.
# So, just do some instance checking and just log rpc error at
- # diferent levels.
+ # different levels.
r = self.__rpc_requests[request_id]
msg = 'RPCError Message Received!'
msg += '\n' + '-' * 80
@@ -168,7 +165,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
# Let's log these as errors
log.error(msg)
else:
- # The rest just get's logged in debug level, just to log
+ # The rest just gets logged in debug level, just to log
# what's happening
log.debug(msg)
except Exception:
@@ -230,6 +227,7 @@ class DelugeRPCClientFactory(ClientFactory):
self.daemon.host = None
self.daemon.port = None
self.daemon.username = None
+ self.daemon.daemon_version = None
self.daemon.connected = False
if (
@@ -243,7 +241,7 @@ class DelugeRPCClientFactory(ClientFactory):
self.daemon.disconnect_callback()
-class DaemonProxy(object):
+class DaemonProxy:
pass
@@ -263,6 +261,7 @@ class DaemonSSLProxy(DaemonProxy):
self.host = None
self.port = None
self.username = None
+ self.daemon_version = None
self.authentication_level = 0
self.connected = False
@@ -392,7 +391,7 @@ class DaemonSSLProxy(DaemonProxy):
log.debug('__on_connect called')
def on_info(daemon_info):
- self.daemon_info = daemon_info
+ self.daemon_version = daemon_info
log.debug('Got info from daemon: %s', daemon_info)
self.daemon_info_deferred.callback(daemon_info)
@@ -526,7 +525,7 @@ class DaemonStandaloneProxy(DaemonProxy):
self.__daemon.core.eventmanager.deregister_event_handler(event, handler)
-class DottedObject(object):
+class DottedObject:
"""
This is used for dotted name calls to client
"""
@@ -551,7 +550,7 @@ class RemoteMethod(DottedObject):
return self.daemon.call(self.base, *args, **kwargs)
-class Client(object):
+class Client:
"""
This class is used to connect to a daemon process and issue RPC requests.
"""
@@ -615,7 +614,7 @@ class Client(object):
d.addErrback(on_authenticate_fail)
return d
- d.addCallback(on_connected)
+ d.addCallbacks(on_connected)
d.addErrback(on_connect_fail)
if not skip_authentication:
d.addCallback(authenticate, username, password)
@@ -744,6 +743,26 @@ class Client(object):
return None
+ @property
+ def daemon_version(self) -> str:
+ """Get the connected daemon version
+
+ Returns:
+ The daemon version
+ """
+ return self._daemon_proxy.daemon_version if self.connected() else ''
+
+ def daemon_version_check_min(self, min_version=get_version()) -> bool:
+ """Check connected daemon against a minimum version.
+
+ Returns:
+ If connected daemon meets minimum version requirement.
+ """
+ if not (self.daemon_version and min_version):
+ return False
+
+ return VersionSplit(self.daemon_version) >= VersionSplit(min_version)
+
def register_event_handler(self, event, handler):
"""
Registers a handler that will be called when an event is received from the daemon.
diff --git a/deluge/ui/common.py b/deluge/ui/common.py
index 9af1d50bd..64d5ca216 100644
--- a/deluge/ui/common.py
+++ b/deluge/ui/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) Damien Churchill 2008-2009 <damoxc@gmail.com>
# Copyright (C) Andrew Resch 2009 <andrewresch@gmail.com>
@@ -11,11 +10,10 @@
"""
The ui common module contains methods and classes that are deemed useful for all the interfaces.
"""
-from __future__ import unicode_literals
-
import logging
import os
from hashlib import sha1 as sha
+from typing import Tuple
from deluge import bencode
from deluge.common import decode_bytes
@@ -160,7 +158,6 @@ DISK_CACHE_KEYS = [
'disk.num_blocks_written',
'disk.num_read_ops',
'disk.num_write_ops',
- 'disk.num_blocks_cache_hits',
'read_hit_ratio',
'write_hit_ratio',
'disk.disk_blocks_in_use',
@@ -168,17 +165,18 @@ DISK_CACHE_KEYS = [
]
-class TorrentInfo(object):
+class TorrentInfo:
"""Collects information about a torrent file.
Args:
filename (str, optional): The path to the .torrent file.
filetree (int, optional): The version of filetree to create (defaults to 1).
torrent_file (dict, optional): A bdecoded .torrent file contents.
+ force_bt_version (int, optional): The BitTorrent spec to use for parsing (defaults to 1).
"""
- def __init__(self, filename='', filetree=1, torrent_file=None):
+ def __init__(self, filename='', filetree=1, torrent_file=None, force_bt_version=1):
self._filedata = None
if torrent_file:
self._metainfo = torrent_file
@@ -187,7 +185,7 @@ class TorrentInfo(object):
try:
with open(filename, 'rb') as _file:
self._filedata = _file.read()
- except IOError as ex:
+ except OSError as ex:
log.warning('Unable to open %s: %s', filename, ex)
return
@@ -215,12 +213,27 @@ class TorrentInfo(object):
else:
self._name = decode_bytes(info_dict['name'], encoding)
+ meta_version = info_dict['meta version'] if 'meta version' in info_dict else -1
+ is_hybrid = 'files' in info_dict and meta_version == 2
+
+ parse_v1 = False
+ parse_v2 = False
+ if is_hybrid:
+ if force_bt_version == 1:
+ parse_v1 = True
+ elif force_bt_version == 2:
+ parse_v2 = True
+ elif 'files' in info_dict:
+ parse_v1 = True
+ elif meta_version == 2 and 'file tree' in info_dict:
+ parse_v2 = True
+
# Get list of files from torrent info
self._files = []
- if 'files' in info_dict:
+ if parse_v1:
paths = {}
dirs = {}
- prefix = self._name if len(info_dict['files']) > 1 else ''
+ prefix = self._name
for index, f in enumerate(info_dict['files']):
f = {k.decode(): v for k, v in f.items()}
@@ -249,25 +262,67 @@ class TorrentInfo(object):
if filetree == 2:
- def walk(path, item):
+ def walk(full_path, item):
if item['type'] == 'dir':
- item.update(dirs[path])
+ item.update(dirs[full_path])
else:
- item.update(paths[path])
+ item.update(paths[full_path])
item['download'] = True
file_tree = FileTree2(list(paths))
file_tree.walk(walk)
else:
- def walk(path, item):
+ def walk(full_path, item):
if isinstance(item, dict):
return item
- return [paths[path]['index'], paths[path]['length'], True]
+ return [paths[full_path]['index'], paths[full_path]['length'], True]
file_tree = FileTree(paths)
file_tree.walk(walk)
self._files_tree = file_tree.get_tree()
+ elif parse_v2:
+
+ def single_file_torrent(inner_info_dict):
+ if len(inner_info_dict['file tree']) > 1:
+ return False
+
+ file_name = [key for key in inner_info_dict['file tree']][0]
+ return inner_info_dict['name'] == file_name
+
+ if not single_file_torrent(info_dict):
+ info_dict['file tree'] = {info_dict['name']: info_dict['file tree']}
+
+ if filetree == 2:
+
+ def walk(full_path, item):
+ if item['type'] == 'file':
+ item['path'] = full_path
+ self._files.append(
+ {
+ 'path': full_path,
+ 'size': item['length'],
+ 'download': True,
+ }
+ )
+ item['download'] = True
+
+ file_tree = FileTree2BTv2(info_dict['file tree'])
+ file_tree.walk(walk)
+ else:
+
+ def walk(full_path, item):
+ if isinstance(item, dict):
+ return item
+ self._files.append(
+ {'path': full_path, 'size': item[1], 'download': True}
+ )
+ return [item[0], item[1], True]
+
+ file_tree = FiletreeBTv2(info_dict['file tree'])
+ file_tree.walk(walk)
+
+ self._files_tree = file_tree.get_tree()
else:
self._files.append(
{'path': self._name, 'size': info_dict['length'], 'download': True}
@@ -388,15 +443,33 @@ class TorrentInfo(object):
return self._filedata
-class FileTree2(object):
+class FileTree2:
"""
- Converts a list of paths in to a file tree.
+ Converts a list of paths, from a V1 torrent, into a file tree.
+
+ Each file will have the dictionary structure of:
+ { file_name: {type, path, index, length, download} }
+ where:
+ type (str): will always be "file"
+ path (str): the absolute file path from the root the torrent
+ index (int): the index of file in the torrent
+ length (int): the size of the file, in bytes
+ download (bool): marks the file to download
+
+ Folder will be dictionaries of files:
+ { dir1: type, contents: {file_name1: {...}, file_name2: {...}}, dir2: ... }
+ where:
+ type (str): will always be "dir"
+ contents (dict): a dictionary of inner files and folders
+
+ The entire tree will start with a root dictionary:
+ { contents: {dirs...}, type: "dir" }
- :param paths: The paths to be converted
- :type paths: list
+ Args:
+ paths (list): The paths to be converted.
"""
- def __init__(self, paths):
+ def __init__(self, paths: list):
self.tree = {'contents': {}, 'type': 'dir'}
def get_parent(path):
@@ -468,15 +541,25 @@ class FileTree2(object):
return '\n'.join(lines)
-class FileTree(object):
+class FileTree:
"""
- Convert a list of paths in a file tree.
+ Converts a dict of paths, from a V1 torrent, into a file tree.
+
+ Each file will have the dictionary structure of:
+ { file_name: [index, length, download] }
+ Where:
+ index (int): the index of file in the torrent
+ length (int): the size of the file, in bytes
+ download (bool): marks the file to download
- :param paths: The paths to be converted.
- :type paths: list
+ Folder will be dictionaries of files:
+ { dir1: {file_name1: [...], file_name2: [...]}, dir2: ... }
+
+ Args:
+ paths (dict): The paths to be converted.
"""
- def __init__(self, paths):
+ def __init__(self, paths: dict):
self.tree = {}
def get_parent(path):
@@ -502,8 +585,8 @@ class FileTree(object):
"""
Return the tree, after first converting all file lists to a tuple.
- :returns: the file tree.
- :rtype: dictionary
+ Returns:
+ dict: the file tree.
"""
def to_tuple(path, item):
@@ -519,10 +602,10 @@ class FileTree(object):
Walk through the file tree calling the callback function on each item
contained.
- :param callback: The function to be used as a callback, it should have
- the signature func(item, path) where item is a `tuple` for a file
- and `dict` for a directory.
- :type callback: function
+ Args:
+ callback (function): The function to be used as a callback, it should have
+ the signature func(item, path) where item is a `tuple` for a file
+ and `dict` for a directory.
"""
def walk(directory, parent_path):
@@ -551,3 +634,94 @@ class FileTree(object):
self.walk(write)
return '\n'.join(lines)
+
+
+class FiletreeBTv2(FileTree):
+ """
+ Converts a dict of paths, from a V2 torrent, into a file tree.
+
+ Each file will have the dictionary structure of:
+ { file_name: [index, length, download] }
+ Where:
+ index (int): the index of file in the torrent
+ length (int): the size of the file, in bytes
+ download (bool): marks the file to download
+
+ Folder will be dictionaries of files:
+ { dir1: {file_name1: [...], file_name2: [...]}, dir2: ... }
+
+ Args:
+ file_tree (dict): The paths to be converted.
+ """
+
+ def __init__(self, file_tree):
+ self.tree = {}
+
+ def get_parent(curr_tree_dict, index, parent) -> int:
+ for key, item in curr_tree_dict.items():
+ key = decode_bytes(key)
+ if b'' in item:
+ parent[key] = [index, item[b''][b'length']]
+ index += 1
+ else:
+ parent[key] = {}
+ index = get_parent(item, index, parent[key])
+ return index
+
+ get_parent(file_tree, 0, self.tree)
+
+
+class FileTree2BTv2(FileTree2):
+ """
+ Converts a dict of paths, from a V2 torrent, into a file tree.
+
+ Each file will have the dictionary structure of:
+ { file_name: {type, path, index, length, download} }
+ where:
+ type (str): will always be "file"
+ path (str): the absolute file path from the root the torrent
+ index (int): the index of file in the torrent
+ length (int): the size of the file, in bytes
+ download (bool): marks the file to download
+
+ Folder will be dictionaries of files:
+ { dir1: type, contents: {file_name1: {...}, file_name2: {...}}, dir2: ... }
+ where:
+ type (str): will always be "dir"
+ contents (dict): a dictionary of inner files and folders
+
+ The entire tree will start with a root dictionary:
+ { contents: {dirs...}, type: "dir" }
+
+ Args:
+ file_tree (dict): The paths to be converted.
+ """
+
+ def __init__(self, file_tree):
+ self.tree = {'contents': {}, 'type': 'dir'}
+
+ def get_parent(curr_tree_dict, index, parent) -> Tuple[int, int]:
+ total_length = 0
+ for key, item in curr_tree_dict.items():
+ key = decode_bytes(key)
+ if b'' in item:
+ length = item[b''][b'length']
+ total_length += length
+ parent['contents'][key] = {
+ 'index': index,
+ 'length': length,
+ 'type': 'file',
+ }
+ index += 1
+ else:
+ parent['contents'][key] = {
+ 'contents': {},
+ 'type': 'dir',
+ 'length': 0,
+ }
+ index, length = get_parent(item, index, parent['contents'][key])
+ parent['contents'][key]['length'] = length
+ total_length += length
+ return index, total_length
+
+ get_parent(file_tree, 0, self.tree)
diff --git a/deluge/ui/console/__init__.py b/deluge/ui/console/__init__.py
index 56e8d629d..a09d9366c 100644
--- a/deluge/ui/console/__init__.py
+++ b/deluge/ui/console/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
#
@@ -7,13 +6,19 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.ui.console.console import Console
UI_PATH = __path__[0]
def start():
-
Console().start()
+
+
+def test_start():
+ """Entry point for tests
+
+ A workaround for unit tests which require a deferred object to be
+ returned to run properly due to mocking the Twisted reactor.
+ """
+ return Console().start()
diff --git a/deluge/ui/console/cmdline/command.py b/deluge/ui/console/cmdline/command.py
index 2ff32dff9..63dc9265a 100644
--- a/deluge/ui/console/cmdline/command.py
+++ b/deluge/ui/console/cmdline/command.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -9,8 +8,6 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import logging
import shlex
@@ -23,7 +20,7 @@ from deluge.ui.console.utils.colors import strip_colors
log = logging.getLogger(__name__)
-class Commander(object):
+class Commander:
def __init__(self, cmds, interactive=False):
self._commands = cmds
self.interactive = interactive
@@ -144,8 +141,7 @@ class Commander(object):
return ret
-class BaseCommand(object):
-
+class BaseCommand:
usage = None
interactive_only = False
aliases = []
diff --git a/deluge/ui/console/cmdline/commands/__init__.py b/deluge/ui/console/cmdline/commands/__init__.py
index 628fae597..39dbefe2a 100644
--- a/deluge/ui/console/cmdline/commands/__init__.py
+++ b/deluge/ui/console/cmdline/commands/__init__.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
from deluge.ui.console.cmdline.command import BaseCommand
__all__ = ['BaseCommand']
diff --git a/deluge/ui/console/cmdline/commands/add.py b/deluge/ui/console/cmdline/commands/add.py
index da42695b5..706ae168e 100644
--- a/deluge/ui/console/cmdline/commands/add.py
+++ b/deluge/ui/console/cmdline/commands/add.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,10 +7,10 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import os
from base64 import b64encode
+from urllib.parse import urlparse
+from urllib.request import url2pathname
from twisted.internet import defer
@@ -21,14 +20,6 @@ from deluge.ui.client import client
from . import BaseCommand
-try:
- from urllib.parse import urlparse
- from urllib.request import url2pathname
-except ImportError:
- # PY2 fallback
- from urllib import url2pathname # pylint: disable=ungrouped-imports
- from urlparse import urlparse # pylint: disable=ungrouped-imports
-
class Command(BaseCommand):
"""Add torrents"""
diff --git a/deluge/ui/console/cmdline/commands/cache.py b/deluge/ui/console/cmdline/commands/cache.py
index e427f085f..fe6cd580d 100644
--- a/deluge/ui/console/cmdline/commands/cache.py
+++ b/deluge/ui/console/cmdline/commands/cache.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.ui.client import client
from deluge.ui.common import DISK_CACHE_KEYS
@@ -24,7 +21,7 @@ class Command(BaseCommand):
def on_cache_status(status):
for key, value in sorted(status.items()):
- self.console.write('{!info!}%s: {!input!}%s' % (key, value))
+ self.console.write(f'{{!info!}}{key}: {{!input!}}{value}')
return client.core.get_session_status(DISK_CACHE_KEYS).addCallback(
on_cache_status
diff --git a/deluge/ui/console/cmdline/commands/config.py b/deluge/ui/console/cmdline/commands/config.py
index 9821e47bc..8b31ca3cd 100644
--- a/deluge/ui/console/cmdline/commands/config.py
+++ b/deluge/ui/console/cmdline/commands/config.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import json
import logging
import re
@@ -97,10 +94,10 @@ class Command(BaseCommand):
value = pprint.pformat(value, 2, 80)
new_value = []
for line in value.splitlines():
- new_value.append('%s%s' % (color, line))
+ new_value.append(f'{color}{line}')
value = '\n'.join(new_value)
- string += '%s: %s%s\n' % (key, color, value)
+ string += f'{key}: {color}{value}\n'
self.console.write(string.strip())
return client.core.get_config().addCallback(_on_get_config)
@@ -132,7 +129,7 @@ class Command(BaseCommand):
def on_set_config(result):
self.console.write('{!success!}Configuration value successfully updated.')
- self.console.write('Setting "%s" to: %r' % (key, val))
+ self.console.write(f'Setting "{key}" to: {val!r}')
return client.core.set_config({key: val}).addCallback(on_set_config)
def complete(self, text):
diff --git a/deluge/ui/console/cmdline/commands/connect.py b/deluge/ui/console/cmdline/commands/connect.py
index 6588f7a04..4c76de38f 100644
--- a/deluge/ui/console/cmdline/commands/connect.py
+++ b/deluge/ui/console/cmdline/commands/connect.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -57,17 +54,12 @@ class Command(BaseCommand):
def on_connect(result):
if self.console.interactive:
- self.console.write('{!success!}Connected to %s:%s!' % (host, port))
+ self.console.write(f'{{!success!}}Connected to {host}:{port}!')
return component.start()
def on_connect_fail(result):
- try:
- msg = result.value.exception_msg
- except AttributeError:
- msg = result.value.message
self.console.write(
- '{!error!}Failed to connect to %s:%s with reason: %s'
- % (host, port, msg)
+ f'{{!error!}}Failed to connect to {host}:{port} with reason: {result.value.message}'
)
return result
diff --git a/deluge/ui/console/cmdline/commands/debug.py b/deluge/ui/console/cmdline/commands/debug.py
index 3ca06ed15..af48a8b7f 100644
--- a/deluge/ui/console/cmdline/commands/debug.py
+++ b/deluge/ui/console/cmdline/commands/debug.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from twisted.internet import defer
import deluge.component as component
diff --git a/deluge/ui/console/cmdline/commands/gui.py b/deluge/ui/console/cmdline/commands/gui.py
index 10e4c499b..575bc9b3a 100644
--- a/deluge/ui/console/cmdline/commands/gui.py
+++ b/deluge/ui/console/cmdline/commands/gui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
diff --git a/deluge/ui/console/cmdline/commands/halt.py b/deluge/ui/console/cmdline/commands/halt.py
index 635595898..608f2de9d 100644
--- a/deluge/ui/console/cmdline/commands/halt.py
+++ b/deluge/ui/console/cmdline/commands/halt.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.ui.client import client
diff --git a/deluge/ui/console/cmdline/commands/help.py b/deluge/ui/console/cmdline/commands/help.py
index 2711eea99..754dadbec 100644
--- a/deluge/ui/console/cmdline/commands/help.py
+++ b/deluge/ui/console/cmdline/commands/help.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from twisted.internet import defer
diff --git a/deluge/ui/console/cmdline/commands/info.py b/deluge/ui/console/cmdline/commands/info.py
index 0d22f76a9..7ea9a6773 100644
--- a/deluge/ui/console/cmdline/commands/info.py
+++ b/deluge/ui/console/cmdline/commands/info.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
from os.path import sep as dirsep
import deluge.component as component
@@ -70,6 +67,7 @@ STATUS_KEYS = [
'total_payload_download',
'total_payload_upload',
'time_added',
+ 'label',
]
# Add filter specific state to torrent states
@@ -177,7 +175,7 @@ class Command(BaseCommand):
sort_key = 'name'
sort_reverse = False
for key, value in sorted(
- list(status.items()),
+ status.items(),
key=lambda x: x[1].get(sort_key),
reverse=sort_reverse,
):
@@ -218,9 +216,9 @@ class Command(BaseCommand):
for depth, subdir in enumerate(filepath):
indent = ' ' * depth * spaces_per_level
if depth >= len(prevpath):
- self.console.write('%s{!cyan!}%s' % (indent, subdir))
+ self.console.write(f'{indent}{{!cyan!}}{subdir}')
elif subdir != prevpath[depth]:
- self.console.write('%s{!cyan!}%s' % (indent, subdir))
+ self.console.write(f'{indent}{{!cyan!}}{subdir}')
depth = len(filepath)
@@ -296,7 +294,7 @@ class Command(BaseCommand):
s += peer['ip']
else:
# IPv6
- s += '[%s]:%s' % (
+ s += '[{}]:{}'.format(
':'.join(peer['ip'].split(':')[:-1]),
peer['ip'].split(':')[-1],
)
@@ -308,7 +306,7 @@ class Command(BaseCommand):
s += '\t\t'
else:
s += '\t'
- s += '%s%s\t%s%s' % (
+ s += '{}{}\t{}{}'.format(
colors.state_color['Seeding'],
fspeed(peer['up_speed']),
colors.state_color['Downloading'],
@@ -336,7 +334,7 @@ class Command(BaseCommand):
if verbose or detailed:
self.console.write('{!info!}Name: {!input!}%s' % (status['name']))
self.console.write('{!info!}ID: {!input!}%s' % (torrent_id))
- s = '{!info!}State: %s%s' % (
+ s = '{{!info!}}State: {}{}'.format(
colors.state_color[status['state']],
status['state'],
)
@@ -354,12 +352,12 @@ class Command(BaseCommand):
self.console.write(s)
if status['state'] in ('Seeding', 'Downloading', 'Queued'):
- s = '{!info!}Seeds: {!input!}%s (%s)' % (
+ s = '{{!info!}}Seeds: {{!input!}}{} ({})'.format(
status['num_seeds'],
status['total_seeds'],
)
s += sep
- s += '{!info!}Peers: {!input!}%s (%s)' % (
+ s += '{{!info!}}Peers: {{!input!}}{} ({})'.format(
status['num_peers'],
status['total_peers'],
)
@@ -378,7 +376,7 @@ class Command(BaseCommand):
if total_done == total_size:
s = '{!info!}Size: {!input!}%s' % (total_size)
else:
- s = '{!info!}Size: {!input!}%s/%s' % (total_done, total_size)
+ s = f'{{!info!}}Size: {{!input!}}{total_done}/{total_size}'
s += sep
s += '{!info!}Downloaded: {!input!}%s' % fsize(
status['all_time_download'], shortform=True
@@ -418,14 +416,20 @@ class Command(BaseCommand):
pbar = f_progressbar(
status['progress'], cols - (13 + len('%.2f%%' % status['progress']))
)
- s = '{!info!}Progress: {!input!}%.2f%% %s' % (status['progress'], pbar)
+ s = '{{!info!}}Progress: {{!input!}}{:.2f}% {}'.format(
+ status['progress'], pbar
+ )
self.console.write(s)
s = '{!info!}Download Folder: {!input!}%s' % status['download_location']
- self.console.write(s + '\n')
+ self.console.write(s)
+
+ if 'label' in status:
+ s = '{!info!}Label: {!input!}%s' % status['label']
+ self.console.write(s)
if detailed:
- self.console.write('{!info!}Files in torrent')
+ self.console.write('\n{!info!}Files in torrent')
self.show_file_info(torrent_id, status)
self.console.write('{!info!}Connected peers')
self.show_peer_info(torrent_id, status)
@@ -433,7 +437,7 @@ class Command(BaseCommand):
up_color = colors.state_color['Seeding']
down_color = colors.state_color['Downloading']
- s = '%s%s' % (
+ s = '{}{}'.format(
colors.state_color[status['state']],
'[' + status['state'][0] + ']',
)
@@ -458,7 +462,7 @@ class Command(BaseCommand):
)
if status['download_payload_rate'] > 0:
- dl_info += ' @ %s%s' % (
+ dl_info += ' @ {}{}'.format(
down_color,
fspeed(status['download_payload_rate'], shortform=True),
)
@@ -468,7 +472,7 @@ class Command(BaseCommand):
status['total_uploaded'], status['total_payload_upload']
)
if status['upload_payload_rate'] > 0:
- ul_info += ' @ %s%s' % (
+ ul_info += ' @ {}{}'.format(
up_color,
fspeed(status['upload_payload_rate'], shortform=True),
)
diff --git a/deluge/ui/console/cmdline/commands/manage.py b/deluge/ui/console/cmdline/commands/manage.py
index 6375a74c3..e5ea9b255 100644
--- a/deluge/ui/console/cmdline/commands/manage.py
+++ b/deluge/ui/console/cmdline/commands/manage.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from twisted.internet import defer
@@ -69,7 +66,7 @@ class Command(BaseCommand):
self.console.write('{!info!}ID: {!input!}%s' % torrentid)
for k, v in data.items():
if k != 'name':
- self.console.write('{!info!}%s: {!input!}%s' % (k, v))
+ self.console.write(f'{{!info!}}{k}: {{!input!}}{v}')
def on_torrents_status_fail(reason):
self.console.write('{!error!}Failed to get torrent data.')
@@ -106,9 +103,7 @@ class Command(BaseCommand):
self.console.write('{!success!}Torrent option successfully updated.')
deferred.callback(True)
- self.console.write(
- 'Setting %s to %s for torrents %s..' % (key, val, torrent_ids)
- )
+ self.console.write(f'Setting {key} to {val} for torrents {torrent_ids}..')
client.core.set_torrent_options(torrent_ids, {key: val}).addCallback(
on_set_config
)
diff --git a/deluge/ui/console/cmdline/commands/move.py b/deluge/ui/console/cmdline/commands/move.py
index 13e475e6f..67ee0af1d 100644
--- a/deluge/ui/console/cmdline/commands/move.py
+++ b/deluge/ui/console/cmdline/commands/move.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os.path
@@ -52,7 +49,7 @@ class Command(BaseCommand):
names.append(self.console.get_torrent_name(tid))
def on_move(res):
- msg = 'Moved "%s" to %s' % (', '.join(names), options.path)
+ msg = 'Moved "{}" to {}'.format(', '.join(names), options.path)
self.console.write(msg)
log.info(msg)
diff --git a/deluge/ui/console/cmdline/commands/pause.py b/deluge/ui/console/cmdline/commands/pause.py
index 1f7ef31a0..133424267 100644
--- a/deluge/ui/console/cmdline/commands/pause.py
+++ b/deluge/ui/console/cmdline/commands/pause.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.ui.client import client
diff --git a/deluge/ui/console/cmdline/commands/plugin.py b/deluge/ui/console/cmdline/commands/plugin.py
index 72cecb40f..c424cb201 100644
--- a/deluge/ui/console/cmdline/commands/plugin.py
+++ b/deluge/ui/console/cmdline/commands/plugin.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
import deluge.configmanager
from deluge.ui.client import client
diff --git a/deluge/ui/console/cmdline/commands/quit.py b/deluge/ui/console/cmdline/commands/quit.py
index 261a01a9b..4459dfc70 100644
--- a/deluge/ui/console/cmdline/commands/quit.py
+++ b/deluge/ui/console/cmdline/commands/quit.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from . import BaseCommand
diff --git a/deluge/ui/console/cmdline/commands/recheck.py b/deluge/ui/console/cmdline/commands/recheck.py
index c9b6360c9..046cb0b1e 100644
--- a/deluge/ui/console/cmdline/commands/recheck.py
+++ b/deluge/ui/console/cmdline/commands/recheck.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.ui.client import client
diff --git a/deluge/ui/console/cmdline/commands/resume.py b/deluge/ui/console/cmdline/commands/resume.py
index 1f62c5f00..27b852894 100644
--- a/deluge/ui/console/cmdline/commands/resume.py
+++ b/deluge/ui/console/cmdline/commands/resume.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.ui.client import client
diff --git a/deluge/ui/console/cmdline/commands/rm.py b/deluge/ui/console/cmdline/commands/rm.py
index ff3125d81..4a3fd008a 100644
--- a/deluge/ui/console/cmdline/commands/rm.py
+++ b/deluge/ui/console/cmdline/commands/rm.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -70,9 +67,11 @@ class Command(BaseCommand):
def on_removed_finished(errors):
if errors:
- self.console.write('Error(s) occured when trying to delete torrent(s).')
+ self.console.write(
+ 'Error(s) occurred when trying to delete torrent(s).'
+ )
for t_id, e_msg in errors:
- self.console.write('Error removing torrent %s : %s' % (t_id, e_msg))
+ self.console.write(f'Error removing torrent {t_id} : {e_msg}')
log.info('Removing %d torrents', len(torrent_ids))
d = client.core.remove_torrents(torrent_ids, options.remove_data)
diff --git a/deluge/ui/console/cmdline/commands/status.py b/deluge/ui/console/cmdline/commands/status.py
index 948ad6b94..05c9796ce 100644
--- a/deluge/ui/console/cmdline/commands/status.py
+++ b/deluge/ui/console/cmdline/commands/status.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from twisted.internet import defer
@@ -65,7 +62,12 @@ class Command(BaseCommand):
deferreds = []
ds = client.core.get_session_status(
- ['num_peers', 'payload_upload_rate', 'payload_download_rate', 'dht_nodes']
+ [
+ 'peer.num_peers_connected',
+ 'payload_upload_rate',
+ 'payload_download_rate',
+ 'dht.dht_nodes',
+ ]
)
ds.addCallback(on_session_status)
deferreds.append(ds)
@@ -95,7 +97,7 @@ class Command(BaseCommand):
'{!info!}Total download: %s'
% fspeed(self.status['payload_download_rate'])
)
- self.console.write('{!info!}DHT Nodes: %i' % self.status['dht_nodes'])
+ self.console.write('{!info!}DHT Nodes: %i' % self.status['dht.dht_nodes'])
if isinstance(self.torrents, int):
if self.torrents == -2:
diff --git a/deluge/ui/console/cmdline/commands/update_tracker.py b/deluge/ui/console/cmdline/commands/update_tracker.py
index 591b95192..c05569d7b 100644
--- a/deluge/ui/console/cmdline/commands/update_tracker.py
+++ b/deluge/ui/console/cmdline/commands/update_tracker.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.component as component
from deluge.ui.client import client
diff --git a/deluge/ui/console/console.py b/deluge/ui/console/console.py
index f683c749d..f91563f43 100644
--- a/deluge/ui/console/console.py
+++ b/deluge/ui/console/console.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -7,8 +6,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 fnmatch
import logging
import os
@@ -53,7 +50,7 @@ def load_commands(command_dir):
return dict(commands)
-class LogStream(object):
+class LogStream:
out = sys.stdout
def write(self, data):
@@ -64,13 +61,10 @@ class LogStream(object):
class Console(UI):
-
cmd_description = """Console or command-line user interface"""
def __init__(self, *args, **kwargs):
- super(Console, self).__init__(
- 'console', *args, log_stream=LogStream(), **kwargs
- )
+ super().__init__('console', *args, log_stream=LogStream(), **kwargs)
group = self.parser.add_argument_group(
_('Console Options'),
@@ -150,7 +144,7 @@ class Console(UI):
self.console_parser.subcommand = False
self.parser.subcommand = False if i == -1 else True
- super(Console, self).start(self.console_parser)
+ super().start(self.console_parser)
from deluge.ui.console.main import ConsoleUI # import here because (see top)
def run(options):
diff --git a/deluge/ui/console/eventlog.py b/deluge/ui/console/eventlog.py
new file mode 100644
index 000000000..c1ee6abe4
--- /dev/null
+++ b/deluge/ui/console/eventlog.py
@@ -0,0 +1,125 @@
+import time
+
+import deluge.component as component
+from deluge.decorators import maybe_coroutine
+from deluge.ui.client import client
+from deluge.ui.console.utils import colors
+
+
+class EventLog(component.Component):
+ """
+ Prints out certain events as they are received from the core.
+ """
+
+ def __init__(self):
+ component.Component.__init__(self, 'EventLog')
+ self.console = component.get('ConsoleUI')
+ self.prefix = '{!event!}* [%H:%M:%S] '
+ self.date_change_format = 'On {!yellow!}%a, %d %b %Y{!input!} %Z:'
+
+ event_callbacks = {
+ 'TorrentAddedEvent': self.on_torrent_added,
+ 'PreTorrentRemovedEvent': self.on_torrent_removed,
+ 'TorrentStateChangedEvent': self.on_torrent_state_changed,
+ 'TorrentFinishedEvent': self.on_torrent_finished,
+ 'NewVersionAvailableEvent': self.on_new_version_available,
+ 'SessionPausedEvent': self.on_session_paused,
+ 'SessionResumedEvent': self.on_session_resumed,
+ 'ConfigValueChangedEvent': self.on_config_value_changed,
+ 'PluginEnabledEvent': self.on_plugin_enabled,
+ 'PluginDisabledEvent': self.on_plugin_disabled,
+ }
+
+ for event, callback in event_callbacks.items():
+ client.register_event_handler(event, callback)
+
+ self.previous_time = time.localtime(0)
+
+ @maybe_coroutine
+ async def on_torrent_added(self, torrent_id, from_state):
+ if from_state:
+ return
+
+ status = await client.core.get_torrent_status(torrent_id, ['name', 'state'])
+ self.write(
+ '{!green!}Torrent Added: {!info!}%s ({!cyan!}%s{!info!})'
+ % (status['name'], torrent_id)
+ )
+ # Write out what state the added torrent took
+ self.on_torrent_state_changed(torrent_id, status['state'])
+
+ def on_torrent_removed(self, torrent_id):
+ self.write(
+ '{!red!}Torrent Removed: {!info!}%s ({!cyan!}%s{!info!})'
+ % (self.console.get_torrent_name(torrent_id), torrent_id)
+ )
+
+ def on_torrent_state_changed(self, torrent_id, state):
+ # It's probably a new torrent, ignore it
+ if not state:
+ return
+ # Modify the state string color
+ if state in colors.state_color:
+ state = colors.state_color[state] + state
+
+ t_name = self.console.get_torrent_name(torrent_id)
+
+ # Again, it's most likely a new torrent
+ if not t_name:
+ return
+
+ self.write(f'{state}: {{!info!}}{t_name} ({{!cyan!}}{torrent_id}{{!info!}})')
+
+ def on_torrent_finished(self, torrent_id):
+ if component.get('TorrentList').config['ring_bell']:
+ import curses.beep
+
+ curses.beep()
+ self.write(
+ '{!info!}Torrent Finished: %s ({!cyan!}%s{!info!})'
+ % (self.console.get_torrent_name(torrent_id), torrent_id)
+ )
+
+ def on_new_version_available(self, version):
+ self.write('{!input!}New Deluge version available: {!info!}%s' % (version))
+
+ def on_session_paused(self):
+ self.write('{!input!}Session Paused')
+
+ def on_session_resumed(self):
+ self.write('{!green!}Session Resumed')
+
+ def on_config_value_changed(self, key, value):
+ color = '{!white,black,bold!}'
+ try:
+ color = colors.type_color[type(value)]
+ except KeyError:
+ pass
+
+ self.write(f'ConfigValueChanged: {{!input!}}{key}: {color}{value}')
+
+ def write(self, s):
+ current_time = time.localtime()
+
+ date_different = False
+ for field in ['tm_mday', 'tm_mon', 'tm_year']:
+ c = getattr(current_time, field)
+ p = getattr(self.previous_time, field)
+ if c != p:
+ date_different = True
+
+ if date_different:
+ string = time.strftime(self.date_change_format)
+ self.console.write_event(' ')
+ self.console.write_event(string)
+
+ p = time.strftime(self.prefix)
+
+ self.console.write_event(p + s)
+ self.previous_time = current_time
+
+ def on_plugin_enabled(self, name):
+ self.write('PluginEnabled: {!info!}%s' % name)
+
+ def on_plugin_disabled(self, name):
+ self.write('PluginDisabled: {!info!}%s' % name)
diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py
index c74d9022f..106169f0e 100644
--- a/deluge/ui/console/main.py
+++ b/deluge/ui/console/main.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,22 +7,19 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import locale
import logging
import os
import sys
-import time
from twisted.internet import defer, error, reactor
import deluge.common
import deluge.component as component
from deluge.configmanager import ConfigManager
-from deluge.decorators import overrides
-from deluge.error import DelugeError
+from deluge.decorators import maybe_coroutine, overrides
from deluge.ui.client import client
+from deluge.ui.console.eventlog import EventLog
from deluge.ui.console.modes.addtorrents import AddTorrents
from deluge.ui.console.modes.basemode import TermResizeHandler
from deluge.ui.console.modes.cmdline import CmdLine
@@ -32,6 +28,7 @@ from deluge.ui.console.modes.preferences import Preferences
from deluge.ui.console.modes.torrentdetail import TorrentDetail
from deluge.ui.console.modes.torrentlist.torrentlist import TorrentList
from deluge.ui.console.utils import colors
+from deluge.ui.console.utils.config import migrate_1_to_2
from deluge.ui.console.widgets import StatusBars
from deluge.ui.coreconfig import CoreConfig
from deluge.ui.sessionproxy import SessionProxy
@@ -67,7 +64,7 @@ DEFAULT_CONSOLE_PREFS = {
}
-class MockConsoleLog(object):
+class MockConsoleLog:
def write(self, data):
pass
@@ -163,82 +160,54 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
wrapper(self.run)
- def quit(self):
+ @maybe_coroutine
+ async def quit(self):
if client.connected():
+ await client.disconnect()
- def on_disconnect(result):
- reactor.stop()
-
- return client.disconnect().addCallback(on_disconnect)
- else:
- try:
- reactor.stop()
- except error.ReactorNotRunning:
- pass
+ try:
+ reactor.stop()
+ except error.ReactorNotRunning:
+ pass
- def exec_args(self, options):
+ @maybe_coroutine
+ async def exec_args(self, options):
"""Execute console commands from command line."""
from deluge.ui.console.cmdline.command import Commander
commander = Commander(self._commands)
-
- def on_connect(result):
- def on_components_started(result):
- def on_started(result):
- def do_command(result, cmd):
- return commander.do_command(cmd)
-
- def exec_command(result, cmd):
- return commander.exec_command(cmd)
-
- d = defer.succeed(None)
- for command in options.parsed_cmds:
- if command.command in ('quit', 'exit'):
- break
- d.addCallback(exec_command, command)
- d.addCallback(do_command, 'quit')
- return d
-
- # We need to wait for the rpcs in start() to finish before processing
- # any of the commands.
- self.started_deferred.addCallback(on_started)
- return self.started_deferred
-
- d = self.start_console()
- d.addCallback(on_components_started)
- return d
-
- def on_connect_fail(reason):
- if reason.check(DelugeError):
- rm = reason.getErrorMessage()
+ try:
+ if not self.interactive and options.parsed_cmds[0].command == 'connect':
+ await commander.exec_command(options.parsed_cmds.pop(0))
else:
- rm = reason.value.message
+ daemon_options = (
+ options.daemon_addr,
+ options.daemon_port,
+ options.daemon_user,
+ options.daemon_pass,
+ )
+ log.info(
+ 'Connect: host=%s, port=%s, username=%s',
+ *daemon_options[0:3],
+ )
+ await client.connect(*daemon_options)
+ except Exception as reason:
print(
'Could not connect to daemon: %s:%s\n %s'
- % (options.daemon_addr, options.daemon_port, rm)
+ % (options.daemon_addr, options.daemon_port, reason)
)
commander.do_command('quit')
- d = None
- if not self.interactive and options.parsed_cmds[0].command == 'connect':
- d = commander.exec_command(options.parsed_cmds.pop(0))
- else:
- log.info(
- 'connect: host=%s, port=%s, username=%s, password=%s',
- options.daemon_addr,
- options.daemon_port,
- options.daemon_user,
- options.daemon_pass,
- )
- d = client.connect(
- options.daemon_addr,
- options.daemon_port,
- options.daemon_user,
- options.daemon_pass,
- )
- d.addCallback(on_connect)
- d.addErrback(on_connect_fail)
- return d
+ await self.start_console()
+ # Wait for RPCs in start() to finish before processing commands.
+ await self.started_deferred
+
+ for cmd in options.parsed_cmds:
+ if cmd.command in ('quit', 'exit'):
+ break
+ await commander.exec_command(cmd)
+
+ commander.do_command('quit')
def run(self, stdscr):
"""This method is called by the curses.wrapper to start the mainloop and screen.
@@ -254,7 +223,7 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
self.config = ConfigManager(
'console.conf', defaults=DEFAULT_CONSOLE_PREFS, file_version=2
)
- self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2)
+ self.config.run_converter((0, 1), 2, migrate_1_to_2)
self.statusbars = StatusBars()
from deluge.ui.console.modes.connectionmanager import ConnectionManager
@@ -285,8 +254,8 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
reactor.run()
@overrides(TermResizeHandler)
- def on_terminal_size(self, *args):
- rows, cols = super(ConsoleUI, self).on_terminal_size(args)
+ def on_resize(self, *args):
+ rows, cols = super().on_resize(*args)
for mode in self.modes:
self.modes[mode].on_resize(rows, cols)
@@ -356,78 +325,64 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
def is_active_mode(self, mode):
return mode == self.active_mode
- def start_components(self):
- def on_started(result):
- component.pause(
- [
- 'TorrentList',
- 'EventView',
- 'AddTorrents',
- 'TorrentDetail',
- 'Preferences',
- ]
- )
-
- if self.interactive:
- d = component.start().addCallback(on_started)
- else:
- d = component.start(['SessionProxy', 'ConsoleUI', 'CoreConfig'])
- return d
+ @maybe_coroutine
+ async def start_components(self):
+ if not self.interactive:
+ return await component.start(['SessionProxy', 'ConsoleUI', 'CoreConfig'])
+
+ await component.start()
+ component.pause(
+ [
+ 'TorrentList',
+ 'EventView',
+ 'AddTorrents',
+ 'TorrentDetail',
+ 'Preferences',
+ ]
+ )
- def start_console(self):
- # Maintain a list of (torrent_id, name) for use in tab completion
+ @maybe_coroutine
+ async def start_console(self):
self.started_deferred = defer.Deferred()
- if not self.initialized:
- self.initialized = True
- d = self.start_components()
+ if self.initialized:
+ await component.stop(['SessionProxy'])
+ await component.start(['SessionProxy'])
else:
+ self.initialized = True
+ await self.start_components()
- def on_stopped(result):
- return component.start(['SessionProxy'])
-
- d = component.stop(['SessionProxy']).addCallback(on_stopped)
- return d
-
- def start(self):
- def on_session_state(result):
- self.torrents = []
- self.events = []
+ @maybe_coroutine
+ async def start(self):
+ result = await client.core.get_session_state()
+ # Maintain a list of (torrent_id, name) for use in tab completion
+ self.torrents = []
+ self.events = []
- def on_torrents_status(torrents):
- for torrent_id, status in torrents.items():
- self.torrents.append((torrent_id, status['name']))
- self.started_deferred.callback(True)
+ torrents = await client.core.get_torrents_status({'id': result}, ['name'])
+ for torrent_id, status in torrents.items():
+ self.torrents.append((torrent_id, status['name']))
- client.core.get_torrents_status({'id': result}, ['name']).addCallback(
- on_torrents_status
- )
-
- d = client.core.get_session_state().addCallback(on_session_state)
+ self.started_deferred.callback(True)
# Register event handlers to keep the torrent list up-to-date
- client.register_event_handler('TorrentAddedEvent', self.on_torrent_added_event)
- client.register_event_handler(
- 'TorrentRemovedEvent', self.on_torrent_removed_event
- )
- return d
-
- def on_torrent_added_event(self, event, from_state=False):
- def on_torrent_status(status):
- self.torrents.append((event, status['name']))
+ client.register_event_handler('TorrentAddedEvent', self.on_torrent_added)
+ client.register_event_handler('TorrentRemovedEvent', self.on_torrent_removed)
- client.core.get_torrent_status(event, ['name']).addCallback(on_torrent_status)
+ @defer.inlineCallbacks
+ def on_torrent_added(self, event, from_state=False):
+ status = yield client.core.get_torrent_status(event, ['name'])
+ self.torrents.append((event, status['name']))
- def on_torrent_removed_event(self, event):
+ def on_torrent_removed(self, event):
for index, (tid, name) in enumerate(self.torrents):
if event == tid:
del self.torrents[index]
def match_torrents(self, strings):
- torrent_ids = []
- for s in strings:
- torrent_ids.extend(self.match_torrent(s))
- return list(set(torrent_ids))
+ return list(
+ {torrent for string in strings for torrent in self.match_torrent(string)}
+ )
def match_torrent(self, string):
"""
@@ -513,258 +468,3 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
self.events.append(s)
else:
print(colors.strip_colors(s))
-
- def _migrate_config_1_to_2(self, config):
- """Create better structure by moving most settings out of dict root
- and into sub categories. Some keys are also renamed to be consistent
- with other UIs.
- """
-
- def move_key(source, dest, source_key, dest_key=None):
- if dest_key is None:
- dest_key = source_key
- dest[dest_key] = source[source_key]
- del source[source_key]
-
- # These are moved to 'torrentview' sub dict
- for k in [
- 'sort_primary',
- 'sort_secondary',
- 'move_selection',
- 'separate_complete',
- ]:
- move_key(config, config['torrentview'], k)
-
- # These are moved to 'addtorrents' sub dict
- for k in [
- 'show_misc_files',
- 'show_hidden_folders',
- 'sort_column',
- 'reverse_sort',
- 'last_path',
- ]:
- move_key(config, config['addtorrents'], 'addtorrents_%s' % k, dest_key=k)
-
- # These are moved to 'cmdline' sub dict
- for k in [
- 'ignore_duplicate_lines',
- 'torrents_per_tab_press',
- 'third_tab_lists_all',
- ]:
- move_key(config, config['cmdline'], k)
-
- move_key(
- config,
- config['cmdline'],
- 'save_legacy_history',
- dest_key='save_command_history',
- )
-
- # Add key for localization
- config['language'] = DEFAULT_CONSOLE_PREFS['language']
-
- # Migrate column settings
- columns = [
- 'queue',
- 'size',
- 'state',
- 'progress',
- 'seeds',
- 'peers',
- 'downspeed',
- 'upspeed',
- 'eta',
- 'ratio',
- 'avail',
- 'added',
- 'tracker',
- 'savepath',
- 'downloaded',
- 'uploaded',
- 'remaining',
- 'owner',
- 'downloading_time',
- 'seeding_time',
- 'completed',
- 'seeds_peers_ratio',
- 'complete_seen',
- 'down_limit',
- 'up_limit',
- 'shared',
- 'name',
- ]
- column_name_mapping = {
- 'downspeed': 'download_speed',
- 'upspeed': 'upload_speed',
- 'added': 'time_added',
- 'savepath': 'download_location',
- 'completed': 'completed_time',
- 'complete_seen': 'last_seen_complete',
- 'down_limit': 'max_download_speed',
- 'up_limit': 'max_upload_speed',
- 'downloading_time': 'active_time',
- }
-
- from deluge.ui.console.modes.torrentlist.torrentview import default_columns
-
- # These are moved to 'torrentview.columns' sub dict
- for k in columns:
- column_name = column_name_mapping.get(k, k)
- config['torrentview']['columns'][column_name] = {}
- if k == 'name':
- config['torrentview']['columns'][column_name]['visible'] = True
- else:
- move_key(
- config,
- config['torrentview']['columns'][column_name],
- 'show_%s' % k,
- dest_key='visible',
- )
- move_key(
- config,
- config['torrentview']['columns'][column_name],
- '%s_width' % k,
- dest_key='width',
- )
- config['torrentview']['columns'][column_name]['order'] = default_columns[
- column_name
- ]['order']
-
- return config
-
-
-class EventLog(component.Component):
- """
- Prints out certain events as they are received from the core.
- """
-
- def __init__(self):
- component.Component.__init__(self, 'EventLog')
- self.console = component.get('ConsoleUI')
- self.prefix = '{!event!}* [%H:%M:%S] '
- self.date_change_format = 'On {!yellow!}%a, %d %b %Y{!input!} %Z:'
-
- client.register_event_handler('TorrentAddedEvent', self.on_torrent_added_event)
- client.register_event_handler(
- 'PreTorrentRemovedEvent', self.on_torrent_removed_event
- )
- client.register_event_handler(
- 'TorrentStateChangedEvent', self.on_torrent_state_changed_event
- )
- client.register_event_handler(
- 'TorrentFinishedEvent', self.on_torrent_finished_event
- )
- client.register_event_handler(
- 'NewVersionAvailableEvent', self.on_new_version_available_event
- )
- client.register_event_handler(
- 'SessionPausedEvent', self.on_session_paused_event
- )
- client.register_event_handler(
- 'SessionResumedEvent', self.on_session_resumed_event
- )
- client.register_event_handler(
- 'ConfigValueChangedEvent', self.on_config_value_changed_event
- )
- client.register_event_handler(
- 'PluginEnabledEvent', self.on_plugin_enabled_event
- )
- client.register_event_handler(
- 'PluginDisabledEvent', self.on_plugin_disabled_event
- )
-
- self.previous_time = time.localtime(0)
-
- def on_torrent_added_event(self, torrent_id, from_state):
- if from_state:
- return
-
- def on_torrent_status(status):
- self.write(
- '{!green!}Torrent Added: {!info!}%s ({!cyan!}%s{!info!})'
- % (status['name'], torrent_id)
- )
- # Write out what state the added torrent took
- self.on_torrent_state_changed_event(torrent_id, status['state'])
-
- client.core.get_torrent_status(torrent_id, ['name', 'state']).addCallback(
- on_torrent_status
- )
-
- def on_torrent_removed_event(self, torrent_id):
- self.write(
- '{!red!}Torrent Removed: {!info!}%s ({!cyan!}%s{!info!})'
- % (self.console.get_torrent_name(torrent_id), torrent_id)
- )
-
- def on_torrent_state_changed_event(self, torrent_id, state):
- # It's probably a new torrent, ignore it
- if not state:
- return
- # Modify the state string color
- if state in colors.state_color:
- state = colors.state_color[state] + state
-
- t_name = self.console.get_torrent_name(torrent_id)
-
- # Again, it's most likely a new torrent
- if not t_name:
- return
-
- self.write('%s: {!info!}%s ({!cyan!}%s{!info!})' % (state, t_name, torrent_id))
-
- def on_torrent_finished_event(self, torrent_id):
- if component.get('TorrentList').config['ring_bell']:
- import curses.beep
-
- curses.beep()
- self.write(
- '{!info!}Torrent Finished: %s ({!cyan!}%s{!info!})'
- % (self.console.get_torrent_name(torrent_id), torrent_id)
- )
-
- def on_new_version_available_event(self, version):
- self.write('{!input!}New Deluge version available: {!info!}%s' % (version))
-
- def on_session_paused_event(self):
- self.write('{!input!}Session Paused')
-
- def on_session_resumed_event(self):
- self.write('{!green!}Session Resumed')
-
- def on_config_value_changed_event(self, key, value):
- color = '{!white,black,bold!}'
- try:
- color = colors.type_color[type(value)]
- except KeyError:
- pass
-
- self.write('ConfigValueChanged: {!input!}%s: %s%s' % (key, color, value))
-
- def write(self, s):
- current_time = time.localtime()
-
- date_different = False
- for field in ['tm_mday', 'tm_mon', 'tm_year']:
- c = getattr(current_time, field)
- p = getattr(self.previous_time, field)
- if c != p:
- date_different = True
-
- if date_different:
- string = time.strftime(self.date_change_format)
- if deluge.common.PY2:
- string = string.decode()
- self.console.write_event(' ')
- self.console.write_event(string)
-
- p = time.strftime(self.prefix)
-
- self.console.write_event(p + s)
- self.previous_time = current_time
-
- def on_plugin_enabled_event(self, name):
- self.write('PluginEnabled: {!info!}%s' % name)
-
- def on_plugin_disabled_event(self, name):
- self.write('PluginDisabled: {!info!}%s' % name)
diff --git a/deluge/ui/console/modes/add_util.py b/deluge/ui/console/modes/add_util.py
index ac60b8974..9d29a1f4f 100644
--- a/deluge/ui/console/modes/add_util.py
+++ b/deluge/ui/console/modes/add_util.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -9,15 +8,11 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import glob
import logging
import os
from base64 import b64encode
-from six import unichr as chr # noqa: A001 shadowing
-
import deluge.common
from deluge.ui.client import client
from deluge.ui.common import TorrentInfo
diff --git a/deluge/ui/console/modes/addtorrents.py b/deluge/ui/console/modes/addtorrents.py
index 6b2c105d9..217b63d85 100644
--- a/deluge/ui/console/modes/addtorrents.py
+++ b/deluge/ui/console/modes/addtorrents.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2012 Arek Stefański <asmageddon@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
from base64 import b64encode
@@ -24,12 +21,6 @@ from deluge.ui.console.utils import format_utils
from deluge.ui.console.widgets.popup import InputPopup, MessagePopup
try:
- from future_builtins import zip
-except ImportError:
- # Ignore on Py3.
- pass
-
-try:
import curses
except ImportError:
pass
@@ -377,7 +368,7 @@ class AddTorrents(BaseMode):
def fail_cb(msg, t_file, ress):
log.debug('failed to add torrent: %s: %s', t_file, msg)
ress['fail'] += 1
- ress['fmsg'].append('{!input!} * %s: {!error!}%s' % (t_file, msg))
+ ress['fmsg'].append(f'{{!input!}} * {t_file}: {{!error!}}{msg}')
if (ress['succ'] + ress['fail']) >= ress['total']:
report_add_status(
component.get('TorrentList'),
@@ -526,9 +517,9 @@ class AddTorrents(BaseMode):
self.last_mark = self.cursel
elif chr(c) == 'j':
- self.scroll_list_up(1)
- elif chr(c) == 'k':
self.scroll_list_down(1)
+ elif chr(c) == 'k':
+ self.scroll_list_up(1)
elif chr(c) == 'M':
if self.last_mark != -1:
if self.last_mark > self.cursel:
diff --git a/deluge/ui/console/modes/basemode.py b/deluge/ui/console/modes/basemode.py
index 5267eae5a..a8ab1dbd9 100644
--- a/deluge/ui/console/modes/basemode.py
+++ b/deluge/ui/console/modes/basemode.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,10 +7,11 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
+import signal
+import struct
import sys
+from typing import Tuple
import deluge.component as component
import deluge.ui.console.utils.colors as colors
@@ -25,10 +25,8 @@ except ImportError:
pass
try:
- import signal
- import struct
- import termios
from fcntl import ioctl
+ from termios import TIOCGWINSZ
except ImportError:
pass
@@ -36,7 +34,7 @@ except ImportError:
log = logging.getLogger(__name__)
-class InputKeyHandler(object):
+class InputKeyHandler:
def __init__(self):
self._input_result = None
@@ -62,32 +60,35 @@ class InputKeyHandler(object):
return util.ReadState.IGNORED
-class TermResizeHandler(object):
+class TermResizeHandler:
def __init__(self):
try:
- signal.signal(signal.SIGWINCH, self.on_terminal_size)
+ signal.signal(signal.SIGWINCH, self.on_resize)
except ValueError as ex:
log.debug('TermResize unavailable, unable to catch SIGWINCH signal: %s', ex)
except AttributeError as ex:
log.debug('TermResize unavailable, no SIGWINCH signal on Windows: %s', ex)
- def on_terminal_size(self, *args):
- # Get the new rows and cols value
- rows, cols = struct.unpack('hhhh', ioctl(0, termios.TIOCGWINSZ, b'\000' * 8))[
- 0:2
- ]
+ @staticmethod
+ def get_window_size(fd: int = 0) -> Tuple[int, int]:
+ """Return the tty window size as row, col."""
+ return struct.unpack('4h', ioctl(fd, TIOCGWINSZ, b'\x00' * 8))[0:2]
+
+ def on_resize(self, _signum, _frame):
+ """Handler for SIGWINCH when terminal changes size"""
+ rows, cols = self.get_window_size()
curses.resizeterm(rows, cols)
return rows, cols
-class CursesStdIO(object):
+class CursesStdIO:
"""
fake fd to be registered as a reader with the twisted reactor.
Curses classes needing input should extend this
"""
def fileno(self):
- """ We want to select on FD 0 """
+ """We want to select on FD 0"""
return 0
def doRead(self): # NOQA: N802
diff --git a/deluge/ui/console/modes/cmdline.py b/deluge/ui/console/modes/cmdline.py
index 2735168db..7b0ff2dfc 100644
--- a/deluge/ui/console/modes/cmdline.py
+++ b/deluge/ui/console/modes/cmdline.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -8,16 +7,12 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import re
-from io import open
import deluge.component as component
import deluge.configmanager
-from deluge.common import PY2
from deluge.decorators import overrides
from deluge.ui.console.cmdline.command import Commander
from deluge.ui.console.modes.basemode import BaseMode, move_cursor
@@ -139,18 +134,18 @@ class CmdLine(BaseMode, Commander):
self._hf_lines = [0, 0]
if self.console_config['cmdline']['save_command_history']:
try:
- with open(self.history_file[0], 'r', encoding='utf8') as _file:
+ with open(self.history_file[0], encoding='utf8') as _file:
lines1 = _file.read().splitlines()
self._hf_lines[0] = len(lines1)
- except IOError:
+ except OSError:
lines1 = []
self._hf_lines[0] = 0
try:
- with open(self.history_file[1], 'r', encoding='utf8') as _file:
+ with open(self.history_file[1], encoding='utf8') as _file:
lines2 = _file.read().splitlines()
self._hf_lines[1] = len(lines2)
- except IOError:
+ except OSError:
lines2 = []
self._hf_lines[1] = 0
@@ -332,10 +327,10 @@ class CmdLine(BaseMode, Commander):
# A key to add to the input string
else:
- if c > 31 and c < 256:
+ if 31 < c < 256:
# Emulate getwch
stroke = chr(c)
- uchar = '' if PY2 else stroke
+ uchar = stroke
while not uchar:
try:
uchar = stroke.decode(self.encoding)
@@ -826,21 +821,21 @@ class CmdLine(BaseMode, Commander):
# Let's avoid listing all torrents twice if there's no pattern
if not empty and torrent_id.startswith(line):
# Highlight the matching part
- text = '{!info!}%s{!input!}%s - "%s"' % (
+ text = '{{!info!}}{}{{!input!}}{} - "{}"'.format(
torrent_id[:line_len],
torrent_id[line_len:],
torrent_name,
)
possible_matches.append(text)
if torrent_name.startswith(line):
- text = '{!info!}%s{!input!}%s ({!cyan!}%s{!input!})' % (
+ text = '{{!info!}}{}{{!input!}}{} ({{!cyan!}}{}{{!input!}})'.format(
escaped_name[:line_len],
escaped_name[line_len:],
torrent_id,
)
possible_matches.append(text)
elif torrent_name.lower().startswith(line.lower()):
- text = '{!info!}%s{!input!}%s ({!cyan!}%s{!input!})' % (
+ text = '{{!info!}}{}{{!input!}}{} ({{!cyan!}}{}{{!input!}})'.format(
escaped_name[:line_len],
escaped_name[line_len:],
torrent_id,
diff --git a/deluge/ui/console/modes/connectionmanager.py b/deluge/ui/console/modes/connectionmanager.py
index a5c596860..ce8b6f554 100644
--- a/deluge/ui/console/modes/connectionmanager.py
+++ b/deluge/ui/console/modes/connectionmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -33,9 +30,12 @@ class ConnectionManager(BaseMode, PopupsHandler):
self.all_torrents = None
self.hostlist = HostList()
BaseMode.__init__(self, stdscr, encoding=encoding)
- self.update_hosts_status()
def update_select_host_popup(self):
+ if self.popup and not isinstance(self.popup, SelectablePopup):
+ # Ignore MessagePopup on popup stack upon connect fail
+ return
+
selected_index = self.popup.current_selection() if self.popup else None
popup = SelectablePopup(
@@ -50,22 +50,24 @@ class ConnectionManager(BaseMode, PopupsHandler):
% (_('Quit'), _('Add Host'), _('Delete Host')),
space_below=True,
)
- self.push_popup(popup, clear=True)
for host_entry in self.hostlist.get_hosts_info():
host_id, hostname, port, user = host_entry
- args = {'data': host_id, 'foreground': 'red'}
- state = 'Offline'
- if host_id in self.statuses:
- state = 'Online'
- args.update({'data': self.statuses[host_id], 'foreground': 'green'})
- host_str = '%s:%d [%s]' % (hostname, port, state)
- self.popup.add_line(
+ host_status = self.statuses.get(host_id)
+
+ state = host_status[1] if host_status else 'Offline'
+ state_color = 'green' if state in ('Online', 'Connected') else 'red'
+ host_str = f'{hostname}:{port} [{state}]'
+
+ args = {'data': host_id, 'foreground': state_color}
+ popup.add_line(
host_id, host_str, selectable=True, use_underline=True, **args
)
if selected_index:
- self.popup.set_selection(selected_index)
+ popup.set_selection(selected_index)
+
+ self.push_popup(popup, clear=True)
self.inlist = True
self.refresh()
@@ -85,7 +87,7 @@ class ConnectionManager(BaseMode, PopupsHandler):
d.addCallback(on_console_start)
def _on_connect_fail(self, result):
- self.report_message('Failed to connect!', result)
+ self.report_message('Failed to connect!', result.getErrorMessage())
self.refresh()
if hasattr(result, 'getTraceback'):
log.exception(result)
@@ -125,12 +127,14 @@ class ConnectionManager(BaseMode, PopupsHandler):
def add_host(self, hostname, port, username, password):
log.info('Adding host: %s', hostname)
+ if port.isdecimal():
+ port = int(port)
try:
self.hostlist.add_host(hostname, port, username, password)
except ValueError as ex:
- self.report_message(_('Error adding host'), '%s: %s' % (hostname, ex))
+ self.report_message(_('Error adding host'), f'{hostname}: {ex}')
else:
- self.update_select_host_popup()
+ self.pop_popup()
def delete_host(self, host_id):
log.info('Deleting host: %s', host_id)
@@ -167,7 +171,9 @@ class ConnectionManager(BaseMode, PopupsHandler):
if not self.popup:
self.update_select_host_popup()
- self.popup.refresh()
+ if self.popup:
+ self.popup.refresh()
+
curses.doupdate()
@overrides(BaseMode)
@@ -191,7 +197,8 @@ class ConnectionManager(BaseMode, PopupsHandler):
if chr(c) == 'q':
return
elif chr(c) == 'D':
- host_id = self.popup.current_selection()[1]
+ host_index = self.popup.current_selection()
+ host_id = self.popup.inputs[host_index].name
self.delete_host(host_id)
return
elif chr(c) == 'a':
diff --git a/deluge/ui/console/modes/eventview.py b/deluge/ui/console/modes/eventview.py
index cd3308cf9..b6e63b019 100644
--- a/deluge/ui/console/modes/eventview.py
+++ b/deluge/ui/console/modes/eventview.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -100,9 +97,9 @@ class EventView(BaseMode):
elif c == curses.KEY_END:
self.offset += num_events
elif c == ord('j'):
- self.offset -= 1
- elif c == ord('k'):
self.offset += 1
+ elif c == ord('k'):
+ self.offset -= 1
if self.offset <= 0:
self.offset = 0
diff --git a/deluge/ui/console/modes/preferences/__init__.py b/deluge/ui/console/modes/preferences/__init__.py
index 15d77c4a8..e827d91a3 100644
--- a/deluge/ui/console/modes/preferences/__init__.py
+++ b/deluge/ui/console/modes/preferences/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from deluge.ui.console.modes.preferences.preferences import Preferences
__all__ = ['Preferences']
diff --git a/deluge/ui/console/modes/preferences/preference_panes.py b/deluge/ui/console/modes/preferences/preference_panes.py
index 62029a6a9..b47bc4b07 100644
--- a/deluge/ui/console/modes/preferences/preference_panes.py
+++ b/deluge/ui/console/modes/preferences/preference_panes.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,11 +6,9 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
-from deluge.common import is_ip
+from deluge.common import is_interface
from deluge.decorators import overrides
from deluge.i18n import get_languages
from deluge.ui.client import client
@@ -94,11 +91,12 @@ class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler):
)
elif ipt.name == 'listen_interface':
listen_interface = ipt.get_value().strip()
- if is_ip(listen_interface) or not listen_interface:
+ if is_interface(listen_interface) or not listen_interface:
conf_dict['listen_interface'] = listen_interface
elif ipt.name == 'outgoing_interface':
outgoing_interface = ipt.get_value().strip()
- conf_dict['outgoing_interface'] = outgoing_interface
+ if is_interface(outgoing_interface) or not outgoing_interface:
+ conf_dict['outgoing_interface'] = outgoing_interface
elif ipt.name.startswith('proxy_'):
if ipt.name == 'proxy_type':
conf_dict.setdefault('proxy', {})['type'] = ipt.get_value()
@@ -724,11 +722,6 @@ class CachePane(BasePreferencePane):
self.add_info_field(
'blocks_read', ' %s:' % _('Blocks Read'), status['disk.num_blocks_read']
)
- self.add_info_field(
- 'blocks_read_hit',
- ' %s:' % _('Blocks Read hit'),
- status['disk.num_blocks_cache_hits'],
- )
self.add_info_field('reads', ' %s:' % _('Reads'), status['disk.num_read_ops'])
self.add_info_field(
'read_hit_ratio',
diff --git a/deluge/ui/console/modes/preferences/preferences.py b/deluge/ui/console/modes/preferences/preferences.py
index 45a39a621..2c95323c6 100644
--- a/deluge/ui/console/modes/preferences/preferences.py
+++ b/deluge/ui/console/modes/preferences/preferences.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from collections import deque
@@ -74,7 +71,7 @@ arrow to edit the other value, and escape to get back to the check box.
"""
-class ZONE(object):
+class ZONE:
length = 3
CATEGORIES, PREFRENCES, ACTIONS = list(range(length))
diff --git a/deluge/ui/console/modes/torrentdetail.py b/deluge/ui/console/modes/torrentdetail.py
index 758cac878..4383d58a6 100644
--- a/deluge/ui/console/modes/torrentdetail.py
+++ b/deluge/ui/console/modes/torrentdetail.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
import deluge.component as component
@@ -184,7 +181,6 @@ class TorrentDetail(BaseMode, PopupsHandler):
self.refresh()
def set_state(self, state):
-
if state.get('files'):
self.full_names = {x['index']: x['path'] for x in state['files']}
@@ -366,7 +362,6 @@ class TorrentDetail(BaseMode, PopupsHandler):
).addCallback(self.set_state)
def draw_files(self, files, depth, off, idx):
-
color_selected = 'blue'
color_partially_selected = 'magenta'
color_highlighted = 'white'
@@ -425,9 +420,9 @@ class TorrentDetail(BaseMode, PopupsHandler):
attr = 'bold'
if attr:
- color_string = '{!%s,%s,%s!}' % (fg, bg, attr)
+ color_string = f'{{!{fg},{bg},{attr}!}}'
else:
- color_string = '{!%s,%s!}' % (fg, bg)
+ color_string = f'{{!{fg},{bg}!}}'
# actually draw the dir/file string
if fl[3] and fl[4]: # this is an expanded directory
@@ -439,7 +434,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
r = format_row(
[
- '%s%s %s' % (' ' * depth, xchar, fl[0]),
+ '{}{} {}'.format(' ' * depth, xchar, fl[0]),
fsize(fl[2]),
fl[5],
format_priority(fl[6]),
@@ -447,7 +442,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
self.column_widths,
)
- self.add_string(off, '%s%s' % (color_string, r), trim=False)
+ self.add_string(off, f'{color_string}{r}', trim=False)
off += 1
if fl[3] and fl[4]:
@@ -502,7 +497,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
download_color = colors.state_color['Downloading']
def add_field(name, row, pre_color='{!info!}', post_color='{!input!}'):
- s = '%s%s: %s%s' % (
+ s = '{}{}: {}{}'.format(
pre_color,
torrent_data_fields[name]['name'],
post_color,
@@ -523,7 +518,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
if status['progress'] != 100.0:
s += '/%s' % fsize(status['total_wanted'])
if status['download_payload_rate'] > 0:
- s += ' {!yellow!}@ %s%s' % (
+ s += ' {{!yellow!}}@ {}{}'.format(
download_color,
fsize(status['download_payload_rate']),
)
@@ -534,7 +529,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
# Print UL info and ratio
s = add_field('uploaded', 0, download_color)
if status['upload_payload_rate'] > 0:
- s += ' {!yellow!}@ %s%s' % (
+ s += ' {{!yellow!}}@ {}{}'.format(
colors.state_color['Seeding'],
fsize(status['upload_payload_rate']),
)
@@ -542,13 +537,13 @@ class TorrentDetail(BaseMode, PopupsHandler):
row = self.add_string(row, s)
# Seed/peer info
- s = '{!info!}%s:{!green!} %s {!input!}(%s)' % (
+ s = '{{!info!}}{}:{{!green!}} {} {{!input!}}({})'.format(
torrent_data_fields['seeds']['name'],
status['num_seeds'],
status['total_seeds'],
)
row = self.add_string(row, s)
- s = '{!info!}%s:{!red!} %s {!input!}(%s)' % (
+ s = '{{!info!}}{}:{{!red!}} {} {{!input!}}({})'.format(
torrent_data_fields['peers']['name'],
status['num_peers'],
status['total_peers'],
@@ -557,7 +552,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
# Tracker
tracker_color = '{!green!}' if status['message'] == 'OK' else '{!red!}'
- s = '{!info!}%s: {!magenta!}%s{!input!} says "%s%s{!input!}"' % (
+ s = '{{!info!}}{}: {{!magenta!}}{}{{!input!}} says "{}{}{{!input!}}"'.format(
torrent_data_fields['tracker']['name'],
status['tracker_host'],
tracker_color,
@@ -566,13 +561,13 @@ class TorrentDetail(BaseMode, PopupsHandler):
row = self.add_string(row, s)
# Pieces and availability
- s = '{!info!}%s: {!yellow!}%s {!input!}x {!yellow!}%s' % (
+ s = '{{!info!}}{}: {{!yellow!}}{} {{!input!}}x {{!yellow!}}{}'.format(
torrent_data_fields['pieces']['name'],
status['num_pieces'],
fsize(status['piece_length']),
)
if status['distributed_copies']:
- s += '{!info!}%s: {!input!}%s' % (
+ s += '{{!info!}}{}: {{!input!}}{}'.format(
torrent_data_fields['seed_rank']['name'],
status['seed_rank'],
)
@@ -878,7 +873,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
idx += 1
continue
if num == idx:
- return '%s%s/' % (path, element[0])
+ return f'{path}{element[0]}/'
if element[4]:
i = self._get_full_folder_path(
num, element[3], path + element[0] + '/', idx + 1
@@ -923,7 +918,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
self.popup.close(None, call_cb=False)
return
old_fname = self._get_full_folder_path(self.current_file_idx)
- new_fname = '%s/%s/' % (
+ new_fname = '{}/{}/'.format(
old_fname.strip('/').rpartition('/')[0],
result['new_foldername']['value'],
)
@@ -949,7 +944,7 @@ class TorrentDetail(BaseMode, PopupsHandler):
):
self.popup.close(None, call_cb=False)
return
- fname = '%s/%s' % (
+ fname = '{}/{}'.format(
self.full_names[idx].rpartition('/')[0],
result['new_filename']['value'],
)
@@ -1019,8 +1014,8 @@ class TorrentDetail(BaseMode, PopupsHandler):
elif c == ord('h'):
self.push_popup(MessagePopup(self, 'Help', HELP_STR, width_req=0.75))
elif c == ord('j'):
- self.file_list_up()
- elif c == ord('k'):
self.file_list_down()
+ elif c == ord('k'):
+ self.file_list_up()
self.refresh()
diff --git a/deluge/ui/console/modes/torrentlist/__init__.py b/deluge/ui/console/modes/torrentlist/__init__.py
index 18c4db377..48c60ce5a 100644
--- a/deluge/ui/console/modes/torrentlist/__init__.py
+++ b/deluge/ui/console/modes/torrentlist/__init__.py
@@ -1,7 +1,4 @@
-from __future__ import unicode_literals
-
-
-class ACTION(object):
+class ACTION:
PAUSE = 'pause'
RESUME = 'resume'
REANNOUNCE = 'update_tracker'
diff --git a/deluge/ui/console/modes/torrentlist/add_torrents_popup.py b/deluge/ui/console/modes/torrentlist/add_torrents_popup.py
index b0ac483a0..3ff9ab78d 100644
--- a/deluge/ui/console/modes/torrentlist/add_torrents_popup.py
+++ b/deluge/ui/console/modes/torrentlist/add_torrents_popup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.common
@@ -40,7 +37,7 @@ def show_torrent_add_popup(torrentlist):
def fail_cb(msg, url):
log.debug('failed to add torrent: %s: %s', url, msg)
- error_msg = '{!input!} * %s: {!error!}%s' % (url, msg)
+ error_msg = f'{{!input!}} * {url}: {{!error!}}{msg}'
report_add_status(torrentlist, 0, 1, [error_msg])
def success_cb(tid, url):
diff --git a/deluge/ui/console/modes/torrentlist/filtersidebar.py b/deluge/ui/console/modes/torrentlist/filtersidebar.py
index 0f39b5c3c..982e2457a 100644
--- a/deluge/ui/console/modes/torrentlist/filtersidebar.py
+++ b/deluge/ui/console/modes/torrentlist/filtersidebar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import curses
import logging
diff --git a/deluge/ui/console/modes/torrentlist/queue_mode.py b/deluge/ui/console/modes/torrentlist/queue_mode.py
index 0c44aafdf..33af0135d 100644
--- a/deluge/ui/console/modes/torrentlist/queue_mode.py
+++ b/deluge/ui/console/modes/torrentlist/queue_mode.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.ui.client import client
from deluge.ui.console.utils import curses_util as util
from deluge.ui.console.widgets.popup import MessagePopup, SelectablePopup
@@ -38,7 +35,7 @@ Change queue position of selected torrents
"""
-class QueueMode(object):
+class QueueMode:
def __init__(self, torrentslist, torrent_ids):
self.torrentslist = torrentslist
self.torrentview = torrentslist.torrentview
diff --git a/deluge/ui/console/modes/torrentlist/search_mode.py b/deluge/ui/console/modes/torrentlist/search_mode.py
index 57a8e5f64..6f79628fb 100644
--- a/deluge/ui/console/modes/torrentlist/search_mode.py
+++ b/deluge/ui/console/modes/torrentlist/search_mode.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,11 +6,8 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
-from deluge.common import PY2
from deluge.decorators import overrides
from deluge.ui.console.modes.basemode import InputKeyHandler, move_cursor
from deluge.ui.console.modes.torrentlist.torrentactions import torrent_actions_popup
@@ -49,7 +45,7 @@ SEARCH_FORMAT = {
class SearchMode(InputKeyHandler):
def __init__(self, torrentlist):
- super(SearchMode, self).__init__()
+ super().__init__()
self.torrentlist = torrentlist
self.torrentview = torrentlist.torrentview
self.search_state = SEARCH_EMPTY
@@ -176,7 +172,7 @@ class SearchMode(InputKeyHandler):
elif c > 31 and c < 256:
old_search_string = self.search_string
stroke = chr(c)
- uchar = '' if PY2 else stroke
+ uchar = stroke
while not uchar:
try:
uchar = stroke.decode(self.torrentlist.encoding)
diff --git a/deluge/ui/console/modes/torrentlist/torrentactions.py b/deluge/ui/console/modes/torrentlist/torrentactions.py
index f3cd39509..a153e1154 100644
--- a/deluge/ui/console/modes/torrentlist/torrentactions.py
+++ b/deluge/ui/console/modes/torrentlist/torrentactions.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
@@ -46,7 +43,7 @@ def action_remove(mode=None, torrent_ids=None, **kwargs):
if errors:
error_msgs = ''
for t_id, e_msg in errors:
- error_msgs += 'Error removing torrent %s : %s\n' % (t_id, e_msg)
+ error_msgs += f'Error removing torrent {t_id} : {e_msg}\n'
mode.report_message(
'Error(s) occured when trying to delete torrent(s).', error_msgs
)
@@ -77,7 +74,7 @@ def action_remove(mode=None, torrent_ids=None, **kwargs):
show_max = 6
for i, (name, state) in enumerate(status):
color = colors.state_color[state]
- rem_msg += '\n %s* {!input!}%s' % (color, name)
+ rem_msg += f'\n {color}* {{!input!}}{name}'
if i == show_max - 1:
if i < len(status) - 1:
rem_msg += '\n {!red!}And %i more' % (len(status) - show_max)
@@ -243,7 +240,6 @@ def torrent_action(action, *args, **kwargs):
# Creates the popup. mode is the calling mode, tids is a list of torrents to take action upon
def torrent_actions_popup(mode, torrent_ids, details=False, action=None, close_cb=None):
-
if action is not None:
torrent_action(action, mode=mode, torrent_ids=torrent_ids)
return
diff --git a/deluge/ui/console/modes/torrentlist/torrentlist.py b/deluge/ui/console/modes/torrentlist/torrentlist.py
index a427d65b0..d3c32ec0e 100644
--- a/deluge/ui/console/modes/torrentlist/torrentlist.py
+++ b/deluge/ui/console/modes/torrentlist/torrentlist.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from collections import deque
@@ -179,7 +176,7 @@ class TorrentList(BaseMode, PopupsHandler):
@overrides(BaseMode)
def resume(self):
- super(TorrentList, self).resume()
+ super().resume()
@overrides(BaseMode)
def on_resize(self, rows, cols):
@@ -222,7 +219,9 @@ class TorrentList(BaseMode, PopupsHandler):
# Update the status bars
statusbar_args = {'scr': self.stdscr, 'bottombar_help': True}
if self.torrentview.curr_filter is not None:
- statusbar_args['topbar'] = '%s {!filterstatus!}Current filter: %s' % (
+ statusbar_args[
+ 'topbar'
+ ] = '{} {{!filterstatus!}}Current filter: {}'.format(
self.statusbars.topbar,
self.torrentview.curr_filter,
)
diff --git a/deluge/ui/console/modes/torrentlist/torrentview.py b/deluge/ui/console/modes/torrentlist/torrentview.py
index 67de3e786..1ce509788 100644
--- a/deluge/ui/console/modes/torrentlist/torrentview.py
+++ b/deluge/ui/console/modes/torrentlist/torrentview.py
@@ -1,12 +1,9 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -90,7 +87,7 @@ for col_i, col_name in enumerate(torrentviewcolumns.column_pref_names):
class TorrentView(InputKeyHandler):
def __init__(self, torrentlist, config):
- super(TorrentView, self).__init__()
+ super().__init__()
self.torrentlist = torrentlist
self.config = config
self.filter_dict = {}
@@ -331,7 +328,7 @@ class TorrentView(InputKeyHandler):
self.torrentlist.add_string(
currow + self.torrentlist_offset,
- '%s%s' % (colorstr, row[0]),
+ f'{colorstr}{row[0]}',
trim=False,
scr=self.torrentlist.torrentview_panel,
)
@@ -467,9 +464,9 @@ class TorrentView(InputKeyHandler):
)
self.torrentlist.refresh()
elif c == ord('j'):
- affected_lines = self._scroll_up(1)
- elif c == ord('k'):
affected_lines = self._scroll_down(1)
+ elif c == ord('k'):
+ affected_lines = self._scroll_up(1)
elif c == ord('m'):
self.mark_unmark(self.cursel)
affected_lines = [self.cursel]
diff --git a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py
index 9dff84306..586a56978 100644
--- a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py
+++ b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.decorators import overrides
from deluge.ui.console.utils import curses_util as util
from deluge.ui.console.utils.column import torrent_data_fields
diff --git a/deluge/ui/console/parser.py b/deluge/ui/console/parser.py
index 1c0bd7009..c0686b156 100644
--- a/deluge/ui/console/parser.py
+++ b/deluge/ui/console/parser.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import argparse
import shlex
@@ -23,13 +20,13 @@ class OptionParserError(Exception):
class ConsoleBaseParser(argparse.ArgumentParser):
def format_help(self):
"""Differs from ArgumentParser.format_help by adding the raw epilog
- as formatted in the string. Default bahavior mangles the formatting.
+ as formatted in the string. Default behavior mangles the formatting.
"""
# Handle epilog manually to keep the text formatting
epilog = self.epilog
self.epilog = ''
- help_str = super(ConsoleBaseParser, self).format_help()
+ help_str = super().format_help()
if epilog is not None:
help_str += epilog
self.epilog = epilog
@@ -51,7 +48,7 @@ class ConsoleCommandParser(ConsoleBaseParser):
for cmd_line in cmd_lines:
cmds = shlex.split(cmd_line)
- cmd_options = super(ConsoleCommandParser, self).parse_args(args=cmds)
+ cmd_options = super().parse_args(args=cmds)
cmd_options.command = cmds[0]
command_options.append(cmd_options)
@@ -96,7 +93,7 @@ class ConsoleCommandParser(ConsoleBaseParser):
options = self.base_parser.parse_args(args=args)
options.parsed_cmds = []
else:
- options = super(ConsoleCommandParser, self).parse_args(args=args)
+ options = super().parse_args(args=args)
options.parsed_cmds = [options]
if not hasattr(options, 'remaining'):
@@ -107,7 +104,7 @@ class ConsoleCommandParser(ConsoleBaseParser):
class OptionParser(ConsoleBaseParser):
def __init__(self, **kwargs):
- super(OptionParser, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.formatter = ConsoleColorFormatter()
def exit(self, status=0, msg=None):
@@ -139,5 +136,5 @@ class OptionParser(ConsoleBaseParser):
def format_help(self):
"""Return help formatted with colors."""
- help_str = super(OptionParser, self).format_help()
+ help_str = super().format_help()
return self.formatter.format_colors(help_str)
diff --git a/deluge/ui/console/utils/colors.py b/deluge/ui/console/utils/colors.py
index 587c1f3f6..cc414fea5 100644
--- a/deluge/ui/console/utils/colors.py
+++ b/deluge/ui/console/utils/colors.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import re
@@ -91,8 +88,8 @@ def init_colors():
curses.init_pair(counter, fg, bg)
color_pairs[(fg_name, bg_name)] = counter
counter += 1
- except curses.error as ex:
- log.warning('Error: %s', ex)
+ except (curses.error, ValueError) as ex:
+ log.debug(f'Color pair {fg_name} {bg_name} not available: {ex}')
return counter
# Create the color_pairs dict
@@ -271,7 +268,7 @@ def parse_color_string(string):
last_color_attr = color_pair
attrs = attrs[2:] # Remove colors
except KeyError:
- raise BadColorString('Bad color value in tag: %s,%s' % (fg, bg))
+ raise BadColorString(f'Bad color value in tag: {fg},{bg}')
# Check for additional attributes and OR them to the color_pair
color_pair = apply_attrs(color_pair, attrs)
last_color_attr = color_pair
@@ -292,7 +289,7 @@ def parse_color_string(string):
return ret
-class ConsoleColorFormatter(object):
+class ConsoleColorFormatter:
"""
Format help in a way suited to deluge CmdLine mode - colors, format, indentation...
"""
diff --git a/deluge/ui/console/utils/column.py b/deluge/ui/console/utils/column.py
index d93215957..ecbe04ba3 100644
--- a/deluge/ui/console/utils/column.py
+++ b/deluge/ui/console/utils/column.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import copy
import logging
diff --git a/deluge/ui/console/utils/common.py b/deluge/ui/console/utils/common.py
index df1c07917..fdc88c402 100644
--- a/deluge/ui/console/utils/common.py
+++ b/deluge/ui/console/utils/common.py
@@ -1,12 +1,9 @@
-# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
TORRENT_OPTIONS = {
'max_download_speed': float,
'max_upload_speed': float,
diff --git a/deluge/ui/console/utils/config.py b/deluge/ui/console/utils/config.py
new file mode 100644
index 000000000..debb52a50
--- /dev/null
+++ b/deluge/ui/console/utils/config.py
@@ -0,0 +1,118 @@
+def migrate_1_to_2(config):
+ """Create better structure by moving most settings out of dict root
+ and into sub categories. Some keys are also renamed to be consistent
+ with other UIs.
+ """
+
+ def move_key(source, dest, source_key, dest_key=None):
+ if dest_key is None:
+ dest_key = source_key
+
+ dest[dest_key] = source[source_key]
+ del source[source_key]
+
+ # These are moved to 'torrentview' sub dict
+ for k in [
+ 'sort_primary',
+ 'sort_secondary',
+ 'move_selection',
+ 'separate_complete',
+ ]:
+ move_key(config, config['torrentview'], k)
+
+ # These are moved to 'addtorrents' sub dict
+ for k in [
+ 'show_misc_files',
+ 'show_hidden_folders',
+ 'sort_column',
+ 'reverse_sort',
+ 'last_path',
+ ]:
+ move_key(config, config['addtorrents'], 'addtorrents_%s' % k, dest_key=k)
+
+ # These are moved to 'cmdline' sub dict
+ for k in [
+ 'ignore_duplicate_lines',
+ 'torrents_per_tab_press',
+ 'third_tab_lists_all',
+ ]:
+ move_key(config, config['cmdline'], k)
+
+ move_key(
+ config,
+ config['cmdline'],
+ 'save_legacy_history',
+ dest_key='save_command_history',
+ )
+
+ # Add key for localization
+ config['language'] = ''
+
+ # Migrate column settings
+ columns = [
+ 'queue',
+ 'size',
+ 'state',
+ 'progress',
+ 'seeds',
+ 'peers',
+ 'downspeed',
+ 'upspeed',
+ 'eta',
+ 'ratio',
+ 'avail',
+ 'added',
+ 'tracker',
+ 'savepath',
+ 'downloaded',
+ 'uploaded',
+ 'remaining',
+ 'owner',
+ 'downloading_time',
+ 'seeding_time',
+ 'completed',
+ 'seeds_peers_ratio',
+ 'complete_seen',
+ 'down_limit',
+ 'up_limit',
+ 'shared',
+ 'name',
+ ]
+ column_name_mapping = {
+ 'downspeed': 'download_speed',
+ 'upspeed': 'upload_speed',
+ 'added': 'time_added',
+ 'savepath': 'download_location',
+ 'completed': 'completed_time',
+ 'complete_seen': 'last_seen_complete',
+ 'down_limit': 'max_download_speed',
+ 'up_limit': 'max_upload_speed',
+ 'downloading_time': 'active_time',
+ }
+
+ from deluge.ui.console.modes.torrentlist.torrentview import default_columns
+
+ # These are moved to 'torrentview.columns' sub dict
+ for k in columns:
+ column_name = column_name_mapping.get(k, k)
+ config['torrentview']['columns'][column_name] = {}
+ if k == 'name':
+ config['torrentview']['columns'][column_name]['visible'] = True
+ else:
+ move_key(
+ config,
+ config['torrentview']['columns'][column_name],
+ 'show_%s' % k,
+ dest_key='visible',
+ )
+ move_key(
+ config,
+ config['torrentview']['columns'][column_name],
+ '%s_width' % k,
+ dest_key='width',
+ )
+ config['torrentview']['columns'][column_name]['order'] = default_columns[
+ column_name
+ ]['order']
+
+ return config
diff --git a/deluge/ui/console/utils/curses_util.py b/deluge/ui/console/utils/curses_util.py
index a0cd6dc4b..50b044402 100644
--- a/deluge/ui/console/utils/curses_util.py
+++ b/deluge/ui/console/utils/curses_util.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
try:
import curses
except ImportError:
@@ -39,7 +36,7 @@ def is_int_chr(c):
return c > 47 and c < 58
-class Curser(object):
+class Curser:
INVISIBLE = 0
NORMAL = 1
VERY_VISIBLE = 2
@@ -59,7 +56,7 @@ def safe_curs_set(visibility):
pass
-class ReadState(object):
+class ReadState:
IGNORED = 0
READ = 1
CHANGED = 2
diff --git a/deluge/ui/console/utils/format_utils.py b/deluge/ui/console/utils/format_utils.py
index 029fb2011..50ec1915f 100644
--- a/deluge/ui/console/utils/format_utils.py
+++ b/deluge/ui/console/utils/format_utils.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import re
from collections import deque
from unicodedata import east_asian_width, normalize
@@ -98,7 +95,7 @@ def f_seedrank_dash(seed_rank, seeding_time):
def ftotal_sized(first, second):
- return '%s (%s)' % (
+ return '{} ({})'.format(
deluge.common.fsize(first, shortform=True),
deluge.common.fsize(second, shortform=True),
)
@@ -159,7 +156,7 @@ def format_column(col, lim):
if size >= lim - 1:
return trim_string(col, lim, dbls > 0)
else:
- return '%s%s' % (col, ' ' * (lim - size))
+ return '{}{}'.format(col, ' ' * (lim - size))
def format_row(row, column_widths):
@@ -213,7 +210,7 @@ def wrap_string(string, width, min_lines=0, strip_colors=True):
mtc = mtchs.popleft() - offset
clr = clrs.popleft()
end_pos += len(clr)
- s = '%s%s%s' % (s[:mtc], clr, s[mtc:])
+ s = f'{s[:mtc]}{clr}{s[mtc:]}'
return s
for s in s1:
@@ -238,11 +235,11 @@ def wrap_string(string, width, min_lines=0, strip_colors=True):
else:
cstr = s
- def append_indent(l, string, offset):
+ def append_indent(line, string, offset):
"""Prepends indent to string if specified"""
if indent and offset != 0:
string = indent + string
- l.append(string)
+ line.append(string)
while cstr:
# max with for a line. If indent is specified, we account for this
@@ -290,7 +287,7 @@ def wrap_string(string, width, min_lines=0, strip_colors=True):
last_color_string = ''
for i, line in enumerate(ret):
if i != 0:
- ret[i] = '%s%s' % (last_color_string, ret[i])
+ ret[i] = f'{last_color_string}{ret[i]}'
colors = re.findall('\\{![^!]+!\\}', line)
if colors:
@@ -313,9 +310,9 @@ def pad_string(string, length, character=' ', side='right'):
w = strwidth(string)
diff = length - w
if side == 'left':
- return '%s%s' % (character * diff, string)
+ return f'{character * diff}{string}'
elif side == 'right':
- return '%s%s' % (string, character * diff)
+ return f'{string}{character * diff}'
def delete_alt_backspace(input_text, input_cursor, sep_chars=' *?!._~-#$^;\'"/'):
diff --git a/deluge/ui/console/widgets/__init__.py b/deluge/ui/console/widgets/__init__.py
index a11e3f2b8..bc88a3b6b 100644
--- a/deluge/ui/console/widgets/__init__.py
+++ b/deluge/ui/console/widgets/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from deluge.ui.console.widgets.inputpane import BaseInputPane
from deluge.ui.console.widgets.statusbars import StatusBars
from deluge.ui.console.widgets.window import BaseWindow
diff --git a/deluge/ui/console/widgets/fields.py b/deluge/ui/console/widgets/fields.py
index 021cab738..d8d892d52 100644
--- a/deluge/ui/console/widgets/fields.py
+++ b/deluge/ui/console/widgets/fields.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
@@ -9,12 +8,9 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
-from deluge.common import PY2
from deluge.decorators import overrides
from deluge.ui.console.modes.basemode import InputKeyHandler
from deluge.ui.console.utils import colors
@@ -35,7 +31,7 @@ log = logging.getLogger(__name__)
class BaseField(InputKeyHandler):
def __init__(self, parent=None, name=None, selectable=True, **kwargs):
- super(BaseField, self).__init__()
+ super().__init__()
self.name = name
self.parent = parent
self.fmt_keys = {}
@@ -74,7 +70,7 @@ class BaseField(InputKeyHandler):
def build_fmt_string(self, focused, active, value_key='msg', **kwargs):
color_key, font_key = self.get_fmt_keys(focused, active, **kwargs)
- return '{!%%(%s)s,%%(%s)s!}%%(%s)s{!%%(%s)s!}' % (
+ return '{{!%({})s,%({})s!}}%({})s{{!%({})s!}}'.format(
color_key,
font_key,
value_key,
@@ -176,7 +172,7 @@ class InfoField(NoInputField):
NoInputField.__init__(self, parent=parent, name=name, **kwargs)
self.label = label
self.value = value
- self.txt = '%s %s' % (label, value)
+ self.txt = f'{label} {value}'
@overrides(BaseField)
def render(self, screen, row, col=0, **kwargs):
@@ -187,9 +183,9 @@ class InfoField(NoInputField):
def set_value(self, v):
self.value = v
if isinstance(v, float):
- self.txt = '%s %.2f' % (self.label, self.value)
+ self.txt = f'{self.label} {self.value:.2f}'
else:
- self.txt = '%s %s' % (self.label, self.value)
+ self.txt = f'{self.label} {self.value}'
class CheckedInput(InputField):
@@ -202,7 +198,7 @@ class CheckedInput(InputField):
checked_char='X',
unchecked_char=' ',
checkbox_format='[%s] ',
- **kwargs
+ **kwargs,
):
InputField.__init__(self, parent, name, message, **kwargs)
self.set_value(checked)
@@ -231,9 +227,7 @@ class CheckedInput(InputField):
@overrides(BaseField)
def get_fmt_keys(self, focused, active, **kwargs):
- color_key, font_key = super(CheckedInput, self).get_fmt_keys(
- focused, active, **kwargs
- )
+ color_key, font_key = super().get_fmt_keys(focused, active, **kwargs)
if self.checked:
color_key += '_checked'
font_key += '_checked'
@@ -284,7 +278,7 @@ class CheckedPlusInput(CheckedInput):
child_always_visible=False,
show_usage_hints=True,
msg_fmt='%s ',
- **kwargs
+ **kwargs,
):
CheckedInput.__init__(self, parent, name, message, **kwargs)
self.child = child
@@ -372,7 +366,7 @@ class IntSpinInput(InputField):
incr_large=10,
strict_validation=False,
fmt='%d',
- **kwargs
+ **kwargs,
):
InputField.__init__(self, parent, name, message, **kwargs)
self.convert_func = int
@@ -618,7 +612,7 @@ class SelectInput(InputField):
active_index,
active_default=False,
require_select_action=True,
- **kwargs
+ **kwargs,
):
InputField.__init__(self, parent, name, message, **kwargs)
self.opts = opts
@@ -667,9 +661,7 @@ class SelectInput(InputField):
@overrides(BaseField)
def get_fmt_keys(self, focused, active, selected=False, **kwargs):
- color_key, font_key = super(SelectInput, self).get_fmt_keys(
- focused, active, **kwargs
- )
+ color_key, font_key = super().get_fmt_keys(focused, active, **kwargs)
if selected:
color_key += '_selected'
font_key += '_selected'
@@ -739,7 +731,7 @@ class TextInput(InputField):
value,
complete=False,
activate_input=False,
- **kwargs
+ **kwargs,
):
InputField.__init__(self, parent, name, message, **kwargs)
self.move_func = move_func
@@ -815,7 +807,7 @@ class TextInput(InputField):
focused=True,
col=0,
cursor_offset=0,
- **kwargs
+ **kwargs,
):
if not self.value and not active and len(self.default_value) != 0:
self.value = self.default_value
@@ -951,7 +943,7 @@ class TextInput(InputField):
elif c > 31 and c < 256:
# Emulate getwch
stroke = chr(c)
- uchar = '' if PY2 else stroke
+ uchar = stroke
while not uchar:
try:
uchar = stroke.decode(self.parent.encoding)
@@ -1081,7 +1073,7 @@ class ComboInput(InputField):
choice[1],
selectable=True,
selected=choice[0] == self.get_value(),
- **args
+ **args,
)
self.parent.push_popup(select_popup)
return util.ReadState.CHANGED
@@ -1149,7 +1141,7 @@ class TextArea(TextField):
for i, line in enumerate(lines):
self.parent.add_string(
row + i,
- '%s%s' % (color, line),
+ f'{color}{line}',
scr=screen,
col=col,
pad=False,
@@ -1176,7 +1168,7 @@ class DividerField(NoInputField):
selectable=False,
fill_width=True,
value_fmt='%s',
- **kwargs
+ **kwargs,
):
NoInputField.__init__(
self, parent=parent, name=name, selectable=selectable, **kwargs
diff --git a/deluge/ui/console/widgets/inputpane.py b/deluge/ui/console/widgets/inputpane.py
index 097a6cb8d..d8d217501 100644
--- a/deluge/ui/console/widgets/inputpane.py
+++ b/deluge/ui/console/widgets/inputpane.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
@@ -9,8 +8,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.decorators import overrides
diff --git a/deluge/ui/console/widgets/popup.py b/deluge/ui/console/widgets/popup.py
index d588bbb24..07d667d27 100644
--- a/deluge/ui/console/widgets/popup.py
+++ b/deluge/ui/console/widgets/popup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.decorators import overrides
@@ -25,7 +22,7 @@ except ImportError:
log = logging.getLogger(__name__)
-class ALIGN(object):
+class ALIGN:
TOP_LEFT = 1
TOP_CENTER = 2
TOP_RIGHT = 3
@@ -38,7 +35,7 @@ class ALIGN(object):
DEFAULT = MIDDLE_CENTER
-class PopupsHandler(object):
+class PopupsHandler:
def __init__(self):
self._popups = []
@@ -133,7 +130,6 @@ class Popup(BaseWindow, InputKeyHandler):
BaseWindow.refresh(self)
def calculate_size(self):
-
if isinstance(self.height_req, float) and 0.0 < self.height_req <= 1.0:
height = int((self.parent.rows - 2) * self.height_req)
else:
@@ -255,7 +251,7 @@ class SelectablePopup(BaseInputPane, Popup):
def set_selection(self, index):
"""Set a selected index"""
- self.active_input = index
+ self.active_input = min(index, len(self.inputs) - 1)
def add_line(
self,
diff --git a/deluge/ui/console/widgets/sidebar.py b/deluge/ui/console/widgets/sidebar.py
index cc237174d..4015a1375 100644
--- a/deluge/ui/console/widgets/sidebar.py
+++ b/deluge/ui/console/widgets/sidebar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import curses
import logging
diff --git a/deluge/ui/console/widgets/statusbars.py b/deluge/ui/console/widgets/statusbars.py
index fcf4f2f41..1b9173707 100644
--- a/deluge/ui/console/widgets/statusbars.py
+++ b/deluge/ui/console/widgets/statusbars.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,13 +6,12 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import deluge.common
import deluge.component as component
-from deluge.core.preferencesmanager import DEFAULT_PREFS
from deluge.ui.client import client
+DEFAULT_DAEMON_PORT = 58846
+
class StatusBars(component.Component):
def __init__(self):
@@ -38,19 +36,23 @@ class StatusBars(component.Component):
def on_get_session_status(status):
self.upload = deluge.common.fsize(status['payload_upload_rate'])
self.download = deluge.common.fsize(status['payload_download_rate'])
- self.connections = status['num_peers']
+ self.connections = status['peer.num_peers_connected']
if 'dht_nodes' in status:
- self.dht = status['dht_nodes']
+ self.dht = status['dht.dht_nodes']
self.update_statusbars()
def on_get_external_ip(external_ip):
self.external_ip = external_ip
- keys = ['num_peers', 'payload_upload_rate', 'payload_download_rate']
+ keys = [
+ 'peer.num_peers_connected',
+ 'payload_upload_rate',
+ 'payload_download_rate',
+ ]
if self.config['dht']:
- keys.append('dht_nodes')
+ keys.append('dht.dht_nodes')
client.core.get_session_status(keys).addCallback(on_get_session_status)
client.core.get_external_ip().addCallback(on_get_external_ip)
@@ -76,7 +78,7 @@ class StatusBars(component.Component):
connection_info += '{!white,blue,bold!}@{!red,blue,bold!}%s'
# Port
- if info[1] == DEFAULT_PREFS['daemon_port']:
+ if info[1] == DEFAULT_DAEMON_PORT:
connection_info += '{!white,blue!}:%s'
else:
connection_info += '{!status!}:%s'
diff --git a/deluge/ui/console/widgets/window.py b/deluge/ui/console/widgets/window.py
index 2ef35281e..77aff8817 100644
--- a/deluge/ui/console/widgets/window.py
+++ b/deluge/ui/console/widgets/window.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
@@ -9,8 +8,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from deluge.ui.console.modes.basemode import add_string, mkpad, mkpanel
@@ -24,7 +21,7 @@ except ImportError:
log = logging.getLogger(__name__)
-class BaseWindow(object):
+class BaseWindow:
"""
BaseWindow creates a curses screen to be used for showing panels and popup dialogs
"""
diff --git a/deluge/ui/coreconfig.py b/deluge/ui/coreconfig.py
index ed6b614a2..1e2927b5e 100644
--- a/deluge/ui/coreconfig.py
+++ b/deluge/ui/coreconfig.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
diff --git a/deluge/ui/countries.py b/deluge/ui/countries.py
index af390853e..eb94df6d9 100644
--- a/deluge/ui/countries.py
+++ b/deluge/ui/countries.py
@@ -1,10 +1,7 @@
-# -*- coding: utf-8 -*-
#
# This file is public domain.
#
-from __future__ import unicode_literals
-
# ISO 3166-1 country names and codes
COUNTRIES = {
'AF': _('Afghanistan'),
diff --git a/deluge/ui/data/icons/hicolor/128x128/apps/deluge.png b/deluge/ui/data/icons/hicolor/128x128/apps/deluge.png
index 48fcc473e..32aa26ae2 100644
--- a/deluge/ui/data/icons/hicolor/128x128/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/128x128/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.png b/deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.png
index 2f4ae4c70..1291dfaeb 100644
--- a/deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.png
+++ b/deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png b/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png
index 2f4ae4c70..1291dfaeb 100644
--- a/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png b/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png
index 5d54ea41e..93c6feb44 100644
--- a/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.png b/deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.png
index 13fe852ad..95b13e359 100644
--- a/deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.png
+++ b/deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png b/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png
index 13fe852ad..95b13e359 100644
--- a/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.png b/deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.png
index 3a345eb44..96ab75325 100644
--- a/deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.png
+++ b/deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png b/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png
index 3a345eb44..96ab75325 100644
--- a/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png b/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png
index ee5d2909b..4ba3bb997 100644
--- a/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png b/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png
index 6787fa39a..916f13632 100644
--- a/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png b/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png
index 4050041f5..92d35fa0f 100644
--- a/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png b/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png
index 7b067ac94..95592bb49 100644
--- a/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png b/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png
index 70cd91af5..464dd69f5 100644
--- a/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png b/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png
index 427556373..fb87321a5 100644
--- a/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png b/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png
index 7ba0efb1c..f313ed32c 100644
--- a/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png b/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png
index 2c64ec8b9..9b8d3abfe 100644
--- a/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png
+++ b/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/active16.png b/deluge/ui/data/pixmaps/active16.png
index c9af82a5f..e5bab66cd 100644
--- a/deluge/ui/data/pixmaps/active16.png
+++ b/deluge/ui/data/pixmaps/active16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/alert16.png b/deluge/ui/data/pixmaps/alert16.png
index 703663813..49028db9c 100644
--- a/deluge/ui/data/pixmaps/alert16.png
+++ b/deluge/ui/data/pixmaps/alert16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/all16.png b/deluge/ui/data/pixmaps/all16.png
index c63f8df1a..1a9ba9092 100644
--- a/deluge/ui/data/pixmaps/all16.png
+++ b/deluge/ui/data/pixmaps/all16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/checking16.png b/deluge/ui/data/pixmaps/checking16.png
index 6758e36e8..9961675c4 100644
--- a/deluge/ui/data/pixmaps/checking16.png
+++ b/deluge/ui/data/pixmaps/checking16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/deluge-about.png b/deluge/ui/data/pixmaps/deluge-about.png
index 39322eb71..0b4ff4e6b 100644
--- a/deluge/ui/data/pixmaps/deluge-about.png
+++ b/deluge/ui/data/pixmaps/deluge-about.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/deluge.ico b/deluge/ui/data/pixmaps/deluge.ico
index d946d1114..854f867a5 100644
--- a/deluge/ui/data/pixmaps/deluge.ico
+++ b/deluge/ui/data/pixmaps/deluge.ico
Binary files differ
diff --git a/deluge/ui/data/pixmaps/deluge.png b/deluge/ui/data/pixmaps/deluge.png
index 7b067ac94..95592bb49 100644
--- a/deluge/ui/data/pixmaps/deluge.png
+++ b/deluge/ui/data/pixmaps/deluge.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/deluge16.png b/deluge/ui/data/pixmaps/deluge16.png
index 5afdbe4c6..1291dfaeb 100644
--- a/deluge/ui/data/pixmaps/deluge16.png
+++ b/deluge/ui/data/pixmaps/deluge16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/dht16.png b/deluge/ui/data/pixmaps/dht16.png
index 363ee0c0f..2396bb132 100644
--- a/deluge/ui/data/pixmaps/dht16.png
+++ b/deluge/ui/data/pixmaps/dht16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/downloading16.png b/deluge/ui/data/pixmaps/downloading16.png
index 24d6ffa38..e64aa5adc 100644
--- a/deluge/ui/data/pixmaps/downloading16.png
+++ b/deluge/ui/data/pixmaps/downloading16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ad.png b/deluge/ui/data/pixmaps/flags/ad.png
index 93656aa35..cc5ab1e13 100644
--- a/deluge/ui/data/pixmaps/flags/ad.png
+++ b/deluge/ui/data/pixmaps/flags/ad.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ae.png b/deluge/ui/data/pixmaps/flags/ae.png
index e3abee163..590256f8a 100644
--- a/deluge/ui/data/pixmaps/flags/ae.png
+++ b/deluge/ui/data/pixmaps/flags/ae.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/af.png b/deluge/ui/data/pixmaps/flags/af.png
index 8506736ad..d8faed3c9 100644
--- a/deluge/ui/data/pixmaps/flags/af.png
+++ b/deluge/ui/data/pixmaps/flags/af.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ag.png b/deluge/ui/data/pixmaps/flags/ag.png
index ba1aff986..7c6d7dbab 100644
--- a/deluge/ui/data/pixmaps/flags/ag.png
+++ b/deluge/ui/data/pixmaps/flags/ag.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/al.png b/deluge/ui/data/pixmaps/flags/al.png
index 9b56fcba0..71f9d0aae 100644
--- a/deluge/ui/data/pixmaps/flags/al.png
+++ b/deluge/ui/data/pixmaps/flags/al.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/am.png b/deluge/ui/data/pixmaps/flags/am.png
index 83ac72eb3..8bc88a26e 100644
--- a/deluge/ui/data/pixmaps/flags/am.png
+++ b/deluge/ui/data/pixmaps/flags/am.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/an.png b/deluge/ui/data/pixmaps/flags/an.png
index 09cfdb128..49220a4c9 100644
--- a/deluge/ui/data/pixmaps/flags/an.png
+++ b/deluge/ui/data/pixmaps/flags/an.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ao.png b/deluge/ui/data/pixmaps/flags/ao.png
index c2004d618..9f1dcb46a 100644
--- a/deluge/ui/data/pixmaps/flags/ao.png
+++ b/deluge/ui/data/pixmaps/flags/ao.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/aq.png b/deluge/ui/data/pixmaps/flags/aq.png
index 76fe736cb..de9bc8f2c 100644
--- a/deluge/ui/data/pixmaps/flags/aq.png
+++ b/deluge/ui/data/pixmaps/flags/aq.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ar.png b/deluge/ui/data/pixmaps/flags/ar.png
index f16a290b7..e3a87c893 100644
--- a/deluge/ui/data/pixmaps/flags/ar.png
+++ b/deluge/ui/data/pixmaps/flags/ar.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/as.png b/deluge/ui/data/pixmaps/flags/as.png
index e38ce4323..1d00ab05a 100644
--- a/deluge/ui/data/pixmaps/flags/as.png
+++ b/deluge/ui/data/pixmaps/flags/as.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/at.png b/deluge/ui/data/pixmaps/flags/at.png
index 25c7e421f..475bd128e 100644
--- a/deluge/ui/data/pixmaps/flags/at.png
+++ b/deluge/ui/data/pixmaps/flags/at.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/au.png b/deluge/ui/data/pixmaps/flags/au.png
index 0de18f0b2..0335dbfc8 100644
--- a/deluge/ui/data/pixmaps/flags/au.png
+++ b/deluge/ui/data/pixmaps/flags/au.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/aw.png b/deluge/ui/data/pixmaps/flags/aw.png
index 788d7385c..fc58befc3 100644
--- a/deluge/ui/data/pixmaps/flags/aw.png
+++ b/deluge/ui/data/pixmaps/flags/aw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ax.png b/deluge/ui/data/pixmaps/flags/ax.png
index 0060c3566..134b2ac51 100644
--- a/deluge/ui/data/pixmaps/flags/ax.png
+++ b/deluge/ui/data/pixmaps/flags/ax.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/az.png b/deluge/ui/data/pixmaps/flags/az.png
index cb9c9a171..e225bd227 100644
--- a/deluge/ui/data/pixmaps/flags/az.png
+++ b/deluge/ui/data/pixmaps/flags/az.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ba.png b/deluge/ui/data/pixmaps/flags/ba.png
index 160c5e2ab..bc8732550 100644
--- a/deluge/ui/data/pixmaps/flags/ba.png
+++ b/deluge/ui/data/pixmaps/flags/ba.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bd.png b/deluge/ui/data/pixmaps/flags/bd.png
index 10266cd50..23fea0e64 100644
--- a/deluge/ui/data/pixmaps/flags/bd.png
+++ b/deluge/ui/data/pixmaps/flags/bd.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bf.png b/deluge/ui/data/pixmaps/flags/bf.png
index 452329a01..aa7238bce 100644
--- a/deluge/ui/data/pixmaps/flags/bf.png
+++ b/deluge/ui/data/pixmaps/flags/bf.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bh.png b/deluge/ui/data/pixmaps/flags/bh.png
index 1b876e211..812126b66 100644
--- a/deluge/ui/data/pixmaps/flags/bh.png
+++ b/deluge/ui/data/pixmaps/flags/bh.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bi.png b/deluge/ui/data/pixmaps/flags/bi.png
index f4d9adf92..21ead6b8d 100644
--- a/deluge/ui/data/pixmaps/flags/bi.png
+++ b/deluge/ui/data/pixmaps/flags/bi.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bj.png b/deluge/ui/data/pixmaps/flags/bj.png
index 5740ccc5d..6b9a53899 100644
--- a/deluge/ui/data/pixmaps/flags/bj.png
+++ b/deluge/ui/data/pixmaps/flags/bj.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bm.png b/deluge/ui/data/pixmaps/flags/bm.png
index 85411bff3..c1859d1d7 100644
--- a/deluge/ui/data/pixmaps/flags/bm.png
+++ b/deluge/ui/data/pixmaps/flags/bm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bn.png b/deluge/ui/data/pixmaps/flags/bn.png
index a0c223f48..a18d496f0 100644
--- a/deluge/ui/data/pixmaps/flags/bn.png
+++ b/deluge/ui/data/pixmaps/flags/bn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bo.png b/deluge/ui/data/pixmaps/flags/bo.png
index c2ef0f42f..1b25cd93f 100644
--- a/deluge/ui/data/pixmaps/flags/bo.png
+++ b/deluge/ui/data/pixmaps/flags/bo.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bs.png b/deluge/ui/data/pixmaps/flags/bs.png
index 959252d65..dec86f3ac 100644
--- a/deluge/ui/data/pixmaps/flags/bs.png
+++ b/deluge/ui/data/pixmaps/flags/bs.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bt.png b/deluge/ui/data/pixmaps/flags/bt.png
index 2e1969652..3942fc987 100644
--- a/deluge/ui/data/pixmaps/flags/bt.png
+++ b/deluge/ui/data/pixmaps/flags/bt.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bv.png b/deluge/ui/data/pixmaps/flags/bv.png
index ae9060caf..faaa529d1 100644
--- a/deluge/ui/data/pixmaps/flags/bv.png
+++ b/deluge/ui/data/pixmaps/flags/bv.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bw.png b/deluge/ui/data/pixmaps/flags/bw.png
index 2dee7fb3c..d5e0284e4 100644
--- a/deluge/ui/data/pixmaps/flags/bw.png
+++ b/deluge/ui/data/pixmaps/flags/bw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/by.png b/deluge/ui/data/pixmaps/flags/by.png
index 29361d693..a6c5f6c16 100644
--- a/deluge/ui/data/pixmaps/flags/by.png
+++ b/deluge/ui/data/pixmaps/flags/by.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/bz.png b/deluge/ui/data/pixmaps/flags/bz.png
index 88e4ea1b3..63f74285a 100644
--- a/deluge/ui/data/pixmaps/flags/bz.png
+++ b/deluge/ui/data/pixmaps/flags/bz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ca.png b/deluge/ui/data/pixmaps/flags/ca.png
index 155eea8ce..1e53d20a9 100644
--- a/deluge/ui/data/pixmaps/flags/ca.png
+++ b/deluge/ui/data/pixmaps/flags/ca.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cc.png b/deluge/ui/data/pixmaps/flags/cc.png
index 93509ae3a..a518e487d 100644
--- a/deluge/ui/data/pixmaps/flags/cc.png
+++ b/deluge/ui/data/pixmaps/flags/cc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cf.png b/deluge/ui/data/pixmaps/flags/cf.png
index 3951b789f..1b9ea85bd 100644
--- a/deluge/ui/data/pixmaps/flags/cf.png
+++ b/deluge/ui/data/pixmaps/flags/cf.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cg.png b/deluge/ui/data/pixmaps/flags/cg.png
index 6f32484ff..45422bba1 100644
--- a/deluge/ui/data/pixmaps/flags/cg.png
+++ b/deluge/ui/data/pixmaps/flags/cg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ci.png b/deluge/ui/data/pixmaps/flags/ci.png
index ee1740685..5f670c7ec 100644
--- a/deluge/ui/data/pixmaps/flags/ci.png
+++ b/deluge/ui/data/pixmaps/flags/ci.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ck.png b/deluge/ui/data/pixmaps/flags/ck.png
index 043a12f7a..904192b4d 100644
--- a/deluge/ui/data/pixmaps/flags/ck.png
+++ b/deluge/ui/data/pixmaps/flags/ck.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cl.png b/deluge/ui/data/pixmaps/flags/cl.png
index 6c05438c1..afb270e7a 100644
--- a/deluge/ui/data/pixmaps/flags/cl.png
+++ b/deluge/ui/data/pixmaps/flags/cl.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cm.png b/deluge/ui/data/pixmaps/flags/cm.png
index e2a7c0a14..395dd9fac 100644
--- a/deluge/ui/data/pixmaps/flags/cm.png
+++ b/deluge/ui/data/pixmaps/flags/cm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cn.png b/deluge/ui/data/pixmaps/flags/cn.png
index 5a893ee6b..28ab66c84 100644
--- a/deluge/ui/data/pixmaps/flags/cn.png
+++ b/deluge/ui/data/pixmaps/flags/cn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/co.png b/deluge/ui/data/pixmaps/flags/co.png
index 24eb981b0..d31a69d36 100644
--- a/deluge/ui/data/pixmaps/flags/co.png
+++ b/deluge/ui/data/pixmaps/flags/co.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cr.png b/deluge/ui/data/pixmaps/flags/cr.png
index 4efd96730..e892b41c0 100644
--- a/deluge/ui/data/pixmaps/flags/cr.png
+++ b/deluge/ui/data/pixmaps/flags/cr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cu.png b/deluge/ui/data/pixmaps/flags/cu.png
index addfb8e73..64205edc3 100644
--- a/deluge/ui/data/pixmaps/flags/cu.png
+++ b/deluge/ui/data/pixmaps/flags/cu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cv.png b/deluge/ui/data/pixmaps/flags/cv.png
index 6411a6279..54f1abdca 100644
--- a/deluge/ui/data/pixmaps/flags/cv.png
+++ b/deluge/ui/data/pixmaps/flags/cv.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cx.png b/deluge/ui/data/pixmaps/flags/cx.png
index 75c8bfaed..198e299e1 100644
--- a/deluge/ui/data/pixmaps/flags/cx.png
+++ b/deluge/ui/data/pixmaps/flags/cx.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/cy.png b/deluge/ui/data/pixmaps/flags/cy.png
index bbacfbe0c..6993e6b6b 100644
--- a/deluge/ui/data/pixmaps/flags/cy.png
+++ b/deluge/ui/data/pixmaps/flags/cy.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/de.png b/deluge/ui/data/pixmaps/flags/de.png
index 933014a30..3d078604c 100644
--- a/deluge/ui/data/pixmaps/flags/de.png
+++ b/deluge/ui/data/pixmaps/flags/de.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/dj.png b/deluge/ui/data/pixmaps/flags/dj.png
index 883343a4a..ca361d0bd 100644
--- a/deluge/ui/data/pixmaps/flags/dj.png
+++ b/deluge/ui/data/pixmaps/flags/dj.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/dk.png b/deluge/ui/data/pixmaps/flags/dk.png
index 408eea295..3b4d12d94 100644
--- a/deluge/ui/data/pixmaps/flags/dk.png
+++ b/deluge/ui/data/pixmaps/flags/dk.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/dm.png b/deluge/ui/data/pixmaps/flags/dm.png
index e7671bc4b..2767c49bc 100644
--- a/deluge/ui/data/pixmaps/flags/dm.png
+++ b/deluge/ui/data/pixmaps/flags/dm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/do.png b/deluge/ui/data/pixmaps/flags/do.png
index deefc7724..df9b90d33 100644
--- a/deluge/ui/data/pixmaps/flags/do.png
+++ b/deluge/ui/data/pixmaps/flags/do.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/dz.png b/deluge/ui/data/pixmaps/flags/dz.png
index 65028f9b5..a450eeffe 100644
--- a/deluge/ui/data/pixmaps/flags/dz.png
+++ b/deluge/ui/data/pixmaps/flags/dz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ec.png b/deluge/ui/data/pixmaps/flags/ec.png
index 7f93b49f5..26b50cdb4 100644
--- a/deluge/ui/data/pixmaps/flags/ec.png
+++ b/deluge/ui/data/pixmaps/flags/ec.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/eg.png b/deluge/ui/data/pixmaps/flags/eg.png
index 7219431c1..26e80b9f5 100644
--- a/deluge/ui/data/pixmaps/flags/eg.png
+++ b/deluge/ui/data/pixmaps/flags/eg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/eh.png b/deluge/ui/data/pixmaps/flags/eh.png
index ae7daca43..39982d297 100644
--- a/deluge/ui/data/pixmaps/flags/eh.png
+++ b/deluge/ui/data/pixmaps/flags/eh.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/er.png b/deluge/ui/data/pixmaps/flags/er.png
index b3644d4a2..d30b89f4a 100644
--- a/deluge/ui/data/pixmaps/flags/er.png
+++ b/deluge/ui/data/pixmaps/flags/er.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/es.png b/deluge/ui/data/pixmaps/flags/es.png
index 9cc55dd04..afcffef78 100644
--- a/deluge/ui/data/pixmaps/flags/es.png
+++ b/deluge/ui/data/pixmaps/flags/es.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/et.png b/deluge/ui/data/pixmaps/flags/et.png
index f4356fa51..a2c5d3ba7 100644
--- a/deluge/ui/data/pixmaps/flags/et.png
+++ b/deluge/ui/data/pixmaps/flags/et.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/fi.png b/deluge/ui/data/pixmaps/flags/fi.png
index 73f8c91de..c89702679 100644
--- a/deluge/ui/data/pixmaps/flags/fi.png
+++ b/deluge/ui/data/pixmaps/flags/fi.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/fj.png b/deluge/ui/data/pixmaps/flags/fj.png
index 0544e7a95..805d3b2af 100644
--- a/deluge/ui/data/pixmaps/flags/fj.png
+++ b/deluge/ui/data/pixmaps/flags/fj.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/fo.png b/deluge/ui/data/pixmaps/flags/fo.png
index b922a4f30..63ef1bdd4 100644
--- a/deluge/ui/data/pixmaps/flags/fo.png
+++ b/deluge/ui/data/pixmaps/flags/fo.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/fr.png b/deluge/ui/data/pixmaps/flags/fr.png
index 59346a911..84dd91764 100644
--- a/deluge/ui/data/pixmaps/flags/fr.png
+++ b/deluge/ui/data/pixmaps/flags/fr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ga.png b/deluge/ui/data/pixmaps/flags/ga.png
index 63cb01397..e584067d2 100644
--- a/deluge/ui/data/pixmaps/flags/ga.png
+++ b/deluge/ui/data/pixmaps/flags/ga.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gb.png b/deluge/ui/data/pixmaps/flags/gb.png
index 95007c7be..4a351b7ab 100644
--- a/deluge/ui/data/pixmaps/flags/gb.png
+++ b/deluge/ui/data/pixmaps/flags/gb.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gd.png b/deluge/ui/data/pixmaps/flags/gd.png
index 1c7de15f6..56f2d2b23 100644
--- a/deluge/ui/data/pixmaps/flags/gd.png
+++ b/deluge/ui/data/pixmaps/flags/gd.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ge.png b/deluge/ui/data/pixmaps/flags/ge.png
index 44685b656..e2389a15a 100644
--- a/deluge/ui/data/pixmaps/flags/ge.png
+++ b/deluge/ui/data/pixmaps/flags/ge.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gf.png b/deluge/ui/data/pixmaps/flags/gf.png
index 59346a911..84dd91764 100644
--- a/deluge/ui/data/pixmaps/flags/gf.png
+++ b/deluge/ui/data/pixmaps/flags/gf.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gg.png b/deluge/ui/data/pixmaps/flags/gg.png
index f65fbe1d2..3057a73d6 100644
--- a/deluge/ui/data/pixmaps/flags/gg.png
+++ b/deluge/ui/data/pixmaps/flags/gg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gh.png b/deluge/ui/data/pixmaps/flags/gh.png
index bc5741c3f..39f91b1fd 100644
--- a/deluge/ui/data/pixmaps/flags/gh.png
+++ b/deluge/ui/data/pixmaps/flags/gh.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gi.png b/deluge/ui/data/pixmaps/flags/gi.png
index 8edbe3748..3e631b3b1 100644
--- a/deluge/ui/data/pixmaps/flags/gi.png
+++ b/deluge/ui/data/pixmaps/flags/gi.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gl.png b/deluge/ui/data/pixmaps/flags/gl.png
index d8c022fde..40b443434 100644
--- a/deluge/ui/data/pixmaps/flags/gl.png
+++ b/deluge/ui/data/pixmaps/flags/gl.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gn.png b/deluge/ui/data/pixmaps/flags/gn.png
index b73833359..879495159 100644
--- a/deluge/ui/data/pixmaps/flags/gn.png
+++ b/deluge/ui/data/pixmaps/flags/gn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gp.png b/deluge/ui/data/pixmaps/flags/gp.png
index 81a21d3d5..a8340a26f 100644
--- a/deluge/ui/data/pixmaps/flags/gp.png
+++ b/deluge/ui/data/pixmaps/flags/gp.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gq.png b/deluge/ui/data/pixmaps/flags/gq.png
index 5d22f18ad..f773395ca 100644
--- a/deluge/ui/data/pixmaps/flags/gq.png
+++ b/deluge/ui/data/pixmaps/flags/gq.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gr.png b/deluge/ui/data/pixmaps/flags/gr.png
index 9fcb34548..840b53462 100644
--- a/deluge/ui/data/pixmaps/flags/gr.png
+++ b/deluge/ui/data/pixmaps/flags/gr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gt.png b/deluge/ui/data/pixmaps/flags/gt.png
index a05b89c3e..d9b836e26 100644
--- a/deluge/ui/data/pixmaps/flags/gt.png
+++ b/deluge/ui/data/pixmaps/flags/gt.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gu.png b/deluge/ui/data/pixmaps/flags/gu.png
index 83beb89ac..4886da3d4 100644
--- a/deluge/ui/data/pixmaps/flags/gu.png
+++ b/deluge/ui/data/pixmaps/flags/gu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gw.png b/deluge/ui/data/pixmaps/flags/gw.png
index fe406b28a..db172bdf4 100644
--- a/deluge/ui/data/pixmaps/flags/gw.png
+++ b/deluge/ui/data/pixmaps/flags/gw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/gy.png b/deluge/ui/data/pixmaps/flags/gy.png
index fff34e303..6424d8e9c 100644
--- a/deluge/ui/data/pixmaps/flags/gy.png
+++ b/deluge/ui/data/pixmaps/flags/gy.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/hk.png b/deluge/ui/data/pixmaps/flags/hk.png
index 6a5625a00..e969f862b 100644
--- a/deluge/ui/data/pixmaps/flags/hk.png
+++ b/deluge/ui/data/pixmaps/flags/hk.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/hm.png b/deluge/ui/data/pixmaps/flags/hm.png
index 0de18f0b2..0335dbfc8 100644
--- a/deluge/ui/data/pixmaps/flags/hm.png
+++ b/deluge/ui/data/pixmaps/flags/hm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/hn.png b/deluge/ui/data/pixmaps/flags/hn.png
index 77a009ace..e3faca9f3 100644
--- a/deluge/ui/data/pixmaps/flags/hn.png
+++ b/deluge/ui/data/pixmaps/flags/hn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/hr.png b/deluge/ui/data/pixmaps/flags/hr.png
index f56bc2926..aa64bf889 100644
--- a/deluge/ui/data/pixmaps/flags/hr.png
+++ b/deluge/ui/data/pixmaps/flags/hr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ht.png b/deluge/ui/data/pixmaps/flags/ht.png
index 16d00e8c1..bbcfd7ed6 100644
--- a/deluge/ui/data/pixmaps/flags/ht.png
+++ b/deluge/ui/data/pixmaps/flags/ht.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/id.png b/deluge/ui/data/pixmaps/flags/id.png
index 201ad0fcb..b1cdf79b9 100644
--- a/deluge/ui/data/pixmaps/flags/id.png
+++ b/deluge/ui/data/pixmaps/flags/id.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/in.png b/deluge/ui/data/pixmaps/flags/in.png
index 0b515d76a..0f5801e7f 100644
--- a/deluge/ui/data/pixmaps/flags/in.png
+++ b/deluge/ui/data/pixmaps/flags/in.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/iq.png b/deluge/ui/data/pixmaps/flags/iq.png
index cb1ca164e..221fbc6df 100644
--- a/deluge/ui/data/pixmaps/flags/iq.png
+++ b/deluge/ui/data/pixmaps/flags/iq.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ir.png b/deluge/ui/data/pixmaps/flags/ir.png
index f6d1027bd..8992fa456 100644
--- a/deluge/ui/data/pixmaps/flags/ir.png
+++ b/deluge/ui/data/pixmaps/flags/ir.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/is.png b/deluge/ui/data/pixmaps/flags/is.png
index f7383cbce..3f5d5c42f 100644
--- a/deluge/ui/data/pixmaps/flags/is.png
+++ b/deluge/ui/data/pixmaps/flags/is.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/it.png b/deluge/ui/data/pixmaps/flags/it.png
index c3c3143c7..ad34d9f2c 100644
--- a/deluge/ui/data/pixmaps/flags/it.png
+++ b/deluge/ui/data/pixmaps/flags/it.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/jp.png b/deluge/ui/data/pixmaps/flags/jp.png
index e470d756d..def1d3c7d 100644
--- a/deluge/ui/data/pixmaps/flags/jp.png
+++ b/deluge/ui/data/pixmaps/flags/jp.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ke.png b/deluge/ui/data/pixmaps/flags/ke.png
index 212cc2e0e..2adad2d51 100644
--- a/deluge/ui/data/pixmaps/flags/ke.png
+++ b/deluge/ui/data/pixmaps/flags/ke.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/kg.png b/deluge/ui/data/pixmaps/flags/kg.png
index 848da86b8..8be5235df 100644
--- a/deluge/ui/data/pixmaps/flags/kg.png
+++ b/deluge/ui/data/pixmaps/flags/kg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/kh.png b/deluge/ui/data/pixmaps/flags/kh.png
index bf961c5d0..24da97236 100644
--- a/deluge/ui/data/pixmaps/flags/kh.png
+++ b/deluge/ui/data/pixmaps/flags/kh.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/km.png b/deluge/ui/data/pixmaps/flags/km.png
index 7ead97e0e..625437d25 100644
--- a/deluge/ui/data/pixmaps/flags/km.png
+++ b/deluge/ui/data/pixmaps/flags/km.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/kp.png b/deluge/ui/data/pixmaps/flags/kp.png
index b517d1c20..fb473ec22 100644
--- a/deluge/ui/data/pixmaps/flags/kp.png
+++ b/deluge/ui/data/pixmaps/flags/kp.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/kr.png b/deluge/ui/data/pixmaps/flags/kr.png
index ea3122e47..55bcf3872 100644
--- a/deluge/ui/data/pixmaps/flags/kr.png
+++ b/deluge/ui/data/pixmaps/flags/kr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/kw.png b/deluge/ui/data/pixmaps/flags/kw.png
index a3a7fcbcf..565cc0369 100644
--- a/deluge/ui/data/pixmaps/flags/kw.png
+++ b/deluge/ui/data/pixmaps/flags/kw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ky.png b/deluge/ui/data/pixmaps/flags/ky.png
index 63b55463f..b08b3bccb 100644
--- a/deluge/ui/data/pixmaps/flags/ky.png
+++ b/deluge/ui/data/pixmaps/flags/ky.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/kz.png b/deluge/ui/data/pixmaps/flags/kz.png
index 193cf3872..49fcc2611 100644
--- a/deluge/ui/data/pixmaps/flags/kz.png
+++ b/deluge/ui/data/pixmaps/flags/kz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/la.png b/deluge/ui/data/pixmaps/flags/la.png
index 85b6097a1..0098f1b80 100644
--- a/deluge/ui/data/pixmaps/flags/la.png
+++ b/deluge/ui/data/pixmaps/flags/la.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lb.png b/deluge/ui/data/pixmaps/flags/lb.png
index b8e9c2f15..c519388ab 100644
--- a/deluge/ui/data/pixmaps/flags/lb.png
+++ b/deluge/ui/data/pixmaps/flags/lb.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lc.png b/deluge/ui/data/pixmaps/flags/lc.png
index d8a8656b5..f42f007ee 100644
--- a/deluge/ui/data/pixmaps/flags/lc.png
+++ b/deluge/ui/data/pixmaps/flags/lc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/li.png b/deluge/ui/data/pixmaps/flags/li.png
index 6bb7b2b7b..02f75488b 100644
--- a/deluge/ui/data/pixmaps/flags/li.png
+++ b/deluge/ui/data/pixmaps/flags/li.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lk.png b/deluge/ui/data/pixmaps/flags/lk.png
index 4f16cd77a..22abcdb68 100644
--- a/deluge/ui/data/pixmaps/flags/lk.png
+++ b/deluge/ui/data/pixmaps/flags/lk.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lr.png b/deluge/ui/data/pixmaps/flags/lr.png
index d76e171e9..676fda789 100644
--- a/deluge/ui/data/pixmaps/flags/lr.png
+++ b/deluge/ui/data/pixmaps/flags/lr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ls.png b/deluge/ui/data/pixmaps/flags/ls.png
index 02cdd660f..3a2fcae5a 100644
--- a/deluge/ui/data/pixmaps/flags/ls.png
+++ b/deluge/ui/data/pixmaps/flags/ls.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lt.png b/deluge/ui/data/pixmaps/flags/lt.png
index e0714c541..05a445c00 100644
--- a/deluge/ui/data/pixmaps/flags/lt.png
+++ b/deluge/ui/data/pixmaps/flags/lt.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lu.png b/deluge/ui/data/pixmaps/flags/lu.png
index f750b0c24..c9d4d7253 100644
--- a/deluge/ui/data/pixmaps/flags/lu.png
+++ b/deluge/ui/data/pixmaps/flags/lu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/lv.png b/deluge/ui/data/pixmaps/flags/lv.png
index f35709550..72504d11d 100644
--- a/deluge/ui/data/pixmaps/flags/lv.png
+++ b/deluge/ui/data/pixmaps/flags/lv.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ly.png b/deluge/ui/data/pixmaps/flags/ly.png
index 9e35e389d..ebd3cf62c 100644
--- a/deluge/ui/data/pixmaps/flags/ly.png
+++ b/deluge/ui/data/pixmaps/flags/ly.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ma.png b/deluge/ui/data/pixmaps/flags/ma.png
index b8f8decfa..62d706570 100644
--- a/deluge/ui/data/pixmaps/flags/ma.png
+++ b/deluge/ui/data/pixmaps/flags/ma.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mc.png b/deluge/ui/data/pixmaps/flags/mc.png
index 67099ea18..3f2d6be4a 100644
--- a/deluge/ui/data/pixmaps/flags/mc.png
+++ b/deluge/ui/data/pixmaps/flags/mc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/md.png b/deluge/ui/data/pixmaps/flags/md.png
index 6ff6cf59e..c72632e1d 100644
--- a/deluge/ui/data/pixmaps/flags/md.png
+++ b/deluge/ui/data/pixmaps/flags/md.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/me.png b/deluge/ui/data/pixmaps/flags/me.png
index 36cbdd582..0760fb8e8 100644
--- a/deluge/ui/data/pixmaps/flags/me.png
+++ b/deluge/ui/data/pixmaps/flags/me.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mg.png b/deluge/ui/data/pixmaps/flags/mg.png
index d9313e295..7000ff015 100644
--- a/deluge/ui/data/pixmaps/flags/mg.png
+++ b/deluge/ui/data/pixmaps/flags/mg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mh.png b/deluge/ui/data/pixmaps/flags/mh.png
index 7618cc6fa..fe2caeff1 100644
--- a/deluge/ui/data/pixmaps/flags/mh.png
+++ b/deluge/ui/data/pixmaps/flags/mh.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mk.png b/deluge/ui/data/pixmaps/flags/mk.png
index 1c98d5154..8739fee7b 100644
--- a/deluge/ui/data/pixmaps/flags/mk.png
+++ b/deluge/ui/data/pixmaps/flags/mk.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ml.png b/deluge/ui/data/pixmaps/flags/ml.png
index d59cea847..f35c3a302 100644
--- a/deluge/ui/data/pixmaps/flags/ml.png
+++ b/deluge/ui/data/pixmaps/flags/ml.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mm.png b/deluge/ui/data/pixmaps/flags/mm.png
index 175fc5787..94bcb9362 100644
--- a/deluge/ui/data/pixmaps/flags/mm.png
+++ b/deluge/ui/data/pixmaps/flags/mm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mn.png b/deluge/ui/data/pixmaps/flags/mn.png
index 0ad97b350..8e20c1d57 100644
--- a/deluge/ui/data/pixmaps/flags/mn.png
+++ b/deluge/ui/data/pixmaps/flags/mn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mo.png b/deluge/ui/data/pixmaps/flags/mo.png
index f49e677e8..bbc14deef 100644
--- a/deluge/ui/data/pixmaps/flags/mo.png
+++ b/deluge/ui/data/pixmaps/flags/mo.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mp.png b/deluge/ui/data/pixmaps/flags/mp.png
index e91b75b63..ab6d5b8b7 100644
--- a/deluge/ui/data/pixmaps/flags/mp.png
+++ b/deluge/ui/data/pixmaps/flags/mp.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mq.png b/deluge/ui/data/pixmaps/flags/mq.png
index 5c5ae824b..9f52d1556 100644
--- a/deluge/ui/data/pixmaps/flags/mq.png
+++ b/deluge/ui/data/pixmaps/flags/mq.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mr.png b/deluge/ui/data/pixmaps/flags/mr.png
index 91d9a5a9d..e335ec22c 100644
--- a/deluge/ui/data/pixmaps/flags/mr.png
+++ b/deluge/ui/data/pixmaps/flags/mr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ms.png b/deluge/ui/data/pixmaps/flags/ms.png
index d45c16c39..addd14417 100644
--- a/deluge/ui/data/pixmaps/flags/ms.png
+++ b/deluge/ui/data/pixmaps/flags/ms.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mu.png b/deluge/ui/data/pixmaps/flags/mu.png
index ecdc1869a..38b6687a1 100644
--- a/deluge/ui/data/pixmaps/flags/mu.png
+++ b/deluge/ui/data/pixmaps/flags/mu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mv.png b/deluge/ui/data/pixmaps/flags/mv.png
index b17294451..1185f135c 100644
--- a/deluge/ui/data/pixmaps/flags/mv.png
+++ b/deluge/ui/data/pixmaps/flags/mv.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mw.png b/deluge/ui/data/pixmaps/flags/mw.png
index cb9b61f49..d68ffeefb 100644
--- a/deluge/ui/data/pixmaps/flags/mw.png
+++ b/deluge/ui/data/pixmaps/flags/mw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mx.png b/deluge/ui/data/pixmaps/flags/mx.png
index b8e70b868..996bcaef3 100644
--- a/deluge/ui/data/pixmaps/flags/mx.png
+++ b/deluge/ui/data/pixmaps/flags/mx.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/my.png b/deluge/ui/data/pixmaps/flags/my.png
index 879cf519c..2b13696be 100644
--- a/deluge/ui/data/pixmaps/flags/my.png
+++ b/deluge/ui/data/pixmaps/flags/my.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/mz.png b/deluge/ui/data/pixmaps/flags/mz.png
index 35545922b..484e99892 100644
--- a/deluge/ui/data/pixmaps/flags/mz.png
+++ b/deluge/ui/data/pixmaps/flags/mz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/na.png b/deluge/ui/data/pixmaps/flags/na.png
index 426cde9a5..806e2d624 100644
--- a/deluge/ui/data/pixmaps/flags/na.png
+++ b/deluge/ui/data/pixmaps/flags/na.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/nc.png b/deluge/ui/data/pixmaps/flags/nc.png
index 88c35ac13..de2314a82 100644
--- a/deluge/ui/data/pixmaps/flags/nc.png
+++ b/deluge/ui/data/pixmaps/flags/nc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ne.png b/deluge/ui/data/pixmaps/flags/ne.png
index eff1c3530..f76deb1cc 100644
--- a/deluge/ui/data/pixmaps/flags/ne.png
+++ b/deluge/ui/data/pixmaps/flags/ne.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ng.png b/deluge/ui/data/pixmaps/flags/ng.png
index e2f2b61d1..4e66ac3c0 100644
--- a/deluge/ui/data/pixmaps/flags/ng.png
+++ b/deluge/ui/data/pixmaps/flags/ng.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ni.png b/deluge/ui/data/pixmaps/flags/ni.png
index 9332b95c2..6d0cd865d 100644
--- a/deluge/ui/data/pixmaps/flags/ni.png
+++ b/deluge/ui/data/pixmaps/flags/ni.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/nl.png b/deluge/ui/data/pixmaps/flags/nl.png
index 267d1dceb..b0653cc8a 100644
--- a/deluge/ui/data/pixmaps/flags/nl.png
+++ b/deluge/ui/data/pixmaps/flags/nl.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/no.png b/deluge/ui/data/pixmaps/flags/no.png
index ae9060caf..faaa529d1 100644
--- a/deluge/ui/data/pixmaps/flags/no.png
+++ b/deluge/ui/data/pixmaps/flags/no.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/np.png b/deluge/ui/data/pixmaps/flags/np.png
index 6637ea544..9e161085b 100644
--- a/deluge/ui/data/pixmaps/flags/np.png
+++ b/deluge/ui/data/pixmaps/flags/np.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/nr.png b/deluge/ui/data/pixmaps/flags/nr.png
index 60f73461e..c28d3eec7 100644
--- a/deluge/ui/data/pixmaps/flags/nr.png
+++ b/deluge/ui/data/pixmaps/flags/nr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/nu.png b/deluge/ui/data/pixmaps/flags/nu.png
index 21e52a76e..9e82b5e12 100644
--- a/deluge/ui/data/pixmaps/flags/nu.png
+++ b/deluge/ui/data/pixmaps/flags/nu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/nz.png b/deluge/ui/data/pixmaps/flags/nz.png
index d5a7ebc7b..db738747b 100644
--- a/deluge/ui/data/pixmaps/flags/nz.png
+++ b/deluge/ui/data/pixmaps/flags/nz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/om.png b/deluge/ui/data/pixmaps/flags/om.png
index 6dc30d4fc..aa02242c9 100644
--- a/deluge/ui/data/pixmaps/flags/om.png
+++ b/deluge/ui/data/pixmaps/flags/om.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pa.png b/deluge/ui/data/pixmaps/flags/pa.png
index 895443640..810268af3 100644
--- a/deluge/ui/data/pixmaps/flags/pa.png
+++ b/deluge/ui/data/pixmaps/flags/pa.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pf.png b/deluge/ui/data/pixmaps/flags/pf.png
index ea4d04611..9a05d6674 100644
--- a/deluge/ui/data/pixmaps/flags/pf.png
+++ b/deluge/ui/data/pixmaps/flags/pf.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pg.png b/deluge/ui/data/pixmaps/flags/pg.png
index c97ce6a76..e4ec2d175 100644
--- a/deluge/ui/data/pixmaps/flags/pg.png
+++ b/deluge/ui/data/pixmaps/flags/pg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ph.png b/deluge/ui/data/pixmaps/flags/ph.png
index 67d289173..423209405 100644
--- a/deluge/ui/data/pixmaps/flags/ph.png
+++ b/deluge/ui/data/pixmaps/flags/ph.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pk.png b/deluge/ui/data/pixmaps/flags/pk.png
index a92d71c08..bb4822ffc 100644
--- a/deluge/ui/data/pixmaps/flags/pk.png
+++ b/deluge/ui/data/pixmaps/flags/pk.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pl.png b/deluge/ui/data/pixmaps/flags/pl.png
index 2c4f2ed1f..843568a6a 100644
--- a/deluge/ui/data/pixmaps/flags/pl.png
+++ b/deluge/ui/data/pixmaps/flags/pl.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pm.png b/deluge/ui/data/pixmaps/flags/pm.png
index ce429f0d2..f167375f0 100644
--- a/deluge/ui/data/pixmaps/flags/pm.png
+++ b/deluge/ui/data/pixmaps/flags/pm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pr.png b/deluge/ui/data/pixmaps/flags/pr.png
index 60d359917..0dc6d8d37 100644
--- a/deluge/ui/data/pixmaps/flags/pr.png
+++ b/deluge/ui/data/pixmaps/flags/pr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ps.png b/deluge/ui/data/pixmaps/flags/ps.png
index ceda7d7aa..3955b5cd7 100644
--- a/deluge/ui/data/pixmaps/flags/ps.png
+++ b/deluge/ui/data/pixmaps/flags/ps.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pt.png b/deluge/ui/data/pixmaps/flags/pt.png
index c489acda3..ee95fc82f 100644
--- a/deluge/ui/data/pixmaps/flags/pt.png
+++ b/deluge/ui/data/pixmaps/flags/pt.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/pw.png b/deluge/ui/data/pixmaps/flags/pw.png
index 668c93a03..43d9dcb85 100644
--- a/deluge/ui/data/pixmaps/flags/pw.png
+++ b/deluge/ui/data/pixmaps/flags/pw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/py.png b/deluge/ui/data/pixmaps/flags/py.png
index d5e943b0c..bc5d77db3 100644
--- a/deluge/ui/data/pixmaps/flags/py.png
+++ b/deluge/ui/data/pixmaps/flags/py.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/qa.png b/deluge/ui/data/pixmaps/flags/qa.png
index 08a3793e7..09709cc63 100644
--- a/deluge/ui/data/pixmaps/flags/qa.png
+++ b/deluge/ui/data/pixmaps/flags/qa.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/re.png b/deluge/ui/data/pixmaps/flags/re.png
index 59346a911..84dd91764 100644
--- a/deluge/ui/data/pixmaps/flags/re.png
+++ b/deluge/ui/data/pixmaps/flags/re.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ro.png b/deluge/ui/data/pixmaps/flags/ro.png
index f80a44a7f..594f245f5 100644
--- a/deluge/ui/data/pixmaps/flags/ro.png
+++ b/deluge/ui/data/pixmaps/flags/ro.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/rs.png b/deluge/ui/data/pixmaps/flags/rs.png
index 9a35bcc55..03176f73d 100644
--- a/deluge/ui/data/pixmaps/flags/rs.png
+++ b/deluge/ui/data/pixmaps/flags/rs.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/rw.png b/deluge/ui/data/pixmaps/flags/rw.png
index 0d3143791..c7e422a7b 100644
--- a/deluge/ui/data/pixmaps/flags/rw.png
+++ b/deluge/ui/data/pixmaps/flags/rw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sa.png b/deluge/ui/data/pixmaps/flags/sa.png
index 2fc170454..bd19de219 100644
--- a/deluge/ui/data/pixmaps/flags/sa.png
+++ b/deluge/ui/data/pixmaps/flags/sa.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sc.png b/deluge/ui/data/pixmaps/flags/sc.png
index c25e11369..e0bbfa72d 100644
--- a/deluge/ui/data/pixmaps/flags/sc.png
+++ b/deluge/ui/data/pixmaps/flags/sc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sd.png b/deluge/ui/data/pixmaps/flags/sd.png
index d22408c30..cbca3d2b5 100644
--- a/deluge/ui/data/pixmaps/flags/sd.png
+++ b/deluge/ui/data/pixmaps/flags/sd.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/se.png b/deluge/ui/data/pixmaps/flags/se.png
index 482101a66..e3d273a6f 100644
--- a/deluge/ui/data/pixmaps/flags/se.png
+++ b/deluge/ui/data/pixmaps/flags/se.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sg.png b/deluge/ui/data/pixmaps/flags/sg.png
index 055e04a1e..898748b76 100644
--- a/deluge/ui/data/pixmaps/flags/sg.png
+++ b/deluge/ui/data/pixmaps/flags/sg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sh.png b/deluge/ui/data/pixmaps/flags/sh.png
index 838dad78e..cb5282e2d 100644
--- a/deluge/ui/data/pixmaps/flags/sh.png
+++ b/deluge/ui/data/pixmaps/flags/sh.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/si.png b/deluge/ui/data/pixmaps/flags/si.png
index 206721e5f..d2f976b3a 100644
--- a/deluge/ui/data/pixmaps/flags/si.png
+++ b/deluge/ui/data/pixmaps/flags/si.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sj.png b/deluge/ui/data/pixmaps/flags/sj.png
index ae9060caf..faaa529d1 100644
--- a/deluge/ui/data/pixmaps/flags/sj.png
+++ b/deluge/ui/data/pixmaps/flags/sj.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sk.png b/deluge/ui/data/pixmaps/flags/sk.png
index acdd1893d..6f36d1429 100644
--- a/deluge/ui/data/pixmaps/flags/sk.png
+++ b/deluge/ui/data/pixmaps/flags/sk.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sl.png b/deluge/ui/data/pixmaps/flags/sl.png
index d50e5d4ef..3d70d224c 100644
--- a/deluge/ui/data/pixmaps/flags/sl.png
+++ b/deluge/ui/data/pixmaps/flags/sl.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sn.png b/deluge/ui/data/pixmaps/flags/sn.png
index 4029d3a7d..3be2d4c56 100644
--- a/deluge/ui/data/pixmaps/flags/sn.png
+++ b/deluge/ui/data/pixmaps/flags/sn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/so.png b/deluge/ui/data/pixmaps/flags/so.png
index b17fcf27f..c3169fa64 100644
--- a/deluge/ui/data/pixmaps/flags/so.png
+++ b/deluge/ui/data/pixmaps/flags/so.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sr.png b/deluge/ui/data/pixmaps/flags/sr.png
index e79029176..006f86f05 100644
--- a/deluge/ui/data/pixmaps/flags/sr.png
+++ b/deluge/ui/data/pixmaps/flags/sr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/st.png b/deluge/ui/data/pixmaps/flags/st.png
index 63c3e12ff..086db357a 100644
--- a/deluge/ui/data/pixmaps/flags/st.png
+++ b/deluge/ui/data/pixmaps/flags/st.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sv.png b/deluge/ui/data/pixmaps/flags/sv.png
index 309eb39d3..691fc669b 100644
--- a/deluge/ui/data/pixmaps/flags/sv.png
+++ b/deluge/ui/data/pixmaps/flags/sv.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sy.png b/deluge/ui/data/pixmaps/flags/sy.png
index 8a369aabf..5a28ce67b 100644
--- a/deluge/ui/data/pixmaps/flags/sy.png
+++ b/deluge/ui/data/pixmaps/flags/sy.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/sz.png b/deluge/ui/data/pixmaps/flags/sz.png
index 2da75092b..fba6beffc 100644
--- a/deluge/ui/data/pixmaps/flags/sz.png
+++ b/deluge/ui/data/pixmaps/flags/sz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tc.png b/deluge/ui/data/pixmaps/flags/tc.png
index c7b735443..dfecacd03 100644
--- a/deluge/ui/data/pixmaps/flags/tc.png
+++ b/deluge/ui/data/pixmaps/flags/tc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/td.png b/deluge/ui/data/pixmaps/flags/td.png
index c82f450f0..27b6cb805 100644
--- a/deluge/ui/data/pixmaps/flags/td.png
+++ b/deluge/ui/data/pixmaps/flags/td.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tf.png b/deluge/ui/data/pixmaps/flags/tf.png
index bf8077348..0f56fa04b 100644
--- a/deluge/ui/data/pixmaps/flags/tf.png
+++ b/deluge/ui/data/pixmaps/flags/tf.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/th.png b/deluge/ui/data/pixmaps/flags/th.png
index bf739941e..744865f57 100644
--- a/deluge/ui/data/pixmaps/flags/th.png
+++ b/deluge/ui/data/pixmaps/flags/th.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tj.png b/deluge/ui/data/pixmaps/flags/tj.png
index 3efd165cc..837dd2caa 100644
--- a/deluge/ui/data/pixmaps/flags/tj.png
+++ b/deluge/ui/data/pixmaps/flags/tj.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tl.png b/deluge/ui/data/pixmaps/flags/tl.png
index d7c41a9c7..843ec671d 100644
--- a/deluge/ui/data/pixmaps/flags/tl.png
+++ b/deluge/ui/data/pixmaps/flags/tl.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tm.png b/deluge/ui/data/pixmaps/flags/tm.png
index a2aed22dd..6356f3c5d 100644
--- a/deluge/ui/data/pixmaps/flags/tm.png
+++ b/deluge/ui/data/pixmaps/flags/tm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tn.png b/deluge/ui/data/pixmaps/flags/tn.png
index 61086fa77..fdf73c09d 100644
--- a/deluge/ui/data/pixmaps/flags/tn.png
+++ b/deluge/ui/data/pixmaps/flags/tn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/to.png b/deluge/ui/data/pixmaps/flags/to.png
index 8e49d30e3..7bd4389a2 100644
--- a/deluge/ui/data/pixmaps/flags/to.png
+++ b/deluge/ui/data/pixmaps/flags/to.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tp.png b/deluge/ui/data/pixmaps/flags/tp.png
index aa298c0f8..a8bb82fe4 100644
--- a/deluge/ui/data/pixmaps/flags/tp.png
+++ b/deluge/ui/data/pixmaps/flags/tp.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tr.png b/deluge/ui/data/pixmaps/flags/tr.png
index 09f82d09e..34b5a1c4b 100644
--- a/deluge/ui/data/pixmaps/flags/tr.png
+++ b/deluge/ui/data/pixmaps/flags/tr.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tt.png b/deluge/ui/data/pixmaps/flags/tt.png
index 18b09e1d3..7ff729b30 100644
--- a/deluge/ui/data/pixmaps/flags/tt.png
+++ b/deluge/ui/data/pixmaps/flags/tt.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/tz.png b/deluge/ui/data/pixmaps/flags/tz.png
index 3543543ce..38fbdf18f 100644
--- a/deluge/ui/data/pixmaps/flags/tz.png
+++ b/deluge/ui/data/pixmaps/flags/tz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ua.png b/deluge/ui/data/pixmaps/flags/ua.png
index e09f110dd..08e38f95e 100644
--- a/deluge/ui/data/pixmaps/flags/ua.png
+++ b/deluge/ui/data/pixmaps/flags/ua.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ug.png b/deluge/ui/data/pixmaps/flags/ug.png
index d1d9bc0f8..2fa3e44ce 100644
--- a/deluge/ui/data/pixmaps/flags/ug.png
+++ b/deluge/ui/data/pixmaps/flags/ug.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/um.png b/deluge/ui/data/pixmaps/flags/um.png
index 2694ab98e..a0617bd81 100644
--- a/deluge/ui/data/pixmaps/flags/um.png
+++ b/deluge/ui/data/pixmaps/flags/um.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/us.png b/deluge/ui/data/pixmaps/flags/us.png
index 68707f40c..5e0b8cf5c 100644
--- a/deluge/ui/data/pixmaps/flags/us.png
+++ b/deluge/ui/data/pixmaps/flags/us.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/uy.png b/deluge/ui/data/pixmaps/flags/uy.png
index 4c515670f..e69713a6e 100644
--- a/deluge/ui/data/pixmaps/flags/uy.png
+++ b/deluge/ui/data/pixmaps/flags/uy.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/uz.png b/deluge/ui/data/pixmaps/flags/uz.png
index e85d67d4b..5d3d7c16d 100644
--- a/deluge/ui/data/pixmaps/flags/uz.png
+++ b/deluge/ui/data/pixmaps/flags/uz.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/va.png b/deluge/ui/data/pixmaps/flags/va.png
index 582e2ba16..af8f6b23e 100644
--- a/deluge/ui/data/pixmaps/flags/va.png
+++ b/deluge/ui/data/pixmaps/flags/va.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/vc.png b/deluge/ui/data/pixmaps/flags/vc.png
index e5726112b..cf1d6e204 100644
--- a/deluge/ui/data/pixmaps/flags/vc.png
+++ b/deluge/ui/data/pixmaps/flags/vc.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ve.png b/deluge/ui/data/pixmaps/flags/ve.png
index 0899d54ae..e26b28a08 100644
--- a/deluge/ui/data/pixmaps/flags/ve.png
+++ b/deluge/ui/data/pixmaps/flags/ve.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/vg.png b/deluge/ui/data/pixmaps/flags/vg.png
index d9f04d6f7..3ece564ae 100644
--- a/deluge/ui/data/pixmaps/flags/vg.png
+++ b/deluge/ui/data/pixmaps/flags/vg.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/vi.png b/deluge/ui/data/pixmaps/flags/vi.png
index 2b887b30f..54d5c0e90 100644
--- a/deluge/ui/data/pixmaps/flags/vi.png
+++ b/deluge/ui/data/pixmaps/flags/vi.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/vn.png b/deluge/ui/data/pixmaps/flags/vn.png
index 335e9fc8e..8a6970349 100644
--- a/deluge/ui/data/pixmaps/flags/vn.png
+++ b/deluge/ui/data/pixmaps/flags/vn.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/vu.png b/deluge/ui/data/pixmaps/flags/vu.png
index f3fa6e62b..2794923a6 100644
--- a/deluge/ui/data/pixmaps/flags/vu.png
+++ b/deluge/ui/data/pixmaps/flags/vu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/wf.png b/deluge/ui/data/pixmaps/flags/wf.png
index 1d6460edb..574d08ac5 100644
--- a/deluge/ui/data/pixmaps/flags/wf.png
+++ b/deluge/ui/data/pixmaps/flags/wf.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ws.png b/deluge/ui/data/pixmaps/flags/ws.png
index 628f38793..6339a173e 100644
--- a/deluge/ui/data/pixmaps/flags/ws.png
+++ b/deluge/ui/data/pixmaps/flags/ws.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/ye.png b/deluge/ui/data/pixmaps/flags/ye.png
index b3f760b7b..c9187dd99 100644
--- a/deluge/ui/data/pixmaps/flags/ye.png
+++ b/deluge/ui/data/pixmaps/flags/ye.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/yt.png b/deluge/ui/data/pixmaps/flags/yt.png
index d0c5a2c88..41ef4a7f3 100644
--- a/deluge/ui/data/pixmaps/flags/yt.png
+++ b/deluge/ui/data/pixmaps/flags/yt.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/yu.png b/deluge/ui/data/pixmaps/flags/yu.png
index 4827aec2d..18fbde63f 100644
--- a/deluge/ui/data/pixmaps/flags/yu.png
+++ b/deluge/ui/data/pixmaps/flags/yu.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/za.png b/deluge/ui/data/pixmaps/flags/za.png
index 53ba7e1dd..04146b599 100644
--- a/deluge/ui/data/pixmaps/flags/za.png
+++ b/deluge/ui/data/pixmaps/flags/za.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/zm.png b/deluge/ui/data/pixmaps/flags/zm.png
index 2911ca7ce..363b4dfbc 100644
--- a/deluge/ui/data/pixmaps/flags/zm.png
+++ b/deluge/ui/data/pixmaps/flags/zm.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/flags/zw.png b/deluge/ui/data/pixmaps/flags/zw.png
index 9c065013a..030b1474b 100644
--- a/deluge/ui/data/pixmaps/flags/zw.png
+++ b/deluge/ui/data/pixmaps/flags/zw.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/inactive16.png b/deluge/ui/data/pixmaps/inactive16.png
index cae8b2c0a..e434dad9f 100644
--- a/deluge/ui/data/pixmaps/inactive16.png
+++ b/deluge/ui/data/pixmaps/inactive16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/magnet16.png b/deluge/ui/data/pixmaps/magnet16.png
index 61d6dabb7..6fc25ed69 100644
--- a/deluge/ui/data/pixmaps/magnet16.png
+++ b/deluge/ui/data/pixmaps/magnet16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/magnet_add16.png b/deluge/ui/data/pixmaps/magnet_add16.png
index 37c1c36b0..f6010f54a 100644
--- a/deluge/ui/data/pixmaps/magnet_add16.png
+++ b/deluge/ui/data/pixmaps/magnet_add16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/magnet_copy16.png b/deluge/ui/data/pixmaps/magnet_copy16.png
index a4be9d218..9b4ec6bbf 100644
--- a/deluge/ui/data/pixmaps/magnet_copy16.png
+++ b/deluge/ui/data/pixmaps/magnet_copy16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/queued16.png b/deluge/ui/data/pixmaps/queued16.png
index f9f74540f..74db4c5eb 100644
--- a/deluge/ui/data/pixmaps/queued16.png
+++ b/deluge/ui/data/pixmaps/queued16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/seeding16.png b/deluge/ui/data/pixmaps/seeding16.png
index fce70a8d9..2d9dc58f7 100644
--- a/deluge/ui/data/pixmaps/seeding16.png
+++ b/deluge/ui/data/pixmaps/seeding16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/tracker_all16.png b/deluge/ui/data/pixmaps/tracker_all16.png
index 36756bbcc..10a8f9832 100644
--- a/deluge/ui/data/pixmaps/tracker_all16.png
+++ b/deluge/ui/data/pixmaps/tracker_all16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/tracker_warning16.png b/deluge/ui/data/pixmaps/tracker_warning16.png
index 219432c44..7c29972a3 100644
--- a/deluge/ui/data/pixmaps/tracker_warning16.png
+++ b/deluge/ui/data/pixmaps/tracker_warning16.png
Binary files differ
diff --git a/deluge/ui/data/pixmaps/traffic16.png b/deluge/ui/data/pixmaps/traffic16.png
index ecd87204d..b4ce5ea14 100644
--- a/deluge/ui/data/pixmaps/traffic16.png
+++ b/deluge/ui/data/pixmaps/traffic16.png
Binary files differ
diff --git a/deluge/ui/data/share/applications/deluge.desktop.in b/deluge/ui/data/share/applications/deluge.desktop.in
index c952d424a..4335b6da4 100644
--- a/deluge/ui/data/share/applications/deluge.desktop.in
+++ b/deluge/ui/data/share/applications/deluge.desktop.in
@@ -4,6 +4,7 @@ _Name=Deluge
_GenericName=BitTorrent Client
_X-GNOME-FullName=Deluge BitTorrent Client
_Comment=Download and share files over BitTorrent
+_Keywords=bittorrent;torrent;magnet;download;p2p;torrents;downloading;uploading;share;sharing;
TryExec=deluge-gtk
Exec=deluge-gtk %U
Icon=deluge
diff --git a/deluge/ui/data/share/appdata/deluge.appdata.xml.in b/deluge/ui/data/share/metainfo/deluge.metainfo.xml.in
index dcbf0630c..dcbf0630c 100644
--- a/deluge/ui/data/share/appdata/deluge.appdata.xml.in
+++ b/deluge/ui/data/share/metainfo/deluge.metainfo.xml.in
diff --git a/deluge/ui/gtk3/__init__.py b/deluge/ui/gtk3/__init__.py
index 8e8b19613..d1b4ec53f 100644
--- a/deluge/ui/gtk3/__init__.py
+++ b/deluge/ui/gtk3/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from os import environ
@@ -21,11 +18,10 @@ environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
# Keep this class in __init__.py to avoid the console having to import everything in gtkui.py
class Gtk(UI):
-
cmd_description = """GTK-based graphical user interface"""
def __init__(self, *args, **kwargs):
- super(Gtk, self).__init__(
+ super().__init__(
'gtk', *args, description='Starts the Deluge GTK+ interface', **kwargs
)
@@ -42,7 +38,7 @@ class Gtk(UI):
)
def start(self):
- super(Gtk, self).start()
+ super().start()
import deluge.common
from .gtkui import GtkUI
diff --git a/deluge/ui/gtk3/aboutdialog.py b/deluge/ui/gtk3/aboutdialog.py
index 9974a13de..fe3452b82 100644
--- a/deluge/ui/gtk3/aboutdialog.py
+++ b/deluge/ui/gtk3/aboutdialog.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Marcos Mobley ('markybob') <markybob@gmail.com>
#
@@ -7,7 +6,7 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
+from datetime import date
from gi.repository import Gtk
@@ -18,7 +17,7 @@ from deluge.ui.client import client
from .common import get_deluge_icon, get_pixbuf
-class AboutDialog(object):
+class AboutDialog:
def __init__(self):
self.about = Gtk.AboutDialog()
self.about.set_transient_for(component.get('MainWindow').window)
@@ -38,7 +37,7 @@ class AboutDialog(object):
self.about.set_copyright(
_('Copyright %(year_start)s-%(year_end)s Deluge Team')
- % {'year_start': 2007, 'year_end': 2019}
+ % {'year_start': 2007, 'year_end': date.today().year}
)
self.about.set_comments(
_('A peer-to-peer file sharing program\nutilizing the BitTorrent protocol.')
diff --git a/deluge/ui/gtk3/addtorrentdialog.py b/deluge/ui/gtk3/addtorrentdialog.py
index 81b8dbaf3..aa71cc434 100644
--- a/deluge/ui/gtk3/addtorrentdialog.py
+++ b/deluge/ui/gtk3/addtorrentdialog.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,11 +6,9 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
import os
-from base64 import b64encode
+from base64 import b64decode, b64encode
from xml.sax.saxutils import escape as xml_escape
from xml.sax.saxutils import unescape as xml_unescape
@@ -19,6 +16,7 @@ from gi.repository import Gtk
from gi.repository.GObject import TYPE_INT64, TYPE_UINT64
import deluge.component as component
+from deluge.bencode import bdecode
from deluge.common import (
create_magnet_uri,
decode_bytes,
@@ -271,6 +269,7 @@ class AddTorrentDialog(component.Component):
return
if metadata:
+ metadata = bdecode(b64decode(metadata))
info = TorrentInfo.from_metadata(metadata, [[t] for t in trackers])
self.files[info_hash] = info.files
self.infos[info_hash] = info.filedata
@@ -380,7 +379,7 @@ class AddTorrentDialog(component.Component):
self.listview_files.expand_row(root, False)
def prepare_file(self, _file, file_name, file_num, download, files_storage):
- first_slash_index = file_name.find(os.path.sep)
+ first_slash_index = file_name.find('/')
if first_slash_index == -1:
files_storage[file_name] = (file_num, _file, download)
else:
@@ -398,7 +397,7 @@ class AddTorrentDialog(component.Component):
def add_files(self, parent_iter, split_files):
ret = 0
for key, value in split_files.items():
- if key.endswith(os.path.sep):
+ if key.endswith('/'):
chunk_iter = self.files_treestore.append(
parent_iter, [True, key, 0, -1, False, 'folder-symbolic']
)
@@ -585,7 +584,7 @@ class AddTorrentDialog(component.Component):
self.build_priorities(
self.files_treestore.iter_children(_iter), priorities
)
- elif not self.files_treestore.get_value(_iter, 1).endswith(os.path.sep):
+ elif not self.files_treestore.get_value(_iter, 1).endswith('/'):
priorities[
self.files_treestore.get_value(_iter, 3)
] = self.files_treestore.get_value(_iter, 0)
@@ -775,7 +774,7 @@ class AddTorrentDialog(component.Component):
else:
ErrorDialog(
_('Invalid URL'),
- '%s %s' % (url, _('is not a valid URL.')),
+ '{} {}'.format(url, _('is not a valid URL.')),
self.dialog,
).run()
@@ -817,7 +816,7 @@ class AddTorrentDialog(component.Component):
dialog.destroy()
ErrorDialog(
_('Download Failed'),
- '%s %s' % (_('Failed to download:'), url),
+ '{} {}'.format(_('Failed to download:'), url),
details=result.getErrorMessage(),
parent=self.dialog,
).run()
@@ -986,7 +985,10 @@ class AddTorrentDialog(component.Component):
def _on_filename_edited(self, renderer, path, new_text):
index = self.files_treestore[path][3]
- new_text = new_text.strip(os.path.sep).strip()
+ # Ensure agnostic path separator
+ new_text = new_text.replace('\\', '/')
+
+ new_text = new_text.strip('/').strip()
# Return if the text hasn't changed
if new_text == self.files_treestore[path][1]:
@@ -1013,10 +1015,10 @@ class AddTorrentDialog(component.Component):
for row in self.files_treestore[parent].iterchildren():
if new_text == row[1]:
return
- if os.path.sep in new_text:
+ if '/' in new_text:
# There are folders in this path, so we need to create them
# and then move the file iter to top
- split_text = new_text.split(os.path.sep)
+ split_text = new_text.split('/')
for s in split_text[:-1]:
parent = self.files_treestore.append(
parent, [True, s, 0, -1, False, 'folder-symbolic']
@@ -1069,19 +1071,19 @@ class AddTorrentDialog(component.Component):
# we can construct the new proper paths
# We need to check if this folder has been split
- if os.path.sep in new_text:
+ if '/' in new_text:
# It's been split, so we need to add new folders and then re-parent
# itr.
parent = self.files_treestore.iter_parent(itr)
- split_text = new_text.split(os.path.sep)
+ split_text = new_text.split('/')
for s in split_text[:-1]:
# We don't iterate over the last item because we'll just use
# the existing itr and change the text
parent = self.files_treestore.append(
- parent, [True, s + os.path.sep, 0, -1, False, 'folder-symbolic']
+ parent, [True, s + '/', 0, -1, False, 'folder-symbolic']
)
- self.files_treestore[itr][1] = split_text[-1] + os.path.sep
+ self.files_treestore[itr][1] = split_text[-1] + '/'
# Now re-parent itr to parent
reparent_iter(self.files_treestore, itr, parent)
@@ -1094,7 +1096,7 @@ class AddTorrentDialog(component.Component):
else:
# This was a simple folder rename without any splits, so just
# change the path for itr
- self.files_treestore[itr][1] = new_text + os.path.sep
+ self.files_treestore[itr][1] = new_text + '/'
# Walk through the tree from 'itr' and add all the new file paths
# to the 'mapped_files' option
diff --git a/deluge/ui/gtk3/common.py b/deluge/ui/gtk3/common.py
index e7b46c8d5..42a14b407 100644
--- a/deluge/ui/gtk3/common.py
+++ b/deluge/ui/gtk3/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Marcos Mobley ('markybob') <markybob@gmail.com>
#
@@ -7,15 +6,13 @@
# See LICENSE for more details.
#
"""Common functions for various parts of gtkui to use."""
-from __future__ import unicode_literals
-
import contextlib
import logging
import os
+import pickle
import shutil
import sys
-import six.moves.cPickle as pickle # noqa: N813
from gi.repository.Gdk import SELECTION_CLIPBOARD, SELECTION_PRIMARY, Display
from gi.repository.GdkPixbuf import Colorspace, Pixbuf
from gi.repository.GLib import GError
@@ -29,7 +26,7 @@ from gi.repository.Gtk import (
SortType,
)
-from deluge.common import PY2, get_pixmap, is_ip, osx_check, windows_check
+from deluge.common import get_pixmap, is_ip, osx_check, windows_check
log = logging.getLogger(__name__)
@@ -62,12 +59,36 @@ def create_blank_pixbuf(size=16):
return pix
-def get_pixbuf(filename):
+def get_pixbuf(filename: str, size: int = 0) -> Pixbuf:
+ """Creates a new pixbuf by loading an image from file
+
+ Args:
+ filename: An image file to load
+ size: Specify a size constraint (equal aspect ratio)
+
+ Returns:
+ A newly created pixbuf
+
+ """
+ # Skip ico and gif that cause Pixbuf crash on Windows
+ # https://dev.deluge-torrent.org/ticket/3501
+ if windows_check() and filename.endswith(('.ico', '.gif')):
+ return create_blank_pixbuf(size)
+
+ if not os.path.isabs(filename):
+ filename = get_pixmap(filename)
+
+ pixbuf = None
try:
- return Pixbuf.new_from_file(get_pixmap(filename))
+ if size:
+ pixbuf = Pixbuf.new_from_file_at_size(filename, size, size)
+ else:
+ pixbuf = Pixbuf.new_from_file(filename)
except GError as ex:
+ # Failed to load the pixbuf (Bad image file), so return a blank pixbuf.
log.warning(ex)
- return create_blank_pixbuf()
+
+ return pixbuf or create_blank_pixbuf(size or 16)
# Status icons.. Create them from file only once to avoid constantly re-creating them.
@@ -79,17 +100,6 @@ icon_queued = get_pixbuf('queued16.png')
icon_checking = get_pixbuf('checking16.png')
-def get_pixbuf_at_size(filename, size):
- if not os.path.isabs(filename):
- filename = get_pixmap(filename)
- try:
- return Pixbuf.new_from_file_at_size(filename, size, size)
- except GError as ex:
- # Failed to load the pixbuf (Bad image file), so return a blank pixbuf.
- log.warning(ex)
- return create_blank_pixbuf(size)
-
-
def get_logo(size):
"""A Deluge logo.
@@ -102,7 +112,7 @@ def get_logo(size):
filename = 'deluge.svg'
if windows_check():
filename = 'deluge.png'
- return get_pixbuf_at_size(filename, size)
+ return get_pixbuf(filename, size)
def build_menu_radio_list(
@@ -232,14 +242,11 @@ def associate_magnet_links(overwrite=False):
"""
if windows_check():
- try:
- import winreg
- except ImportError:
- import _winreg as winreg # For Python 2.
+ import winreg
try:
hkey = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'Magnet')
- except WindowsError:
+ except OSError:
overwrite = True
else:
winreg.CloseKey(hkey)
@@ -248,7 +255,7 @@ def associate_magnet_links(overwrite=False):
deluge_exe = os.path.join(os.path.dirname(sys.executable), 'deluge.exe')
try:
magnet_key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, 'Magnet')
- except WindowsError:
+ except OSError:
# Could not create for all users, falling back to current user
magnet_key = winreg.CreateKey(
winreg.HKEY_CURRENT_USER, 'Software\\Classes\\Magnet'
@@ -257,14 +264,12 @@ def associate_magnet_links(overwrite=False):
winreg.SetValue(magnet_key, '', winreg.REG_SZ, 'URL:Magnet Protocol')
winreg.SetValueEx(magnet_key, 'URL Protocol', 0, winreg.REG_SZ, '')
winreg.SetValueEx(magnet_key, 'BrowserFlags', 0, winreg.REG_DWORD, 0x8)
- winreg.SetValue(
- magnet_key, 'DefaultIcon', winreg.REG_SZ, '{},0'.format(deluge_exe)
- )
+ winreg.SetValue(magnet_key, 'DefaultIcon', winreg.REG_SZ, f'{deluge_exe},0')
winreg.SetValue(
magnet_key,
r'shell\open\command',
winreg.REG_SZ,
- '"{}" "%1"'.format(deluge_exe),
+ f'"{deluge_exe}" "%1"',
)
winreg.CloseKey(magnet_key)
@@ -320,7 +325,7 @@ def save_pickled_state_file(filename, state):
if os.path.isfile(filepath):
log.debug('Creating backup of %s at: %s', filename, filepath_bak)
shutil.copy2(filepath, filepath_bak)
- except IOError as ex:
+ except OSError as ex:
log.error('Unable to backup %s to %s: %s', filepath, filepath_bak, ex)
else:
log.info('Saving the %s at: %s', filename, filepath)
@@ -331,7 +336,7 @@ def save_pickled_state_file(filename, state):
_file.flush()
os.fsync(_file.fileno())
shutil.move(filepath_tmp, filepath)
- except (IOError, EOFError, pickle.PicklingError) as ex:
+ except (OSError, EOFError, pickle.PicklingError) as ex:
log.error('Unable to save %s: %s', filename, ex)
if os.path.isfile(filepath_bak):
log.info('Restoring backup of %s from: %s', filename, filepath_bak)
@@ -356,11 +361,8 @@ def load_pickled_state_file(filename):
log.info('Opening %s for load: %s', filename, _filepath)
try:
with open(_filepath, 'rb') as _file:
- if PY2:
- state = pickle.load(_file)
- else:
- state = pickle.load(_file, encoding='utf8')
- except (IOError, pickle.UnpicklingError) as ex:
+ state = pickle.load(_file, encoding='utf8')
+ except (OSError, pickle.UnpicklingError) as ex:
log.warning('Unable to load %s: %s', _filepath, ex)
else:
log.info('Successfully loaded %s: %s', filename, _filepath)
diff --git a/deluge/ui/gtk3/connectionmanager.py b/deluge/ui/gtk3/connectionmanager.py
index d5883c4b3..b53dd8e04 100644
--- a/deluge/ui/gtk3/connectionmanager.py
+++ b/deluge/ui/gtk3/connectionmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,11 +6,10 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
-from socket import gaierror, gethostbyname
+from socket import gaierror, getaddrinfo
+from urllib.parse import urlparse
from gi.repository import Gtk
from twisted.internet import defer, reactor
@@ -26,12 +24,6 @@ from deluge.ui.hostlist import DEFAULT_PORT, LOCALHOST, HostList
from .common import get_clipboard_text
from .dialogs import AuthenticationDialog, ErrorDialog
-try:
- from urllib.parse import urlparse
-except ImportError:
- # PY2 fallback
- from urlparse import urlparse # pylint: disable=ungrouped-imports
-
log = logging.getLogger(__name__)
HOSTLIST_COL_ID = 0
@@ -230,7 +222,7 @@ class ConnectionManager(component.Component):
__, host, port, __, __, status, __, __ = model[row]
try:
- gethostbyname(host)
+ getaddrinfo(host, None)
except gaierror as ex:
log.error(
'Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1]
diff --git a/deluge/ui/gtk3/createtorrentdialog.py b/deluge/ui/gtk3/createtorrentdialog.py
index 1e5e73cb6..e9f16906c 100644
--- a/deluge/ui/gtk3/createtorrentdialog.py
+++ b/deluge/ui/gtk3/createtorrentdialog.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
import os.path
from base64 import b64encode
@@ -31,7 +28,7 @@ from .torrentview_data_funcs import cell_data_size
log = logging.getLogger(__name__)
-class CreateTorrentDialog(object):
+class CreateTorrentDialog:
def __init__(self):
pass
diff --git a/deluge/ui/gtk3/details_tab.py b/deluge/ui/gtk3/details_tab.py
index 2431e0836..04a5eabfe 100644
--- a/deluge/ui/gtk3/details_tab.py
+++ b/deluge/ui/gtk3/details_tab.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from xml.sax.saxutils import escape as xml_escape
@@ -23,7 +20,7 @@ log = logging.getLogger(__name__)
class DetailsTab(Tab):
def __init__(self):
- super(DetailsTab, self).__init__('Details', 'details_tab', 'details_tab_label')
+ super().__init__('Details', 'details_tab', 'details_tab_label')
self.add_tab_widget('summary_name', None, ('name',))
self.add_tab_widget('summary_total_size', fsize, ('total_size',))
@@ -65,7 +62,7 @@ class DetailsTab(Tab):
txt = xml_escape(self.widget_status_as_fstr(widget, status))
if decode_bytes(widget.obj.get_text()) != txt:
if 'comment' in widget.status_keys and is_url(txt):
- widget.obj.set_markup('<a href="%s">%s</a>' % (txt, txt))
+ widget.obj.set_markup(f'<a href="{txt}">{txt}</a>')
else:
widget.obj.set_markup(txt)
diff --git a/deluge/ui/gtk3/dialogs.py b/deluge/ui/gtk3/dialogs.py
index 4a2a60a7e..db337d3d5 100644
--- a/deluge/ui/gtk3/dialogs.py
+++ b/deluge/ui/gtk3/dialogs.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -9,7 +8,7 @@
# pylint: disable=super-on-old-class
-from __future__ import unicode_literals
+from collections import namedtuple
from gi.repository import Gtk
from twisted.internet import defer
@@ -17,7 +16,9 @@ from twisted.internet import defer
import deluge.component as component
from deluge.common import windows_check
-from .common import get_deluge_icon, get_pixbuf_at_size
+from .common import get_deluge_icon, get_pixbuf
+
+Account = namedtuple('Account', 'username password authlevel')
class BaseDialog(Gtk.Dialog):
@@ -34,7 +35,7 @@ class BaseDialog(Gtk.Dialog):
:param parent: gtkWindow, the parent window, if None it will default to the
MainWindow
"""
- super(BaseDialog, self).__init__(
+ super().__init__(
title=header,
parent=parent if parent else component.get('MainWindow').window,
flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
@@ -55,7 +56,7 @@ class BaseDialog(Gtk.Dialog):
# Hack for Windows since it doesn't support svg
if icon.endswith('.svg') and windows_check():
icon = icon.rpartition('.svg')[0] + '16.png'
- image.set_from_pixbuf(get_pixbuf_at_size(icon, 24))
+ image.set_from_pixbuf(get_pixbuf(icon, 24))
else:
image.set_from_icon_name(icon, Gtk.IconSize.LARGE_TOOLBAR)
image.set_alignment(0.5, 0.0)
@@ -103,7 +104,7 @@ class YesNoDialog(BaseDialog):
:param text: see `:class:BaseDialog`
:param parent: see `:class:BaseDialog`
"""
- super(YesNoDialog, self).__init__(
+ super().__init__(
header,
text,
'dialog-question',
@@ -127,7 +128,7 @@ class InformationDialog(BaseDialog):
:param text: see `:class:BaseDialog`
:param parent: see `:class:BaseDialog`
"""
- super(InformationDialog, self).__init__(
+ super().__init__(
header,
text,
'dialog-information',
@@ -154,7 +155,7 @@ class ErrorDialog(BaseDialog):
:param traceback: show the traceback information in the details area
:type traceback: bool
"""
- super(ErrorDialog, self).__init__(
+ super().__init__(
header, text, 'dialog-error', (_('_Close'), Gtk.ResponseType.CLOSE), parent
)
@@ -198,7 +199,7 @@ class AuthenticationDialog(BaseDialog):
:param err_msg: the error message we got back from the server
:type err_msg: string
"""
- super(AuthenticationDialog, self).__init__(
+ super().__init__(
_('Authenticate'),
err_msg,
'dialog-password',
@@ -255,7 +256,7 @@ class AccountDialog(BaseDialog):
parent=None,
):
if username:
- super(AccountDialog, self).__init__(
+ super().__init__(
_('Edit Account'),
_('Edit existing account'),
'dialog-information',
@@ -268,7 +269,7 @@ class AccountDialog(BaseDialog):
parent,
)
else:
- super(AccountDialog, self).__init__(
+ super().__init__(
_('New Account'),
_('Create a new account'),
'dialog-information',
@@ -276,21 +277,21 @@ class AccountDialog(BaseDialog):
parent,
)
- self.levels_mapping = levels_mapping
+ self.account = None
table = Gtk.Table(2, 3, False)
- self.username_label = Gtk.Label()
- self.username_label.set_markup('<b>' + _('Username:') + '</b>')
- self.username_label.set_alignment(1.0, 0.5)
- self.username_label.set_padding(5, 5)
+ username_label = Gtk.Label()
+ username_label.set_markup('<b>' + _('Username:') + '</b>')
+ username_label.set_alignment(1.0, 0.5)
+ username_label.set_padding(5, 5)
self.username_entry = Gtk.Entry()
- table.attach(self.username_label, 0, 1, 0, 1)
+ table.attach(username_label, 0, 1, 0, 1)
table.attach(self.username_entry, 1, 2, 0, 1)
- self.authlevel_label = Gtk.Label()
- self.authlevel_label.set_markup('<b>' + _('Authentication Level:') + '</b>')
- self.authlevel_label.set_alignment(1.0, 0.5)
- self.authlevel_label.set_padding(5, 5)
+ authlevel_label = Gtk.Label()
+ authlevel_label.set_markup('<b>' + _('Authentication Level:') + '</b>')
+ authlevel_label.set_alignment(1.0, 0.5)
+ authlevel_label.set_padding(5, 5)
# combo_box_new_text is deprecated but no other pygtk alternative.
self.authlevel_combo = Gtk.ComboBoxText()
@@ -305,16 +306,16 @@ class AccountDialog(BaseDialog):
if active_idx is not None:
self.authlevel_combo.set_active(active_idx)
- table.attach(self.authlevel_label, 0, 1, 1, 2)
+ table.attach(authlevel_label, 0, 1, 1, 2)
table.attach(self.authlevel_combo, 1, 2, 1, 2)
- self.password_label = Gtk.Label()
- self.password_label.set_markup('<b>' + _('Password:') + '</b>')
- self.password_label.set_alignment(1.0, 0.5)
- self.password_label.set_padding(5, 5)
+ password_label = Gtk.Label()
+ password_label.set_markup('<b>' + _('Password:') + '</b>')
+ password_label.set_alignment(1.0, 0.5)
+ password_label.set_padding(5, 5)
self.password_entry = Gtk.Entry()
self.password_entry.set_visibility(False)
- table.attach(self.password_label, 0, 1, 2, 3)
+ table.attach(password_label, 0, 1, 2, 3)
table.attach(self.password_entry, 1, 2, 2, 3)
self.vbox.pack_start(table, False, False, padding=5)
@@ -327,18 +328,17 @@ class AccountDialog(BaseDialog):
if password:
self.password_entry.set_text(username)
- self.show_all()
-
- def get_username(self):
- return self.username_entry.get_text()
-
- def get_password(self):
- return self.password_entry.get_text()
+ self.vbox.show_all()
- def get_authlevel(self):
- combobox = self.authlevel_combo
- level = combobox.get_model()[combobox.get_active()][0]
- return level
+ def _on_response(self, widget, response):
+ if response == Gtk.ResponseType.OK:
+ self.account = Account(
+ self.username_entry.get_text(),
+ self.password_entry.get_text(),
+ self.authlevel_combo.get_active_text(),
+ )
+ self.destroy()
+ self.deferred.callback(response)
class OtherDialog(BaseDialog):
@@ -359,7 +359,7 @@ class OtherDialog(BaseDialog):
if not icon:
icon = 'dialog-information'
- super(OtherDialog, self).__init__(
+ super().__init__(
header,
text,
icon,
@@ -421,7 +421,7 @@ class PasswordDialog(BaseDialog):
:param password_msg: the error message we got back from the server
:type password_msg: string
"""
- super(PasswordDialog, self).__init__(
+ super().__init__(
header=_('Password Protected'),
text=password_msg,
icon='dialog-password',
@@ -455,3 +455,44 @@ class PasswordDialog(BaseDialog):
def on_password_activate(self, widget):
self.response(Gtk.ResponseType.OK)
+
+
+class CopyMagnetDialog(BaseDialog):
+ """
+ Displays a dialog with a magnet URI
+ """
+
+ def __init__(self, torrent_magnet='', parent=None):
+ super().__init__(
+ header=_('Copy Magnet URI'),
+ text='',
+ icon='magnet_copy.svg',
+ buttons=(_('_Close'), Gtk.ResponseType.CLOSE),
+ parent=parent,
+ )
+ self.copied = False
+
+ table = Gtk.Table(1, 2, False)
+ self.magnet_entry = Gtk.Entry()
+ self.magnet_entry.set_text(torrent_magnet)
+ self.magnet_entry.set_editable(False)
+ self.magnet_entry.connect('copy-clipboard', self.on_copy_emitted)
+ table.attach(self.magnet_entry, 0, 1, 0, 1)
+
+ copy_button = Gtk.Button.new_with_label(_('Copy'))
+ copy_button.connect('clicked', self.on_copy_clicked)
+ table.attach(copy_button, 1, 2, 0, 1)
+
+ self.vbox.pack_start(table, False, False, padding=5)
+ self.set_focus(self.magnet_entry)
+
+ self.show_all()
+
+ def on_copy_clicked(self, widget):
+ self.magnet_entry.select_region(0, -1)
+ self.magnet_entry.copy_clipboard()
+ self.magnet_entry.set_position(0)
+ self.copied = True
+
+ def on_copy_emitted(self, widget):
+ self.copied = True
diff --git a/deluge/ui/gtk3/edittrackersdialog.py b/deluge/ui/gtk3/edittrackersdialog.py
index 1dfdd2a68..861e3924b 100644
--- a/deluge/ui/gtk3/edittrackersdialog.py
+++ b/deluge/ui/gtk3/edittrackersdialog.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,12 +6,10 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os.path
-from gi.repository import Gtk
+from gi.repository import Gdk, Gtk
from twisted.internet import defer
import deluge.component as component
@@ -77,7 +74,7 @@ def trackers_tiers_from_text(text_str=''):
return trackers
-class EditTrackersDialog(object):
+class EditTrackersDialog:
def __init__(self, torrent_id, parent=None):
self.torrent_id = torrent_id
self.builder = Gtk.Builder()
@@ -132,6 +129,12 @@ class EditTrackersDialog(object):
self.dialog.connect('delete-event', self._on_delete_event)
self.dialog.connect('response', self._on_response)
+ self.treeview.connect('button_press_event', self.on_button_press_event)
+
+ self.add_tracker_dialog.connect('key-press-event', self.on_key_add_press_event)
+ self.add_tracker_dialog.connect('delete-event', self.on_delete_event_add)
+ self.edit_tracker_entry.connect('key-press-event', self.on_key_edit_press_event)
+ self.edit_tracker_entry.connect('delete-event', self.on_delete_event_edit)
def run(self):
# Make sure we have a torrent_id.. if not just return
@@ -191,7 +194,7 @@ class EditTrackersDialog(object):
self.old_trackers = list(status['trackers'])
for tracker in self.old_trackers:
self.add_tracker(tracker['tier'], tracker['url'])
- self.treeview.set_cursor((0))
+ self.treeview.set_cursor(0)
self.dialog.show()
def add_tracker(self, tier, url):
@@ -207,6 +210,7 @@ class EditTrackersDialog(object):
# Show the add tracker dialog
self.add_tracker_dialog.show()
self.builder.get_object('textview_trackers').grab_focus()
+ self.dialog.set_sensitive(False)
def on_button_remove_clicked(self, widget):
log.debug('on_button_remove_clicked')
@@ -215,8 +219,18 @@ class EditTrackersDialog(object):
self.liststore.remove(selected)
def on_button_edit_clicked(self, widget):
- """edits an existing tracker"""
+ """edits an existing tracker on edit button click"""
log.debug('on_button_edit_clicked')
+ self._edit_tracker()
+
+ def on_button_press_event(self, widget, event):
+ """edits an existing tracker on double click on tracker name"""
+ if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
+ log.debug('button_press_event double click')
+ self._edit_tracker()
+
+ def _edit_tracker(self):
+ """edits an existing tracker"""
selected = self.get_selected()
if selected:
tracker = self.liststore.get_value(selected, 1)
@@ -225,11 +239,27 @@ class EditTrackersDialog(object):
self.edit_tracker_entry.grab_focus()
self.dialog.set_sensitive(False)
- def on_button_edit_cancel_clicked(self, widget):
- log.debug('on_button_edit_cancel_clicked')
+ def _close_edit_dialog(self):
self.dialog.set_sensitive(True)
self.edit_tracker_entry.hide()
+ def on_button_edit_cancel_clicked(self, widget):
+ """handles the cancel button"""
+ log.debug('on_button_edit_cancel_clicked')
+ self._close_edit_dialog()
+
+ def on_key_edit_press_event(self, widget, event):
+ """handles Escape key press"""
+ if event.keyval == Gdk.KEY_Escape:
+ log.debug('on_key_edit_press_event')
+ self._close_edit_dialog()
+
+ def on_delete_event_edit(self, widget, event):
+ """handles the Top-Right X button"""
+ log.debug('on_delete_event_edit')
+ self._close_edit_dialog()
+ return True
+
def on_button_edit_ok_clicked(self, widget):
log.debug('on_button_edit_ok_clicked')
selected = self.get_selected()
@@ -290,11 +320,29 @@ class EditTrackersDialog(object):
# Clear the entry widget and hide the dialog
textview_buf.set_text('')
+ self.dialog.set_sensitive(True)
self.add_tracker_dialog.hide()
- def on_button_add_cancel_clicked(self, widget):
- log.debug('on_button_add_cancel_clicked')
+ def _discard_and_close_add_dialog(self):
# Clear the entry widget and hide the dialog
b = Gtk.TextBuffer()
self.builder.get_object('textview_trackers').set_buffer(b)
+ self.dialog.set_sensitive(True)
self.add_tracker_dialog.hide()
+
+ def on_button_add_cancel_clicked(self, widget):
+ """handles the cancel button"""
+ log.debug('on_button_add_cancel_clicked')
+ self._discard_and_close_add_dialog()
+
+ def on_key_add_press_event(self, widget, event):
+ """handles Escape key press"""
+ if event.keyval == Gdk.KEY_Escape:
+ log.debug('on_key_add_press_event')
+ self._discard_and_close_add_dialog()
+
+ def on_delete_event_add(self, widget, event):
+ """handles the Top-Right X button"""
+ log.debug('on_delete_event_add')
+ self._discard_and_close_add_dialog()
+ return True
diff --git a/deluge/ui/gtk3/files_tab.py b/deluge/ui/gtk3/files_tab.py
index 6fda2fd37..6e0fba417 100644
--- a/deluge/ui/gtk3/files_tab.py
+++ b/deluge/ui/gtk3/files_tab.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,15 +6,13 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import json
import logging
import os.path
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
# isort:imports-thirdparty
from gi.repository import Gio, Gtk
@@ -84,7 +81,7 @@ def cell_progress(column, cell, model, row, data):
class FilesTab(Tab):
def __init__(self):
- super(FilesTab, self).__init__('Files', 'files_tab', 'files_tab_label')
+ super().__init__('Files', 'files_tab', 'files_tab_label')
self.listview = self.main_builder.get_object('files_listview')
# filename, size, progress string, progress value, priority, file index, icon id
@@ -248,7 +245,7 @@ class FilesTab(Tab):
if state['sort_id'] is not None and state['sort_order'] is not None:
self.treestore.set_sort_column_id(state['sort_id'], state['sort_order'])
- for (index, column) in enumerate(self.listview.get_columns()):
+ for index, column in enumerate(self.listview.get_columns()):
cname = column.get_title()
if cname in state['columns']:
cstate = state['columns'][cname]
@@ -449,7 +446,7 @@ class FilesTab(Tab):
try:
value = completed_bytes / self.treestore[parent][1] * 100
except ZeroDivisionError:
- # Catch the unusal error found when moving folders around
+ # Catch the unusual error found when moving folders around
value = 0
self.treestore[parent][3] = value
self.treestore[parent][2] = '%i%%' % value
@@ -762,7 +759,6 @@ class FilesTab(Tab):
fd['path'] = fd['path'].replace(old_folder, new_folder, 1)
if torrent_id == self.torrent_id:
-
old_split = old_folder.split('/')
try:
old_split.remove('')
diff --git a/deluge/ui/gtk3/filtertreeview.py b/deluge/ui/gtk3/filtertreeview.py
index a7b6aa87c..40752d78c 100644
--- a/deluge/ui/gtk3/filtertreeview.py
+++ b/deluge/ui/gtk3/filtertreeview.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 2008 Andrew Resch <andrewresch@gmail.com>
@@ -9,8 +8,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import warnings
@@ -24,7 +21,7 @@ from deluge.common import TORRENT_STATE, decode_bytes, resource_filename
from deluge.configmanager import ConfigManager
from deluge.ui.client import client
-from .common import get_pixbuf, get_pixbuf_at_size
+from .common import get_pixbuf
log = logging.getLogger(__name__)
@@ -90,7 +87,7 @@ class FilterTreeView(component.Component):
self.treeview.set_level_indentation(-21)
# Force theme to use expander-size so we don't cut out entries due to indentation hack.
provider = Gtk.CssProvider()
- provider.load_from_data('* {-GtkTreeView-expander-size: 9;}'.encode())
+ provider.load_from_data(b'* {-GtkTreeView-expander-size: 9;}')
context = self.treeview.get_style_context()
context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
@@ -224,7 +221,7 @@ class FilterTreeView(component.Component):
label = decode_bytes(model.get_value(row, 2))
count = model.get_value(row, 3)
- # Supress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed
+ # Suppress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed
original_filters = warnings.filters[:]
warnings.simplefilter('ignore')
try:
@@ -256,7 +253,7 @@ class FilterTreeView(component.Component):
return get_pixbuf('%s16.png' % pix)
def set_row_image(self, cat, value, filename):
- pix = get_pixbuf_at_size(filename, 16)
+ pix = get_pixbuf(filename, size=16)
row = self.filters[(cat, value)]
self.treestore.set_value(row, 4, pix)
return False
diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui
index a7a8caeaa..8adbad329 100644
--- a/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui
+++ b/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui
@@ -144,8 +144,6 @@
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.ui
index 4d3680344..b0f507d9e 100644
--- a/deluge/ui/gtk3/glade/add_torrent_dialog.ui
+++ b/deluge/ui/gtk3/glade/add_torrent_dialog.ui
@@ -4,13 +4,13 @@
<requires lib="gtk+" version="3.0"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="lower">-1</property>
- <property name="upper">9999</property>
+ <property name="upper">2097151</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="lower">-1</property>
- <property name="upper">9999</property>
+ <property name="upper">2097151</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
@@ -727,8 +727,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_maxup">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment2</property>
<property name="update_policy">if-valid</property>
</object>
@@ -741,8 +739,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_maxconnections">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment3</property>
</object>
<packing>
@@ -754,8 +750,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_maxupslots">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment4</property>
</object>
<packing>
@@ -767,8 +761,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_maxdown">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
</object>
<packing>
diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui
index ecbd0f7cf..6b75b235f 100644
--- a/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui
+++ b/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui
@@ -143,8 +143,6 @@
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/ui/gtk3/glade/connect_peer_dialog.ui b/deluge/ui/gtk3/glade/connect_peer_dialog.ui
index f5e9337ea..4a60751d0 100644
--- a/deluge/ui/gtk3/glade/connect_peer_dialog.ui
+++ b/deluge/ui/gtk3/glade/connect_peer_dialog.ui
@@ -128,8 +128,6 @@
<property name="width_chars">39</property>
<property name="text" translatable="yes">hostname:port</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/ui/gtk3/glade/connection_manager.addhost.ui b/deluge/ui/gtk3/glade/connection_manager.addhost.ui
index 641a71cc1..ea5376e45 100644
--- a/deluge/ui/gtk3/glade/connection_manager.addhost.ui
+++ b/deluge/ui/gtk3/glade/connection_manager.addhost.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
+<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkAdjustment" id="adjustment_port">
@@ -16,7 +16,7 @@
<property name="window_position">center-on-parent</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
- <child>
+ <child type="titlebar">
<placeholder/>
</child>
<child internal-child="vbox">
@@ -24,11 +24,12 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
- <property name="spacing">2</property>
+ <property name="spacing">5</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area5">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_top">15</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button_addhost_cancel">
@@ -65,49 +66,41 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
- <property name="position">0</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkBox" id="hbox2">
+ <object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="spacing">5</property>
+ <property name="row_spacing">5</property>
+ <property name="column_spacing">5</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Hostname:</property>
+ <property name="xalign">0</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
</packing>
</child>
<child>
- <object class="GtkAlignment" id="alignment4">
+ <object class="GtkEntry" id="entry_hostname">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="left_padding">1</property>
- <child>
- <object class="GtkEntry" id="entry_hostname">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">•</property>
- <property name="activates_default">True</property>
- <property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- <signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/>
- </object>
- </child>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">•</property>
+ <property name="activates_default">True</property>
+ <property name="truncate_multiline">True</property>
+ <signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
</packing>
</child>
<child>
@@ -115,116 +108,90 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Port:</property>
+ <property name="xalign">0</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_port">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="halign">start</property>
<property name="max_length">5</property>
<property name="invisible_char">•</property>
<property name="width_chars">5</property>
+ <property name="max_width_chars">5</property>
<property name="progress_pulse_step">1</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_port</property>
<property name="climb_rate">1</property>
<property name="numeric">True</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">3</property>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
</packing>
</child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid" id="table1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
- <object class="GtkAlignment" id="alignment3">
+ <object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="left_padding">5</property>
- <child>
- <object class="GtkEntry" id="entry_password">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="invisible_char">•</property>
- <property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- </object>
- </child>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Username:</property>
+ <property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
</packing>
</child>
<child>
- <object class="GtkAlignment" id="alignment2">
+ <object class="GtkEntry" id="entry_username">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="left_padding">5</property>
- <child>
- <object class="GtkEntry" id="entry_username">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">•</property>
- <property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- </object>
- </child>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">•</property>
+ <property name="truncate_multiline">True</property>
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">0</property>
+ <property name="top_attach">2</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="label5">
+ <object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
- <property name="label" translatable="yes">Username:</property>
+ <property name="label" translatable="yes">Password:</property>
+ <property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="top_attach">3</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="label6">
+ <object class="GtkEntry" id="entry_password">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Password:</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">•</property>
+ <property name="truncate_multiline">True</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">0</property>
</packing>
</child>
</object>
diff --git a/deluge/ui/gtk3/glade/connection_manager.ui b/deluge/ui/gtk3/glade/connection_manager.ui
index 11516aa76..44f4b34e3 100644
--- a/deluge/ui/gtk3/glade/connection_manager.ui
+++ b/deluge/ui/gtk3/glade/connection_manager.ui
@@ -30,7 +30,7 @@
<property name="modal">True</property>
<property name="window_position">center-on-parent</property>
<property name="default_width">300</property>
- <property name="default_height">250</property>
+ <property name="default_height">285</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<child>
diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui
index dc7b7e93e..43283305a 100644
--- a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui
+++ b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui
@@ -143,8 +143,6 @@
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui
index a380718e4..712305489 100644
--- a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui
+++ b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui
@@ -143,8 +143,6 @@
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.ui
index c27a4b866..0d1594014 100644
--- a/deluge/ui/gtk3/glade/create_torrent_dialog.ui
+++ b/deluge/ui/gtk3/glade/create_torrent_dialog.ui
@@ -356,8 +356,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -393,8 +391,6 @@
<object class="GtkEntry" id="entry_comments">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -810,6 +806,7 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_button_cancel_clicked" swapped="no"/>
+ <accelerator key="Escape" signal="clicked"/>
</object>
<packing>
<property name="expand">False</property>
diff --git a/deluge/ui/gtk3/glade/edit_trackers.edit.ui b/deluge/ui/gtk3/glade/edit_trackers.edit.ui
index 2521e8ff8..fc3e51b83 100644
--- a/deluge/ui/gtk3/glade/edit_trackers.edit.ui
+++ b/deluge/ui/gtk3/glade/edit_trackers.edit.ui
@@ -144,8 +144,6 @@
<property name="invisible_char">•</property>
<property name="activates_default">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
diff --git a/deluge/ui/gtk3/glade/main_window.tabs.ui b/deluge/ui/gtk3/glade/main_window.tabs.ui
index d4984dd9d..7ecf61821 100644
--- a/deluge/ui/gtk3/glade/main_window.tabs.ui
+++ b/deluge/ui/gtk3/glade/main_window.tabs.ui
@@ -1166,8 +1166,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">spin_stop_ratio_adjustment</property>
<property name="digits">1</property>
<property name="numeric">True</property>
@@ -1267,8 +1265,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">spin_max_connections_adjustment</property>
<property name="numeric">True</property>
</object>
@@ -1282,8 +1278,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">spin_max_upload_adjustment</property>
<property name="digits">1</property>
<property name="numeric">True</property>
@@ -1298,8 +1292,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">spin_max_download_adjustment</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
@@ -1356,8 +1348,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">spin_max_upload_slots_adjustment</property>
<property name="numeric">True</property>
</object>
diff --git a/deluge/ui/gtk3/glade/main_window.ui b/deluge/ui/gtk3/glade/main_window.ui
index ecbb8f61c..e7267560c 100644
--- a/deluge/ui/gtk3/glade/main_window.ui
+++ b/deluge/ui/gtk3/glade/main_window.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
+<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkImage" id="about-image">
@@ -7,7 +7,6 @@
<property name="can_focus">False</property>
<property name="icon_name">help-about-symbolic</property>
</object>
- <object class="GtkAccelGroup" id="accelgroup1"/>
<object class="GtkImage" id="add-image">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -67,6 +66,7 @@
<property name="icon_name">zoom-fit-best-symbolic</property>
<property name="icon_size">1</property>
</object>
+ <object class="GtkAccelGroup" id="main_accelgroup"/>
<object class="GtkImage" id="new-image">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -93,9 +93,9 @@
<property name="can_focus">False</property>
<property name="title">Deluge</property>
<accel-groups>
- <group name="accelgroup1"/>
+ <group name="main_accelgroup"/>
</accel-groups>
- <child>
+ <child type="titlebar">
<placeholder/>
</child>
<child>
@@ -114,8 +114,10 @@
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child type="submenu">
- <object class="GtkMenu" id="menuitem1_menu1">
+ <object class="GtkMenu" id="submenu_file">
<property name="can_focus">False</property>
+ <property name="accel_group">main_accelgroup</property>
+ <property name="accel_path">&lt;Deluge-MainWindow&gt;/File</property>
<child>
<object class="GtkImageMenuItem" id="menuitem_addtorrent">
<property name="label" translatable="yes">_Add Torrent</property>
@@ -126,7 +128,6 @@
<property name="image">add-image</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menuitem_addtorrent_activate" swapped="no"/>
- <accelerator key="O" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
@@ -138,7 +139,6 @@
<property name="image">new-image</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menuitem_createtorrent_activate" swapped="no"/>
- <accelerator key="N" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
@@ -155,7 +155,6 @@
<property name="use_stock">False</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_menuitem_quitdaemon_activate" swapped="no"/>
- <accelerator key="Q" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
</object>
</child>
<child>
@@ -172,9 +171,8 @@
<property name="use_underline">True</property>
<property name="image">quit_image</property>
<property name="use_stock">False</property>
- <property name="accel_group">accelgroup1</property>
+ <property name="accel_group">main_accelgroup</property>
<signal name="activate" handler="on_menuitem_quit_activate" swapped="no"/>
- <accelerator key="Q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
</object>
@@ -188,9 +186,11 @@
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
- <object class="GtkMenu" id="menu1">
+ <object class="GtkMenu" id="submenu_edit">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="accel_group">main_accelgroup</property>
+ <property name="accel_path">&lt;Deluge-MainWindow&gt;/Edit</property>
<child>
<object class="GtkImageMenuItem" id="menuitem_preferences">
<property name="label" translatable="yes">_Preferences</property>
@@ -199,9 +199,8 @@
<property name="use_underline">True</property>
<property name="image">prefs-image</property>
<property name="use_stock">False</property>
- <property name="accel_group">accelgroup1</property>
+ <property name="accel_group">main_accelgroup</property>
<signal name="activate" handler="on_menuitem_preferences_activate" swapped="no"/>
- <accelerator key="P" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
@@ -213,7 +212,6 @@
<property name="image">connection-image</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menuitem_connectionmanager_activate" swapped="no"/>
- <accelerator key="M" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
</object>
@@ -223,6 +221,7 @@
<child>
<object class="GtkMenuItem" id="menu_torrent">
<property name="can_focus">False</property>
+ <property name="accel_path">&lt;MainWindow&gt;/Torrent</property>
<property name="label" translatable="yes">_Torrent</property>
<property name="use_underline">True</property>
</object>
@@ -234,9 +233,11 @@
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<child type="submenu">
- <object class="GtkMenu" id="menu2">
+ <object class="GtkMenu" id="submenu_view">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="accel_group">main_accelgroup</property>
+ <property name="accel_path">&lt;Deluge-MainWindow&gt;/View</property>
<child>
<object class="GtkCheckMenuItem" id="menuitem_toolbar">
<property name="visible">True</property>
@@ -297,7 +298,6 @@
<property name="label" translatable="yes">_Find ...</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_search_filter_toggle" swapped="no"/>
- <accelerator key="f" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
@@ -356,8 +356,10 @@
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
- <object class="GtkMenu" id="menuitem2_menu1">
+ <object class="GtkMenu" id="submenu_help">
<property name="can_focus">False</property>
+ <property name="accel_group">main_accelgroup</property>
+ <property name="accel_path">&lt;Deluge-MainWindow&gt;/Help</property>
<child>
<object class="GtkImageMenuItem" id="menuitem_homepage">
<property name="label" translatable="yes">_Homepage</property>
@@ -377,7 +379,6 @@
<property name="use_underline">True</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menuitem_faq_activate" swapped="no"/>
- <accelerator key="F1" signal="activate"/>
</object>
</child>
<child>
@@ -405,7 +406,7 @@
<property name="use_underline">True</property>
<property name="image">about-image</property>
<property name="use_stock">False</property>
- <property name="accel_group">accelgroup1</property>
+ <property name="accel_group">main_accelgroup</property>
<signal name="activate" handler="on_menuitem_about_activate" swapped="no"/>
</object>
</child>
@@ -467,7 +468,6 @@ This will filter torrents for the current selection on the sidebar.</property>
<property name="label" translatable="yes">Filter</property>
<property name="icon_name">system-search-symbolic</property>
<signal name="clicked" handler="on_search_filter_toggle" swapped="no"/>
- <accelerator key="f" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
</object>
<packing>
<property name="expand">False</property>
diff --git a/deluge/ui/gtk3/glade/other_dialog.ui b/deluge/ui/gtk3/glade/other_dialog.ui
index 26d3d08bf..01a5597f2 100644
--- a/deluge/ui/gtk3/glade/other_dialog.ui
+++ b/deluge/ui/gtk3/glade/other_dialog.ui
@@ -148,8 +148,6 @@
<property name="max_length">6</property>
<property name="activates_default">True</property>
<property name="width_chars">6</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment1</property>
</object>
</child>
diff --git a/deluge/ui/gtk3/glade/path_combo_chooser.ui b/deluge/ui/gtk3/glade/path_combo_chooser.ui
index f79685d26..871bac01e 100644
--- a/deluge/ui/gtk3/glade/path_combo_chooser.ui
+++ b/deluge/ui/gtk3/glade/path_combo_chooser.ui
@@ -98,8 +98,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">2</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment3</property>
<property name="climb_rate">1</property>
<property name="numeric">True</property>
@@ -571,6 +569,7 @@
<object class="GtkEntry" id="entry_text">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="hexpand">True</property>
<property name="invisible_char">•</property>
<signal name="changed" handler="on_entry_text_changed" swapped="no"/>
<signal name="delete-text" handler="on_entry_text_delete_text" swapped="no"/>
diff --git a/deluge/ui/gtk3/glade/preferences_dialog.ui b/deluge/ui/gtk3/glade/preferences_dialog.ui
index 1882521fd..720dc6b40 100644
--- a/deluge/ui/gtk3/glade/preferences_dialog.ui
+++ b/deluge/ui/gtk3/glade/preferences_dialog.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
+<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkAdjustment" id="adjustment_cache_expiry">
@@ -245,7 +245,7 @@
<property name="type_hint">dialog</property>
<signal name="configure-event" handler="on_pref_dialog_configure_event" swapped="no"/>
<signal name="delete-event" handler="on_pref_dialog_delete_event" swapped="no"/>
- <child>
+ <child type="titlebar">
<placeholder/>
</child>
<child internal-child="vbox">
@@ -479,6 +479,23 @@
</packing>
</child>
<child>
+ <object class="GtkCheckButton" id="urldetect_toggle">
+ <property name="label" translatable="yes">Detect torrent URLs from clipboard</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Automatically open Add Torrent dialog when clipboard contains a torrent URL</property>
+ <property name="halign">start</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_urldetect_toggle_toggled" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkCheckButton" id="piecesbar_toggle">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -501,7 +518,7 @@ and daemon (does not apply in Standalone mode).</property>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
@@ -697,7 +714,7 @@ and daemon (does not apply in Standalone mode).</property>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">3</property>
+ <property name="position">4</property>
</packing>
</child>
</object>
@@ -719,7 +736,7 @@ and daemon (does not apply in Standalone mode).</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">5</property>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
@@ -912,8 +929,6 @@ and daemon (does not apply in Standalone mode).</property>
<property name="width_chars">16</property>
<property name="text">********</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -953,6 +968,48 @@ and daemon (does not apply in Standalone mode).</property>
</packing>
</child>
<child>
+ <object class="GtkFrame" id="frame_theme">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment_theme">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="top-padding">3</property>
+ <property name="left-padding">10</property>
+ <child>
+ <object class="GtkCheckButton" id="chk_prefer_dark_theme">
+ <property name="label" translatable="yes">Prefer Dark</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="tooltip_text" translatable="yes">If available, use dark variant of GTK theme.</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_theme">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Theme</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">5</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkFrame" id="frame19">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -1009,7 +1066,7 @@ and daemon (does not apply in Standalone mode).</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">5</property>
- <property name="position">3</property>
+ <property name="position">4</property>
</packing>
</child>
<child>
@@ -1086,7 +1143,7 @@ and daemon (does not apply in Standalone mode).</property>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">4</property>
+ <property name="position">5</property>
</packing>
</child>
</object>
@@ -1133,6 +1190,7 @@ and daemon (does not apply in Standalone mode).</property>
<property name="top_padding">2</property>
<property name="bottom_padding">2</property>
<property name="left_padding">12</property>
+ <property name="right_padding">12</property>
<child>
<object class="GtkGrid" id="table9">
<property name="visible">True</property>
@@ -1528,8 +1586,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_max_connections_per_second">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_conn_per_sec</property>
<property name="numeric">True</property>
</object>
@@ -1542,8 +1598,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_max_half_open_connections">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_half_open_conn</property>
<property name="numeric">True</property>
</object>
@@ -1607,8 +1661,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum number of connections allowed. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_conn_global</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
@@ -1638,8 +1690,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum download speed for all torrents. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_download</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
@@ -1655,8 +1705,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum upload speed for all torrents. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_upload</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
@@ -1672,8 +1720,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum upload slots for all torrents. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_upload_slots_global</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
@@ -1832,8 +1878,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum upload slots per torrent. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_upload_slots_per_torrent</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
@@ -1849,8 +1893,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum number of connections per torrent. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_conn_per_torrent</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
@@ -1913,8 +1955,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum number download speed per torrent. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_download_per_torrent</property>
<property name="digits">1</property>
<property name="numeric">True</property>
@@ -1929,8 +1969,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The maximum upload speed per torrent. Set -1 for unlimited.</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_max_upload_per_torrent</property>
<property name="digits">1</property>
<property name="numeric">True</property>
@@ -2114,8 +2152,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_active">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_active</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
@@ -2129,8 +2165,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_seeding">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_seeding</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
@@ -2168,8 +2202,6 @@ used sparingly.</property>
<object class="GtkSpinButton" id="spin_downloading">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_downloading</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
@@ -2204,7 +2236,7 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Torrents not transfering any data do not count towards download/seeding active count.</property>
+ <property name="tooltip_text" translatable="yes">Torrents not transferring any data do not count towards download/seeding active count.</property>
<property name="halign">start</property>
<property name="draw_indicator">True</property>
</object>
@@ -2306,8 +2338,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_seed_time_limit</property>
</object>
<packing>
@@ -2320,8 +2350,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_time_ratio_limit</property>
<property name="digits">2</property>
</object>
@@ -2335,8 +2363,6 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_share_ratio_limit</property>
<property name="digits">2</property>
</object>
@@ -2408,8 +2434,6 @@ used sparingly.</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_share_ratio</property>
<property name="digits">2</property>
<property name="numeric">True</property>
@@ -2555,12 +2579,10 @@ used sparingly.</property>
<object class="GtkEntry" id="entry_interface">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="tooltip_text" translatable="yes">The IP address of the interface to listen for incoming bittorrent connections on. Leave this empty if you want to use the default.</property>
- <property name="max_length">15</property>
+ <property name="tooltip_text" translatable="yes">IP address or network interface name to listen for incoming BitTorrent connections. Leave empty to use system default.</property>
+ <property name="max_length">40</property>
<property name="width_chars">15</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
</child>
</object>
@@ -2569,7 +2591,7 @@ used sparingly.</property>
<object class="GtkLabel" id="label110">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">Incoming Address</property>
+ <property name="label" translatable="yes">Incoming Interface</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
@@ -2611,8 +2633,6 @@ used sparingly.</property>
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="max_width_chars">6</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_incoming_port</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
@@ -2711,6 +2731,24 @@ used sparingly.</property>
</packing>
</child>
<child>
+ <object class="GtkAlignment" id="alignment31">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">10</property>
+ <child>
+ <object class="GtkSpinner" id="port_spinner">
+ <property name="visible">False</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkAlignment" id="alignment48">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -2725,7 +2763,7 @@ used sparingly.</property>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
</object>
@@ -2776,14 +2814,12 @@ used sparingly.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">
-The network interface name or IP address for outgoing BitTorrent connections. (Leave empty for default.)
+ IP address or network interface name for outgoing BitTorrent connections. Leave empty to use system default.
</property>
- <property name="max_length">15</property>
+ <property name="max_length">40</property>
<property name="invisible_char">●</property>
<property name="width_chars">15</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
</child>
</object>
@@ -2863,8 +2899,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="width_chars">7</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_outgoing_port_min</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
@@ -2896,8 +2930,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="width_chars">7</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_outgoing_port_max</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
@@ -3219,8 +3251,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<property name="width_chars">1</property>
<property name="text">0x00</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -3328,8 +3358,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -3353,8 +3381,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<signal name="paste-clipboard" handler="on_entry_proxy_host_paste_clipboard" swapped="no"/>
</object>
<packing>
@@ -3382,8 +3408,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<object class="GtkSpinButton" id="spin_proxy_port">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_proxy_port</property>
<property name="numeric">True</property>
</object>
@@ -3399,8 +3423,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -3679,8 +3701,6 @@ the proxy instead of using the local DNS service</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_cache_size</property>
<property name="numeric">True</property>
<property name="update_policy">if-valid</property>
@@ -3696,8 +3716,6 @@ the proxy instead of using the local DNS service</property>
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="invisible_char">●</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_cache_expiry</property>
</object>
<packing>
@@ -3880,28 +3898,16 @@ the proxy instead of using the local DNS service</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="label119">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">The number of blocks that were served from cache.</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Blocks Read Hit:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
<object class="GtkLabel" id="label122">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">The cache hit ratio for the read cache.</property>
+ <property name="halign">start</property>
<property name="label" translatable="yes">Read Cache Hit Ratio:</property>
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">3</property>
+ <property name="top_attach">2</property>
</packing>
</child>
<child>
@@ -3915,23 +3921,13 @@ the proxy instead of using the local DNS service</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="label_cache_num_blocks_cache_hits">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
<object class="GtkLabel" id="label_cache_read_hit_ratio">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">3</property>
+ <property name="top_attach">2</property>
</packing>
</child>
<child>
@@ -3945,7 +3941,7 @@ the proxy instead of using the local DNS service</property>
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="top_attach">1</property>
</packing>
</child>
<child>
@@ -3955,7 +3951,7 @@ the proxy instead of using the local DNS service</property>
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">2</property>
+ <property name="top_attach">1</property>
</packing>
</child>
</object>
@@ -4253,8 +4249,6 @@ the proxy instead of using the local DNS service</property>
<property name="tooltip_text" translatable="yes">If Deluge cannot find the database file at this location it will fallback to using DNS to resolve the peer's country.</property>
<property name="invisible_char">●</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -4432,8 +4426,6 @@ the proxy instead of using the local DNS service</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_width_chars">6</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment_spin_daemon_port</property>
</object>
<packing>
diff --git a/deluge/ui/gtk3/glade/torrent_menu.ui b/deluge/ui/gtk3/glade/torrent_menu.ui
index c1b77b4ad..c9ee289c1 100644
--- a/deluge/ui/gtk3/glade/torrent_menu.ui
+++ b/deluge/ui/gtk3/glade/torrent_menu.ui
@@ -31,6 +31,11 @@
<property name="icon_name">media-playback-pause-symbolic</property>
<property name="icon_size">1</property>
</object>
+ <object class="GtkImage" id="menu-item-image15">
+ <property name="can_focus">False</property>
+ <property name="icon_name">edit-copy-symbolic</property>
+ <property name="icon_size">1</property>
+ </object>
<object class="GtkImage" id="menu-item-image19">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -155,6 +160,17 @@
</object>
</child>
<child>
+ <object class="GtkImageMenuItem" id="menuitem_copymagnet">
+ <property name="label" translatable="yes">_Copy Magnet URI</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="image">menu-item-image15</property>
+ <property name="use_stock">False</property>
+ <signal name="activate" handler="on_menuitem_copymagnet_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
<object class="GtkImageMenuItem" id="menuitem_updatetracker">
<property name="label" translatable="yes">_Update Tracker</property>
<property name="visible">True</property>
diff --git a/deluge/ui/gtk3/gtkui.py b/deluge/ui/gtk3/gtkui.py
index ebb78e8b2..73a7c97ae 100644
--- a/deluge/ui/gtk3/gtkui.py
+++ b/deluge/ui/gtk3/gtkui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,8 +7,6 @@
#
# pylint: disable=wrong-import-position
-from __future__ import division, unicode_literals
-
import logging
import os
import signal
@@ -18,8 +15,8 @@ import time
import gi # isort:skip (Required before Gtk import).
-gi.require_version('Gtk', '3.0') # NOQA: E402
-gi.require_version('Gdk', '3.0') # NOQA: E402
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
# isort:imports-thirdparty
from gi.repository.GLib import set_prgname
@@ -32,7 +29,7 @@ try:
# Install twisted reactor, before any other modules import reactor.
reactor = gtk3reactor.install()
except ReactorAlreadyInstalledError:
- # Running unit tests so trial already installed a rector
+ # Running unit tests so already installed a rector
from twisted.internet import reactor
# isort:imports-firstparty
@@ -87,12 +84,13 @@ except ImportError:
DEFAULT_PREFS = {
'standalone': True,
+ 'prefer_dark_theme': False,
'interactive_add': True,
'focus_add_dialog': True,
'enable_system_tray': True,
'close_to_tray': False,
'start_in_tray': False,
- 'enable_appindicator': False,
+ 'enable_appindicator': True,
'lock_tray': False,
'tray_password': '',
'check_new_releases': True,
@@ -130,6 +128,7 @@ DEFAULT_PREFS = {
'show_rate_in_title': False,
'createtorrent.trackers': [],
'show_piecesbar': False,
+ 'detect_urls': True,
'pieces_color_missing': [65535, 0, 0],
'pieces_color_waiting': [4874, 56494, 0],
'pieces_color_downloading': [65535, 55255, 0],
@@ -139,7 +138,7 @@ DEFAULT_PREFS = {
}
-class GtkUI(object):
+class GtkUI:
def __init__(self, args):
# Setup gtkbuilder/glade translation
setup_translation()
@@ -221,7 +220,7 @@ class GtkUI(object):
menubar_osx(self, self.osxapp)
self.osxapp.ready()
- # Initalize the plugins
+ # Initialize the plugins
self.plugins = PluginManager()
# Show the connection manager
diff --git a/deluge/ui/gtk3/ipcinterface.py b/deluge/ui/gtk3/ipcinterface.py
index 78858c443..0ef28d8c0 100644
--- a/deluge/ui/gtk3/ipcinterface.py
+++ b/deluge/ui/gtk3/ipcinterface.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,14 +6,14 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import sys
from base64 import b64encode
from glob import glob
from tempfile import mkstemp
+from urllib.parse import urlparse
+from urllib.request import url2pathname
import rencode
import twisted.internet.error
@@ -26,14 +25,6 @@ from deluge.common import decode_bytes, is_magnet, is_url, windows_check
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.ui.client import client
-try:
- from urllib.parse import urlparse
- from urllib.request import url2pathname
-except ImportError:
- # PY2 fallback
- from urllib import url2pathname # pylint: disable=ungrouped-imports
- from urlparse import urlparse # pylint: disable=ungrouped-imports
-
log = logging.getLogger(__name__)
diff --git a/deluge/ui/gtk3/listview.py b/deluge/ui/gtk3/listview.py
index 07b366978..a80d79507 100644
--- a/deluge/ui/gtk3/listview.py
+++ b/deluge/ui/gtk3/listview.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,20 +6,18 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from gi.repository import GObject, Gtk
-from deluge.common import PY2, decode_bytes
+from deluge.common import decode_bytes
from .common import cmp, load_pickled_state_file, save_pickled_state_file
log = logging.getLogger(__name__)
-class ListViewColumnState(object):
+class ListViewColumnState:
"""Class used for saving/loading column state."""
def __init__(self, name, position, width, visible, sort, sort_order):
@@ -32,13 +29,13 @@ class ListViewColumnState(object):
self.sort_order = sort_order
-class ListView(object):
+class ListView:
"""ListView is used to make custom GtkTreeViews. It supports the adding
and removing of columns, creating a menu for a column toggle list and
support for 'status_field's which are used while updating the columns data.
"""
- class ListViewColumn(object):
+ class ListViewColumn:
"""Holds information regarding a column in the ListView"""
def __init__(self, name, column_indices):
@@ -66,7 +63,7 @@ class ListView(object):
self.pixbuf_index = 0
self.data_func = None
- class TreeviewColumn(Gtk.TreeViewColumn, object):
+ class TreeviewColumn(Gtk.TreeViewColumn):
"""
TreeViewColumn does not signal right-click events, and we need them
This subclass is equivalent to TreeViewColumn, but it signals these events
@@ -75,13 +72,11 @@ class ListView(object):
"""
__gsignals__ = {
- 'button-press-event'
- if not PY2
- else b'button-press-event': (GObject.SIGNAL_RUN_LAST, None, (object,))
+ 'button-press-event': (GObject.SignalFlags.RUN_LAST, None, (object,))
}
def __init__(self, title=None, cell_renderer=None, **args):
- """ Constructor, see Gtk.TreeViewColumn """
+ """Constructor, see Gtk.TreeViewColumn"""
Gtk.TreeViewColumn.__init__(self, title, cell_renderer, **args)
label = Gtk.Label(label=title)
self.set_widget(label)
@@ -112,7 +107,7 @@ class ListView(object):
Gtk.TreeViewColumn.set_visible(self, visible)
if self.data_func:
if not visible:
- # Set data function to None to prevent unecessary calls when column is hidden
+ # Set data function to None to prevent unnecessary calls when column is hidden
self.set_cell_data_func(self.cell_renderer, None, func_data=None)
else:
self.set_cell_data_func(
diff --git a/deluge/ui/gtk3/mainwindow.py b/deluge/ui/gtk3/mainwindow.py
index 740a9d13a..6c871d2d8 100644
--- a/deluge/ui/gtk3/mainwindow.py
+++ b/deluge/ui/gtk3/mainwindow.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os.path
from hashlib import sha1 as sha
@@ -20,11 +17,11 @@ from twisted.internet import reactor
from twisted.internet.error import ReactorNotRunning
import deluge.component as component
-from deluge.common import decode_bytes, fspeed, resource_filename
+from deluge.common import decode_bytes, fspeed, is_magnet, is_url, resource_filename
from deluge.configmanager import ConfigManager
from deluge.ui.client import client
-from .common import get_deluge_icon, windowing
+from .common import get_clipboard_text, get_deluge_icon, windowing
from .dialogs import PasswordDialog
from .ipcinterface import process_args
@@ -45,9 +42,8 @@ if windowing('X11'):
log = logging.getLogger(__name__)
-class _GtkBuilderSignalsHolder(object):
+class _GtkBuilderSignalsHolder:
def connect_signals(self, mapping_or_class):
-
if isinstance(mapping_or_class, dict):
for name, handler in mapping_or_class.items():
if hasattr(self, name):
@@ -76,6 +72,12 @@ class MainWindow(component.Component):
self.config = ConfigManager('gtk3ui.conf')
self.main_builder = Gtk.Builder()
+ # Set theme
+ Gtk.Settings.get_default().set_property(
+ 'gtk-application-prefer-dark-theme',
+ self.config['prefer_dark_theme'],
+ )
+
# Patch this GtkBuilder to avoid connecting signals from elsewhere
#
# Think about splitting up mainwindow gtkbuilder file into the necessary parts
@@ -132,6 +134,7 @@ class MainWindow(component.Component):
self.window.connect('configure-event', self.on_window_configure_event)
self.window.connect('delete-event', self.on_window_delete_event)
self.window.connect('drag-data-received', self.on_drag_data_received_event)
+ self.window.connect('notify::is-active', self.on_focus)
self.tabsbar_pane.connect(
'notify::position', self.on_tabsbar_pane_position_event
)
@@ -148,6 +151,9 @@ class MainWindow(component.Component):
'NewVersionAvailableEvent', self.on_newversionavailable_event
)
+ self.previous_clipboard_text = ''
+ self.first_run = True
+
def connect_signals(self, mapping_or_class):
self.gtk_builder_signals_holder.connect_signals(mapping_or_class)
@@ -330,6 +336,21 @@ class MainWindow(component.Component):
def on_expose_event(self, widget, event):
component.get('SystemTray').blink(False)
+ def on_focus(self, window, param):
+ if window.props.is_active and not self.first_run and self.config['detect_urls']:
+ text = get_clipboard_text()
+ if text == self.previous_clipboard_text:
+ return
+ self.previous_clipboard_text = text
+ if text and (
+ (is_url(text) and text.endswith('.torrent'))
+ or is_magnet(text)
+ and not component.get('MenuBar').magnet_copied()
+ ):
+ component.get('AddTorrentDialog').show()
+ component.get('AddTorrentDialog').on_button_url_clicked(window)
+ self.first_run = False
+
def stop(self):
self.window.set_title('Deluge')
diff --git a/deluge/ui/gtk3/menubar.py b/deluge/ui/gtk3/menubar.py
index e09f394fc..9165320fe 100644
--- a/deluge/ui/gtk3/menubar.py
+++ b/deluge/ui/gtk3/menubar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -9,8 +8,6 @@
#
-from __future__ import unicode_literals
-
import logging
import os.path
@@ -21,11 +18,22 @@ import deluge.component as component
from deluge.configmanager import ConfigManager
from deluge.ui.client import client
-from .dialogs import ErrorDialog, OtherDialog
+from .dialogs import CopyMagnetDialog, ErrorDialog, OtherDialog
from .path_chooser import PathChooser
log = logging.getLogger(__name__)
+default_main_window_accelmap = {
+ '<Deluge-MainWindow>/File/Add Torrent': '<Primary>o',
+ '<Deluge-MainWindow>/File/Create Torrent': '<Primary>n',
+ '<Deluge-MainWindow>/File/Quit & Shutdown Daemon': '<Primary><Shift>q',
+ '<Deluge-MainWindow>/File/Quit': '<Primary>q',
+ '<Deluge-MainWindow>/Edit/Preferences': '<Primary>p',
+ '<Deluge-MainWindow>/Edit/Connection Manager': '<Primary>m',
+ '<Deluge-MainWindow>/View/Find ...': '<Primary>f',
+ '<Deluge-MainWindow>/Help/FAQ': 'F1',
+}
+
class MenuBar(component.Component):
def __init__(self):
@@ -34,6 +42,7 @@ class MenuBar(component.Component):
self.mainwindow = component.get('MainWindow')
self.main_builder = self.mainwindow.get_builder()
self.config = ConfigManager('gtk3ui.conf')
+ self._magnet_copied = False
self.builder = Gtk.Builder()
# Get the torrent menu from the gtk builder file
@@ -114,6 +123,11 @@ class MenuBar(component.Component):
# Attach the torrent_menu to the Torrent file menu
self.menu_torrent.set_submenu(self.torrentmenu)
+ # Set keyboard shortcuts
+ for accel_path, accelerator in default_main_window_accelmap.items():
+ accel_key, accel_mods = Gtk.accelerator_parse(accelerator)
+ Gtk.AccelMap.change_entry(accel_path, accel_key, accel_mods, True)
+
# Make sure the view menuitems are showing the correct active state
self.main_builder.get_object('menuitem_toolbar').set_active(
self.config['show_toolbar']
@@ -142,6 +156,19 @@ class MenuBar(component.Component):
self.change_sensitivity = ['menuitem_addtorrent']
+ def magnet_copied(self):
+ """
+ lets the caller know whether a magnet was copied internally
+
+ the `mainwindow` checks every time the data in the clipboard,
+ so it will automatically open the AddTorrentURL dialog in case it
+ contains a valid link (URL to a torrent or a magnet URI).
+
+ """
+ val = self._magnet_copied
+ self._magnet_copied = False
+ return val
+
def start(self):
for widget in self.change_sensitivity:
self.main_builder.get_object(widget).set_sensitive(True)
@@ -282,6 +309,21 @@ class MenuBar(component.Component):
component.get('TorrentView').get_selected_torrents()
)
+ def on_menuitem_copymagnet_activate(self, data=None):
+ log.debug('on_menuitem_copymagnet_activate')
+ torrent_ids = component.get('TorrentView').get_selected_torrents()
+ if torrent_ids:
+
+ def _on_magnet_uri(magnet_uri):
+ def update_copied(response_id):
+ if dialog.copied:
+ self._magnet_copied = True
+
+ dialog = CopyMagnetDialog(magnet_uri)
+ dialog.run().addCallback(update_copied)
+
+ client.core.get_magnet_uri(torrent_ids[0]).addCallback(_on_magnet_uri)
+
def on_menuitem_updatetracker_activate(self, data=None):
log.debug('on_menuitem_updatetracker_activate')
client.core.force_reannounce(
@@ -536,42 +578,26 @@ class MenuBar(component.Component):
component.get('FilterTreeView').update()
def _on_known_accounts(self, known_accounts):
- known_accounts_to_log = []
- for account in known_accounts:
- account_to_log = {}
- for key, value in account.copy().items():
- if key == 'password':
- value = '*' * len(value)
- account_to_log[key] = value
- known_accounts_to_log.append(account_to_log)
- log.debug('_on_known_accounts: %s', known_accounts_to_log)
+ menuitem_change_owner = self.builder.get_object('menuitem_change_owner')
if len(known_accounts) <= 1:
+ menuitem_change_owner.set_visible(False)
return
- self.builder.get_object('menuitem_change_owner').set_visible(True)
-
- self.change_owner_submenu = Gtk.Menu()
- self.change_owner_submenu_items = {}
- maingroup = Gtk.RadioMenuItem()
-
- self.change_owner_submenu_items[None] = Gtk.RadioMenuItem(maingroup)
+ self.users_menu = Gtk.Menu()
+ self.users_menu_items = {}
+ menu_group = None
for account in known_accounts:
username = account['username']
- item = Gtk.RadioMenuItem.new_with_label(maingroup, username)
- self.change_owner_submenu_items[username] = item
- self.change_owner_submenu.append(item)
+ item = Gtk.RadioMenuItem.new_with_label(menu_group, username)
+ menu_group = item.get_group()
item.connect('toggled', self._on_change_owner_toggled, username)
+ self.users_menu_items[username] = item
+ self.users_menu.append(item)
- self.change_owner_submenu.show_all()
- self.change_owner_submenu_items[None].set_active(True)
- self.change_owner_submenu_items[None].hide()
- self.builder.get_object('menuitem_change_owner').connect(
- 'activate', self._on_change_owner_submenu_active
- )
- self.builder.get_object('menuitem_change_owner').set_submenu(
- self.change_owner_submenu
- )
+ self.users_menu.show_all()
+ menuitem_change_owner.set_submenu(self.users_menu)
+ menuitem_change_owner.set_visible(True)
def _on_known_accounts_fail(self, reason):
self.builder.get_object('menuitem_change_owner').set_visible(False)
@@ -580,13 +606,13 @@ class MenuBar(component.Component):
log.debug('_on_change_owner_submenu_active')
selected = component.get('TorrentView').get_selected_torrents()
if len(selected) > 1:
- self.change_owner_submenu_items[None].set_active(True)
+ self.users_menu_items[None].set_active(True)
return
torrent_owner = component.get('TorrentView').get_torrent_status(selected[0])[
'owner'
]
- for username, item in self.change_owner_submenu_items.items():
+ for username, item in self.users_menu_items.items():
item.set_active(username == torrent_owner)
def _on_change_owner_toggled(self, widget, username):
diff --git a/deluge/ui/gtk3/menubar_osx.py b/deluge/ui/gtk3/menubar_osx.py
index 1df6fab08..7f846dc72 100644
--- a/deluge/ui/gtk3/menubar_osx.py
+++ b/deluge/ui/gtk3/menubar_osx.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -6,50 +5,36 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-
-from __future__ import unicode_literals
-
-from gi.repository.Gdk import ModifierType
-from gi.repository.Gtk import SeparatorMenuItem, accel_groups_from_object
-from gi.repository.Gtk.AccelFlags import VISIBLE
+from gi.repository import Gtk
from deluge.configmanager import ConfigManager
-
-def accel_swap(item, group, skey, smod, dkey, dmod):
- # Accel map hack broken, see ticket #3078
- # item.remove_accelerator(group, ord(skey), smod)
- item.add_accelerator('activate', group, ord(dkey), dmod, VISIBLE)
-
-
-def accel_meta(item, group, key):
- accel_swap(item, group, key, ModifierType.CONTROL_MASK, key, ModifierType.META_MASK)
+macos_main_window_accelmap = {
+ '<Deluge-MainWindow>/File/Add Torrent': '<Meta>o',
+ '<Deluge-MainWindow>/File/Create Torrent': '<Meta>n',
+ '<Deluge-MainWindow>/File/Quit & Shutdown Daemon': '<Meta><Shift>q',
+ '<Deluge-MainWindow>/File/Quit': '<Meta>q',
+ '<Deluge-MainWindow>/Edit/Preferences': '<Meta>comma',
+ '<Deluge-MainWindow>/Edit/Connection Manager': '<Meta>m',
+ '<Deluge-MainWindow>/View/Find ...': '<Meta>f',
+ '<Deluge-MainWindow>/Help/FAQ': '<Meta>question',
+}
def menubar_osx(gtkui, osxapp):
+ # Change key shortcuts
+ for accel_path, accelerator in macos_main_window_accelmap.items():
+ accel_key, accel_mods = Gtk.accelerator_parse(accelerator)
+ Gtk.AccelMap.change_entry(accel_path, accel_key, accel_mods, True)
+
main_builder = gtkui.mainwindow.get_builder()
menubar = main_builder.get_object('menubar')
- group = accel_groups_from_object(gtkui.mainwindow.window)[0]
config = ConfigManager('gtk3ui.conf')
-
- # NOTE: accel maps doesn't work with glade file format
- # because of libglade not setting MenuItem accel groups
- # That's why we remove / set accelerators by hand... (dirty)
- # Clean solution: migrate glades files to gtkbuilder format
file_menu = main_builder.get_object('menu_file').get_submenu()
file_items = file_menu.get_children()
- accel_meta(file_items[0], group, 'o')
- accel_meta(file_items[1], group, 'n')
quit_all_item = file_items[3]
- accel_swap(
- quit_all_item,
- group,
- 'q',
- ModifierType.SHIFT_MASK | ModifierType.CONTROL_MASK,
- 'q',
- ModifierType.SHIFT_MASK | ModifierType.META_MASK,
- )
+
for item in range(2, len(file_items)): # remove quits
file_menu.remove(file_items[item])
@@ -57,13 +42,9 @@ def menubar_osx(gtkui, osxapp):
edit_menu = menu_widget.get_submenu()
edit_items = edit_menu.get_children()
pref_item = edit_items[0]
- accel_swap(
- pref_item, group, 'p', ModifierType.CONTROL_MASK, ',', ModifierType.META_MASK
- )
edit_menu.remove(pref_item)
conn_item = edit_items[1]
- accel_meta(conn_item, group, 'm')
edit_menu.remove(conn_item)
menubar.remove(menu_widget)
@@ -78,10 +59,10 @@ def menubar_osx(gtkui, osxapp):
osxapp.set_menu_bar(menubar)
# populate app menu
osxapp.insert_app_menu_item(about_item, 0)
- osxapp.insert_app_menu_item(SeparatorMenuItem(), 1)
+ osxapp.insert_app_menu_item(Gtk.SeparatorMenuItem(), 1)
osxapp.insert_app_menu_item(pref_item, 2)
if not config['standalone']:
osxapp.insert_app_menu_item(conn_item, 3)
if quit_all_item.get_visible():
- osxapp.insert_app_menu_item(SeparatorMenuItem(), 4)
+ osxapp.insert_app_menu_item(Gtk.SeparatorMenuItem(), 4)
osxapp.insert_app_menu_item(quit_all_item, 5)
diff --git a/deluge/ui/gtk3/new_release_dialog.py b/deluge/ui/gtk3/new_release_dialog.py
index 6aa328260..a635bd2cd 100644
--- a/deluge/ui/gtk3/new_release_dialog.py
+++ b/deluge/ui/gtk3/new_release_dialog.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from gi.repository.Gtk import IconSize
import deluge.common
@@ -17,7 +14,7 @@ from deluge.configmanager import ConfigManager
from deluge.ui.client import client
-class NewReleaseDialog(object):
+class NewReleaseDialog:
def __init__(self):
pass
diff --git a/deluge/ui/gtk3/options_tab.py b/deluge/ui/gtk3/options_tab.py
index 6a25fd1e8..b0411a8b6 100644
--- a/deluge/ui/gtk3/options_tab.py
+++ b/deluge/ui/gtk3/options_tab.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
# 2017 Calum Lind <calumlind+deluge@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from gi.repository.Gdk import keyval_name
import deluge.component as component
@@ -21,7 +18,7 @@ from .torrentdetails import Tab
class OptionsTab(Tab):
def __init__(self):
- super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label')
+ super().__init__('Options', 'options_tab', 'options_tab_label')
self.prev_torrent_ids = None
self.prev_status = None
@@ -191,8 +188,9 @@ class OptionsTab(Tab):
):
options[status_key] = widget_value
- if options.get('move_completed', False):
- options['move_completed_path'] = self.move_completed_path_chooser.get_text()
+ move_completed_path = self.move_completed_path_chooser.get_text()
+ if move_completed_path != self.prev_status['move_completed_path']:
+ options['move_completed_path'] = move_completed_path
client.core.set_torrent_options(self.prev_torrent_ids, options)
self.button_apply.set_sensitive(False)
diff --git a/deluge/ui/gtk3/path_chooser.py b/deluge/ui/gtk3/path_chooser.py
index b7228415e..805819660 100644
--- a/deluge/ui/gtk3/path_chooser.py
+++ b/deluge/ui/gtk3/path_chooser.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 Bro <bro.development@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -125,7 +122,7 @@ class PathChoosersHandler(component.Component):
class PathChooser(PathChooserComboBox):
def __init__(self, paths_config_key=None, parent=None):
self.paths_config_key = paths_config_key
- super(PathChooser, self).__init__(parent=parent)
+ super().__init__(parent=parent)
self.chooser_handler = PathChoosersHandler()
self.chooser_handler.register_chooser(self)
self.set_auto_completer_func(self.on_completion)
diff --git a/deluge/ui/gtk3/path_combo_chooser.py b/deluge/ui/gtk3/path_combo_chooser.py
index 72e98e497..aeb4c7a9e 100755
--- a/deluge/ui/gtk3/path_combo_chooser.py
+++ b/deluge/ui/gtk3/path_combo_chooser.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 Bro <bro.development@gmail.com>
#
@@ -8,15 +7,13 @@
# See LICENSE for more details.
#
-from __future__ import division, print_function, unicode_literals
-
import os
import warnings
from gi.repository import Gdk, GObject, Gtk
from gi.repository.GObject import SignalFlags
-from deluge.common import PY2, resource_filename
+from deluge.common import resource_filename
from deluge.path_chooser_common import get_completion_paths
# Filter the pygobject signal warning:
@@ -64,8 +61,7 @@ def path_without_trailing_path_sep(path):
return path
-class ValueList(object):
-
+class ValueList:
paths_without_trailing_path_sep = False
def get_values_count(self):
@@ -176,7 +172,7 @@ class ValueList(object):
"""
for i, row in enumerate(self.tree_store):
if row[0] == value:
- self.treeview.set_cursor((i))
+ self.treeview.set_cursor(i)
return
# The value was not found
if select_first:
@@ -374,7 +370,7 @@ class StoredValuesList(ValueList):
"""
# This is left click
if event.button != 3:
- super(StoredValuesList, self).on_treeview_mouse_button_press_event(
+ super().on_treeview_mouse_button_press_event(
treeview, event, double_click=True
)
return False
@@ -412,9 +408,7 @@ class StoredValuesList(ValueList):
PathChooserPopup.popup(self)
def on_stored_values_treeview_key_press_event(self, widget, event):
- super(StoredValuesList, self).on_value_list_treeview_key_press_event(
- widget, event
- )
+ super().on_value_list_treeview_key_press_event(widget, event)
# Prevent the default event handler to move the cursor in the list
if key_is_up_or_down(event.keyval):
return True
@@ -479,9 +473,9 @@ class CompletionList(ValueList):
] = self.on_completion_treeview_motion_notify_event
# Add super class signal handler
- self.signal_handlers['on_completion_treeview_mouse_button_press_event'] = super(
- CompletionList, self
- ).on_treeview_mouse_button_press_event
+ self.signal_handlers[
+ 'on_completion_treeview_mouse_button_press_event'
+ ] = super().on_treeview_mouse_button_press_event
def reduce_values(self, prefix):
"""
@@ -499,9 +493,7 @@ class CompletionList(ValueList):
self.add_values(matching_values, clear=True)
def on_completion_treeview_key_press_event(self, widget, event):
- ret = super(CompletionList, self).on_value_list_treeview_key_press_event(
- widget, event
- )
+ ret = super().on_value_list_treeview_key_press_event(widget, event)
if ret:
return ret
keyval = event.keyval
@@ -529,7 +521,7 @@ class CompletionList(ValueList):
self.handle_list_scroll(path=path[0], _next=None)
-class PathChooserPopup(object):
+class PathChooserPopup:
"""This creates the popop window for the ComboEntry."""
def __init__(self, min_visible_rows, max_visible_rows, popup_alignment_widget):
@@ -983,7 +975,7 @@ class PathCompletionPopup(CompletionList, PathChooserPopup):
return True
-class PathAutoCompleter(object):
+class PathAutoCompleter:
def __init__(self, builder, path_entry, max_visible_rows):
self.completion_popup = PathCompletionPopup(
builder, path_entry, max_visible_rows
@@ -1104,11 +1096,8 @@ class PathAutoCompleter(object):
class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject):
-
__gsignals__ = {
- signal
- if not PY2
- else signal.encode(): (SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,))
+ signal: (SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,))
for signal in [
'text-changed',
'accelerator-set',
@@ -1414,7 +1403,7 @@ class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject):
self.set_text(self.get_text())
def _on_entry_combobox_hbox_realize(self, widget):
- """ Must do this when the widget is realized """
+ """Must do this when the widget is realized"""
self.set_filechooser_button_visible(self.filechooser_visible)
self.set_path_entry_visible(self.path_entry_visible)
@@ -1466,7 +1455,7 @@ class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject):
)
return True
elif is_ascii_value(keyval, 's'):
- super(PathChooserComboBox, self).add_current_value_to_saved_list()
+ super().add_current_value_to_saved_list()
return True
elif is_ascii_value(keyval, 'd'):
# Set the default value in the text entry
@@ -1696,7 +1685,7 @@ if __name__ == '__main__':
box1 = Gtk.Box.new(Gtk.Orientation.VERTICAL, spacing=0)
def get_resource2(filename):
- return '%s/glade/%s' % (os.path.abspath(os.path.dirname(sys.argv[0])), filename)
+ return f'{os.path.abspath(os.path.dirname(sys.argv[0]))}/glade/{filename}'
# Override get_resource which fetches from deluge install
# get_resource = get_resource2
diff --git a/deluge/ui/gtk3/peers_tab.py b/deluge/ui/gtk3/peers_tab.py
index e0282becf..5768fbe3c 100644
--- a/deluge/ui/gtk3/peers_tab.py
+++ b/deluge/ui/gtk3/peers_tab.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os.path
@@ -42,18 +39,12 @@ from .torrentview_data_funcs import (
cell_data_speed_up,
)
-try:
- from future_builtins import zip
-except ImportError:
- # Ignore on Py3.
- pass
-
log = logging.getLogger(__name__)
class PeersTab(Tab):
def __init__(self):
- super(PeersTab, self).__init__('Peers', 'peers_tab', 'peers_tab_label')
+ super().__init__('Peers', 'peers_tab', 'peers_tab_label')
self.peer_menu = self.main_builder.get_object('menu_peer_tab')
component.get('MainWindow').connect_signals(self)
@@ -195,7 +186,7 @@ class PeersTab(Tab):
if state['sort_id'] and state['sort_order'] is not None:
self.liststore.set_sort_column_id(state['sort_id'], state['sort_order'])
- for (index, column) in enumerate(self.listview.get_columns()):
+ for index, column in enumerate(self.listview.get_columns()):
cname = column.get_title()
if cname in state['columns']:
cstate = state['columns'][cname]
@@ -313,7 +304,7 @@ class PeersTab(Tab):
ip_int = int(
binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16
)
- peer_ip = '[%s]:%s' % (ip, peer['ip'].split(':')[-1])
+ peer_ip = '[{}]:{}'.format(ip, peer['ip'].split(':')[-1])
if peer['seed']:
icon = self.seed_pixbuf
diff --git a/deluge/ui/gtk3/piecesbar.py b/deluge/ui/gtk3/piecesbar.py
index ba03e5520..a5bf86549 100644
--- a/deluge/ui/gtk3/piecesbar.py
+++ b/deluge/ui/gtk3/piecesbar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
#
@@ -7,14 +6,13 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
from math import pi
import gi # isort:skip (Version check required before import).
-gi.require_version('PangoCairo', '1.0') # NOQA: E402
-gi.require_version('cairo', '1.0') # NOQA: E402
+gi.require_version('PangoCairo', '1.0')
+gi.require_foreign('cairo')
+gi.require_version('cairo', '1.0')
# isort:imports-thirdparty
import cairo # Backward compat cairo <= 1.15
@@ -23,7 +21,6 @@ from gi.repository.Gtk import DrawingArea, ProgressBar, StateFlags
from gi.repository.Pango import SCALE, Weight
# isort:imports-firstparty
-from deluge.common import PY2
from deluge.configmanager import ConfigManager
COLOR_STATES = ['missing', 'waiting', 'downloading', 'completed']
@@ -31,14 +28,15 @@ COLOR_STATES = ['missing', 'waiting', 'downloading', 'completed']
class PiecesBar(DrawingArea):
# Draw in response to an draw
- __gsignals__ = {'draw': 'override'} if not PY2 else {b'draw': b'override'}
+ __gsignals__ = {'draw': 'override'}
def __init__(self):
- super(PiecesBar, self).__init__()
+ super().__init__()
# Get progress bar styles, in order to keep font consistency
pb = ProgressBar()
pb_style = pb.get_style_context()
- self.text_font = pb_style.get_property('font', StateFlags.NORMAL)
+ # Get a copy of Pango.FontDescription since original needs freed.
+ self.text_font = pb_style.get_property('font', StateFlags.NORMAL).copy()
self.text_font.set_weight(Weight.BOLD)
# Done with the ProgressBar styles, don't keep refs of it
del pb, pb_style
@@ -53,7 +51,6 @@ class PiecesBar(DrawingArea):
self.text = self.prev_text = ''
self.fraction = self.prev_fraction = 0
self.progress_overlay = self.text_overlay = self.pieces_overlay = None
- self.cr = None
self.connect('size-allocate', self.do_size_allocate_event)
self.show()
@@ -65,34 +62,30 @@ class PiecesBar(DrawingArea):
self.height = size.height
# Handle the draw by drawing
- def do_draw(self, event):
- # Create cairo context
- self.cr = self.props.window.cairo_create()
- self.cr.set_line_width(max(self.cr.device_to_user_distance(0.5, 0.5)))
+ def do_draw(self, ctx):
+ ctx.set_line_width(max(ctx.device_to_user_distance(0.5, 0.5)))
# Restrict Cairo to the exposed area; avoid extra work
- self.roundcorners_clipping()
+ self.roundcorners_clipping(ctx)
- self.draw_pieces()
- self.draw_progress_overlay()
- self.write_text()
- self.roundcorners_border()
+ self.draw_pieces(ctx)
+ self.draw_progress_overlay(ctx)
+ self.write_text(ctx)
+ self.roundcorners_border(ctx)
# Drawn once, update width, height
if self.resized():
self.prev_width = self.width
self.prev_height = self.height
- def roundcorners_clipping(self):
- self.create_roundcorners_subpath(self.cr, 0, 0, self.width, self.height)
- self.cr.clip()
+ def roundcorners_clipping(self, ctx):
+ self.create_roundcorners_subpath(ctx, 0, 0, self.width, self.height)
+ ctx.clip()
- def roundcorners_border(self):
- self.create_roundcorners_subpath(
- self.cr, 0.5, 0.5, self.width - 1, self.height - 1
- )
- self.cr.set_source_rgba(0, 0, 0, 0.9)
- self.cr.stroke()
+ def roundcorners_border(self, ctx):
+ self.create_roundcorners_subpath(ctx, 0.5, 0.5, self.width - 1, self.height - 1)
+ ctx.set_source_rgba(0, 0, 0, 0.9)
+ ctx.stroke()
@staticmethod
def create_roundcorners_subpath(ctx, x, y, width, height):
@@ -108,11 +101,9 @@ class PiecesBar(DrawingArea):
ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees)
ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees)
ctx.close_path()
- return ctx
- def draw_pieces(self):
+ def draw_pieces(self, ctx):
if not self.num_pieces:
- # Nothing to draw.
return
if (
@@ -124,7 +115,7 @@ class PiecesBar(DrawingArea):
self.pieces_overlay = cairo.ImageSurface(
cairo.FORMAT_ARGB32, self.width, self.height
)
- ctx = cairo.Context(self.pieces_overlay)
+ pieces_ctx = cairo.Context(self.pieces_overlay)
if self.pieces:
pieces = self.pieces
@@ -141,17 +132,16 @@ class PiecesBar(DrawingArea):
for state in COLOR_STATES
]
for state in pieces:
- ctx.set_source_rgb(*pieces_colors[state])
- ctx.rectangle(start_pos, 0, piece_width, self.height)
- ctx.fill()
+ pieces_ctx.set_source_rgb(*pieces_colors[state])
+ pieces_ctx.rectangle(start_pos, 0, piece_width, self.height)
+ pieces_ctx.fill()
start_pos += piece_width
- self.cr.set_source_surface(self.pieces_overlay)
- self.cr.paint()
+ ctx.set_source_surface(self.pieces_overlay)
+ ctx.paint()
- def draw_progress_overlay(self):
+ def draw_progress_overlay(self, ctx):
if not self.text:
- # Nothing useful to draw, return now!
return
if (
@@ -163,16 +153,15 @@ class PiecesBar(DrawingArea):
self.progress_overlay = cairo.ImageSurface(
cairo.FORMAT_ARGB32, self.width, self.height
)
- ctx = cairo.Context(self.progress_overlay)
- ctx.set_source_rgba(0.1, 0.1, 0.1, 0.3) # Transparent
- ctx.rectangle(0, 0, self.width * self.fraction, self.height)
- ctx.fill()
- self.cr.set_source_surface(self.progress_overlay)
- self.cr.paint()
-
- def write_text(self):
+ progress_ctx = cairo.Context(self.progress_overlay)
+ progress_ctx.set_source_rgba(0.1, 0.1, 0.1, 0.3) # Transparent
+ progress_ctx.rectangle(0, 0, self.width * self.fraction, self.height)
+ progress_ctx.fill()
+ ctx.set_source_surface(self.progress_overlay)
+ ctx.paint()
+
+ def write_text(self, ctx):
if not self.text:
- # Nothing useful to draw, return now!
return
if self.resized() or self.text != self.prev_text or self.text_overlay is None:
@@ -180,8 +169,8 @@ class PiecesBar(DrawingArea):
self.text_overlay = cairo.ImageSurface(
cairo.FORMAT_ARGB32, self.width, self.height
)
- ctx = cairo.Context(self.text_overlay)
- pl = PangoCairo.create_layout(ctx)
+ text_ctx = cairo.Context(self.text_overlay)
+ pl = PangoCairo.create_layout(text_ctx)
pl.set_font_description(self.text_font)
pl.set_width(-1) # No text wrapping
pl.set_text(self.text, -1)
@@ -190,12 +179,14 @@ class PiecesBar(DrawingArea):
text_height = plsize[1] // SCALE
area_width_without_text = self.width - text_width
area_height_without_text = self.height - text_height
- ctx.move_to(area_width_without_text // 2, area_height_without_text // 2)
- ctx.set_source_rgb(1, 1, 1)
- PangoCairo.update_layout(ctx, pl)
- PangoCairo.show_layout(ctx, pl)
- self.cr.set_source_surface(self.text_overlay)
- self.cr.paint()
+ text_ctx.move_to(
+ area_width_without_text // 2, area_height_without_text // 2
+ )
+ text_ctx.set_source_rgb(1, 1, 1)
+ PangoCairo.update_layout(text_ctx, pl)
+ PangoCairo.show_layout(text_ctx, pl)
+ ctx.set_source_surface(self.text_overlay)
+ ctx.paint()
def resized(self):
return self.prev_width != self.width or self.prev_height != self.height
@@ -228,7 +219,6 @@ class PiecesBar(DrawingArea):
self.text = self.prev_text = ''
self.fraction = self.prev_fraction = 0
self.progress_overlay = self.text_overlay = self.pieces_overlay = None
- self.cr = None
self.update()
def update(self):
diff --git a/deluge/ui/gtk3/pluginmanager.py b/deluge/ui/gtk3/pluginmanager.py
index d60f8d390..63353c0df 100644
--- a/deluge/ui/gtk3/pluginmanager.py
+++ b/deluge/ui/gtk3/pluginmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
diff --git a/deluge/ui/gtk3/preferences.py b/deluge/ui/gtk3/preferences.py
index 5650ad8df..cd67a5b09 100644
--- a/deluge/ui/gtk3/preferences.py
+++ b/deluge/ui/gtk3/preferences.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
@@ -8,19 +7,19 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
from hashlib import sha1 as sha
+from urllib.parse import urlparse
from gi import require_version
-from gi.repository import Gtk
+from gi.repository import GObject, Gtk
from gi.repository.Gdk import Color
import deluge.common
import deluge.component as component
from deluge.configmanager import ConfigManager, get_config_dir
+from deluge.decorators import maybe_coroutine
from deluge.error import AuthManagerError, NotAuthorizedError
from deluge.i18n import get_languages
from deluge.ui.client import client
@@ -31,14 +30,12 @@ from .dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog
from .path_chooser import PathChooser
try:
- from urllib.parse import urlparse
-except ImportError:
- # PY2 fallback
- from urlparse import urlparse # pylint: disable=ungrouped-imports
-
-try:
- require_version('AppIndicator3', '0.1')
- from gi.repository import AppIndicator3 # noqa: F401
+ try:
+ require_version('AyatanaAppIndicator3', '0.1')
+ from gi.repository import AyatanaAppIndicator3 # noqa: F401
+ except (ValueError, ImportError):
+ require_version('AppIndicator3', '0.1')
+ from gi.repository import AppIndicator3 # noqa: F401
except (ImportError, ValueError):
appindicator = False
else:
@@ -120,7 +117,6 @@ class Preferences(component.Component):
# Setup accounts tab lisview
self.accounts_levels_mapping = None
- self.accounts_authlevel = self.builder.get_object('accounts_authlevel')
self.accounts_liststore = Gtk.ListStore(str, str, str, int)
self.accounts_liststore.set_sort_column_id(
ACCOUNTS_USERNAME, Gtk.SortType.ASCENDING
@@ -175,6 +171,14 @@ class Preferences(component.Component):
# Radio buttons to choose between systray and appindicator
self.builder.get_object('alignment_tray_type').set_visible(appindicator)
+ # Initialize a binding for dark theme
+ Gtk.Settings.get_default().bind_property(
+ 'gtk-application-prefer-dark-theme',
+ self.builder.get_object('chk_prefer_dark_theme'),
+ 'active',
+ GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE,
+ )
+
from .gtkui import DEFAULT_PREFS
self.COLOR_DEFAULTS = {}
@@ -302,7 +306,7 @@ class Preferences(component.Component):
'Bandwidth'"""
self.window_open = True
if page is not None:
- for (index, string, __) in self.liststore:
+ for index, string, __ in self.liststore:
if page == string:
self.treeview.get_selection().select_path(index)
break
@@ -561,6 +565,9 @@ class Preferences(component.Component):
self.builder.get_object('radio_thinclient').set_active(
not self.gtkui_config['standalone']
)
+ self.builder.get_object('chk_prefer_dark_theme').set_active(
+ self.gtkui_config['prefer_dark_theme']
+ )
self.builder.get_object('chk_show_rate_in_title').set_active(
self.gtkui_config['show_rate_in_title']
)
@@ -570,6 +577,9 @@ class Preferences(component.Component):
self.builder.get_object('piecesbar_toggle').set_active(
self.gtkui_config['show_piecesbar']
)
+ self.builder.get_object('urldetect_toggle').set_active(
+ self.gtkui_config['detect_urls']
+ )
self.__set_color('completed', from_config=True)
self.__set_color('downloading', from_config=True)
self.__set_color('waiting', from_config=True)
@@ -676,11 +686,15 @@ class Preferences(component.Component):
'chk_random_outgoing_ports'
).get_active()
incoming_address = self.builder.get_object('entry_interface').get_text().strip()
- if deluge.common.is_ip(incoming_address) or not incoming_address:
+ if deluge.common.is_interface(incoming_address) or not incoming_address:
new_core_config['listen_interface'] = incoming_address
- new_core_config['outgoing_interface'] = (
+ outgoing_address = (
self.builder.get_object('entry_outgoing_interface').get_text().strip()
)
+ if deluge.common.is_interface(outgoing_address) or not outgoing_address:
+ new_core_config['outgoing_interface'] = (
+ self.builder.get_object('entry_outgoing_interface').get_text().strip()
+ )
new_core_config['peer_tos'] = self.builder.get_object(
'entry_peer_tos'
).get_text()
@@ -738,6 +752,9 @@ class Preferences(component.Component):
).get_active()
# Interface tab #
+ new_gtkui_config['prefer_dark_theme'] = self.builder.get_object(
+ 'chk_prefer_dark_theme'
+ ).get_active()
new_gtkui_config['enable_system_tray'] = self.builder.get_object(
'chk_use_tray'
).get_active()
@@ -945,6 +962,7 @@ class Preferences(component.Component):
def hide(self):
self.window_open = False
+ self.builder.get_object('port_spinner').stop()
self.builder.get_object('port_img').hide()
self.pref_dialog.hide()
@@ -957,7 +975,6 @@ class Preferences(component.Component):
'label_cache_num_blocks_written',
'label_cache_read_hit_ratio',
'label_cache_write_hit_ratio',
- 'label_cache_num_blocks_cache_hits',
'label_cache_disk_blocks_in_use',
'label_cache_read_cache_blocks',
)
@@ -1071,6 +1088,10 @@ class Preferences(component.Component):
def on_button_cancel_clicked(self, data):
log.debug('on_button_cancel_clicked')
+ Gtk.Settings.get_default().set_property(
+ 'gtk-application-prefer-dark-theme',
+ self.gtkui_config['prefer_dark_theme'],
+ )
self.hide()
return True
@@ -1090,6 +1111,8 @@ class Preferences(component.Component):
log.debug('on_test_port_clicked')
def on_get_test(status):
+ self.builder.get_object('port_spinner').stop()
+ self.builder.get_object('port_spinner').hide()
if status:
self.builder.get_object('port_img').set_from_icon_name(
'emblem-ok-symbolic', Gtk.IconSize.MENU
@@ -1102,12 +1125,9 @@ class Preferences(component.Component):
self.builder.get_object('port_img').show()
client.core.test_listen_port().addCallback(on_get_test)
- # XXX: Consider using gtk.Spinner() instead of the loading gif
- # It requires gtk.ver > 2.12
- self.builder.get_object('port_img').set_from_file(
- deluge.common.get_pixmap('loading.gif')
- )
- self.builder.get_object('port_img').show()
+ self.builder.get_object('port_spinner').start()
+ self.builder.get_object('port_spinner').show()
+ self.builder.get_object('port_img').hide()
client.force_call()
def on_plugin_toggled(self, renderer, path):
@@ -1331,58 +1351,46 @@ class Preferences(component.Component):
(model, itr) = treeselection.get_selected()
if not itr:
return
- username = model[itr][0]
- if username:
+ level = model[itr][1]
+ if level:
self.builder.get_object('accounts_edit').set_sensitive(True)
self.builder.get_object('accounts_delete').set_sensitive(True)
else:
self.builder.get_object('accounts_edit').set_sensitive(False)
self.builder.get_object('accounts_delete').set_sensitive(False)
- def on_accounts_add_clicked(self, widget):
+ @maybe_coroutine
+ async def on_accounts_add_clicked(self, widget):
dialog = AccountDialog(
levels_mapping=client.auth_levels_mapping, parent=self.pref_dialog
)
+ response = await dialog.run()
+ if response != Gtk.ResponseType.OK:
+ return
- def dialog_finished(response_id):
- username = dialog.get_username()
- password = dialog.get_password()
- authlevel = dialog.get_authlevel()
-
- def add_ok(rv):
- accounts_iter = self.accounts_liststore.append()
- self.accounts_liststore.set_value(
- accounts_iter, ACCOUNTS_USERNAME, username
- )
- self.accounts_liststore.set_value(
- accounts_iter, ACCOUNTS_LEVEL, authlevel
- )
- self.accounts_liststore.set_value(
- accounts_iter, ACCOUNTS_PASSWORD, password
- )
-
- def add_fail(failure):
- if failure.type == AuthManagerError:
- ErrorDialog(
- _('Error Adding Account'),
- _('Authentication failed'),
- parent=self.pref_dialog,
- details=failure.getErrorMessage(),
- ).run()
- else:
- ErrorDialog(
- _('Error Adding Account'),
- _('An error occurred while adding account'),
- parent=self.pref_dialog,
- details=failure.getErrorMessage(),
- ).run()
-
- if response_id == Gtk.ResponseType.OK:
- client.core.create_account(username, password, authlevel).addCallback(
- add_ok
- ).addErrback(add_fail)
-
- dialog.run().addCallback(dialog_finished)
+ account = dialog.account
+ try:
+ await client.core.create_account(*account)
+ except AuthManagerError as ex:
+ return ErrorDialog(
+ _('Error Adding Account'),
+ _('Authentication failed'),
+ parent=self.pref_dialog,
+ details=ex,
+ ).run()
+ except Exception as ex:
+ return ErrorDialog(
+ _('Error Adding Account'),
+ _(f'An error occurred while adding account: {account}'),
+ parent=self.pref_dialog,
+ details=ex,
+ ).run()
+
+ self.accounts_liststore.set(
+ self.accounts_liststore.append(),
+ [ACCOUNTS_USERNAME, ACCOUNTS_LEVEL, ACCOUNTS_PASSWORD],
+ [account.username, account.authlevel, account.password],
+ )
def on_accounts_edit_clicked(self, widget):
(model, itr) = self.accounts_listview.get_selection().get_selected()
@@ -1462,6 +1470,9 @@ class Preferences(component.Component):
colors_widget = self.builder.get_object('piecebar_colors_expander')
colors_widget.set_visible(widget.get_active())
+ def on_urldetect_toggle_toggled(self, widget):
+ self.gtkui_config['detect_urls'] = widget.get_active()
+
def on_checkbutton_language_toggled(self, widget):
self.language_combo.set_visible(not self.language_checkbox.get_active())
diff --git a/deluge/ui/gtk3/queuedtorrents.py b/deluge/ui/gtk3/queuedtorrents.py
index 0f08c24c6..6fdecec76 100644
--- a/deluge/ui/gtk3/queuedtorrents.py
+++ b/deluge/ui/gtk3/queuedtorrents.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os.path
diff --git a/deluge/ui/gtk3/removetorrentdialog.py b/deluge/ui/gtk3/removetorrentdialog.py
index 48806a5d5..06fca7704 100644
--- a/deluge/ui/gtk3/removetorrentdialog.py
+++ b/deluge/ui/gtk3/removetorrentdialog.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
@@ -21,7 +18,7 @@ from deluge.ui.client import client
log = logging.getLogger(__name__)
-class RemoveTorrentDialog(object):
+class RemoveTorrentDialog:
"""
This class is used to create and show a Remove Torrent Dialog.
diff --git a/deluge/ui/gtk3/sidebar.py b/deluge/ui/gtk3/sidebar.py
index 1d751918f..5a2b15466 100644
--- a/deluge/ui/gtk3/sidebar.py
+++ b/deluge/ui/gtk3/sidebar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
@@ -8,8 +7,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from gi.repository.Gtk import Label, PolicyType, ScrolledWindow
diff --git a/deluge/ui/gtk3/status_tab.py b/deluge/ui/gtk3/status_tab.py
index fab671940..6a9010b6f 100644
--- a/deluge/ui/gtk3/status_tab.py
+++ b/deluge/ui/gtk3/status_tab.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
import deluge.component as component
@@ -32,7 +29,7 @@ log = logging.getLogger(__name__)
class StatusTab(Tab):
def __init__(self):
- super(StatusTab, self).__init__('Status', 'status_tab', 'status_tab_label')
+ super().__init__('Status', 'status_tab', 'status_tab_label')
self.config = ConfigManager('gtk3ui.conf')
@@ -110,7 +107,7 @@ class StatusTab(Tab):
if decode_bytes(widget[0].get_text()) != txt:
widget[0].set_text(txt)
- # Update progress bar seperately as it's a special case (not a label).
+ # Update progress bar separately as it's a special case (not a label).
fraction = status['progress'] / 100
if self.config['show_piecesbar']:
diff --git a/deluge/ui/gtk3/statusbar.py b/deluge/ui/gtk3/statusbar.py
index 18db753fa..0a2e80095 100644
--- a/deluge/ui/gtk3/statusbar.py
+++ b/deluge/ui/gtk3/statusbar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
import logging
from gi.repository import Gtk
@@ -25,7 +22,7 @@ from .dialogs import OtherDialog
log = logging.getLogger(__name__)
-class StatusBarItem(object):
+class StatusBarItem:
def __init__(
self,
image=None,
@@ -320,18 +317,22 @@ class StatusBar(component.Component):
def send_status_request(self):
# Sends an async request for data from the core
keys = [
- 'num_peers',
+ 'peer.num_peers_connected',
'upload_rate',
'download_rate',
'payload_upload_rate',
'payload_download_rate',
+ 'net.sent_bytes',
+ 'net.recv_bytes',
+ 'net.sent_payload_bytes',
+ 'net.recv_payload_bytes',
]
if self.dht_status:
- keys.append('dht_nodes')
+ keys.append('dht.dht_nodes')
if not self.health:
- keys.append('has_incoming_connections')
+ keys.append('net.has_incoming_connections')
client.core.get_session_status(keys).addCallback(self._on_get_session_status)
client.core.get_free_space().addCallback(self._on_get_free_space)
@@ -370,18 +371,18 @@ class StatusBar(component.Component):
self.upload_protocol_rate = (
status['upload_rate'] - status['payload_upload_rate']
) // 1024
- self.num_connections = status['num_peers']
+ self.num_connections = status['peer.num_peers_connected']
self.update_download_label()
self.update_upload_label()
self.update_traffic_label()
self.update_connections_label()
- if 'dht_nodes' in status:
- self.dht_nodes = status['dht_nodes']
+ if 'dht.dht_nodes' in status:
+ self.dht_nodes = status['dht.dht_nodes']
self.update_dht_label()
- if 'has_incoming_connections' in status:
- self.health = status['has_incoming_connections']
+ if 'net.has_incoming_connections' in status:
+ self.health = status['net.has_incoming_connections']
if self.health:
self.remove_item(self.health_item)
@@ -412,7 +413,7 @@ class StatusBar(component.Component):
if self.max_connections_global < 0:
label_string = '%s' % self.num_connections
else:
- label_string = '%s <small>(%s)</small>' % (
+ label_string = '{} <small>({})</small>'.format(
self.num_connections,
self.max_connections_global,
)
diff --git a/deluge/ui/gtk3/systemtray.py b/deluge/ui/gtk3/systemtray.py
index f851f3275..5318cf2d5 100644
--- a/deluge/ui/gtk3/systemtray.py
+++ b/deluge/ui/gtk3/systemtray.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
@@ -30,8 +27,12 @@ from .common import build_menu_radio_list, get_logo
from .dialogs import OtherDialog
try:
- require_version('AppIndicator3', '0.1')
- from gi.repository import AppIndicator3
+ try:
+ require_version('AyatanaAppIndicator3', '0.1')
+ from gi.repository import AyatanaAppIndicator3 as AppIndicator3
+ except (ValueError, ImportError):
+ require_version('AppIndicator3', '0.1')
+ from gi.repository import AppIndicator3
except (ValueError, ImportError):
AppIndicator3 = None
@@ -143,7 +144,6 @@ class SystemTray(component.Component):
def __start(self):
if self.config['enable_system_tray']:
-
if self.config['standalone']:
try:
self.hide_widget_list.remove('menuitem_quitdaemon')
@@ -234,13 +234,13 @@ class SystemTray(component.Component):
if max_download_speed == -1:
max_download_speed = _('Unlimited')
else:
- max_download_speed = '%s %s' % (max_download_speed, _('K/s'))
+ max_download_speed = '{} {}'.format(max_download_speed, _('K/s'))
if max_upload_speed == -1:
max_upload_speed = _('Unlimited')
else:
- max_upload_speed = '%s %s' % (max_upload_speed, _('K/s'))
+ max_upload_speed = '{} {}'.format(max_upload_speed, _('K/s'))
- msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (
+ msg = '{}\n{}: {} ({})\n{}: {} ({})'.format(
_('Deluge'),
_('Down'),
self.download_rate,
diff --git a/deluge/ui/gtk3/tab_data_funcs.py b/deluge/ui/gtk3/tab_data_funcs.py
index 6fa0ba59c..a78994f69 100644
--- a/deluge/ui/gtk3/tab_data_funcs.py
+++ b/deluge/ui/gtk3/tab_data_funcs.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,14 +6,12 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
from deluge.common import fdate, fsize, fspeed, ftime
from deluge.ui.common import TRACKER_STATUS_TRANSLATION
def ftotal_sized(first, second):
- return '%s (%s)' % (fsize(first, shortform=True), fsize(second, shortform=True))
+ return f'{fsize(first, shortform=True)} ({fsize(second, shortform=True)})'
def fratio(value):
@@ -24,7 +21,7 @@ def fratio(value):
def fpcnt(value, state, message):
state_i18n = _(state)
if state not in ('Error', 'Seeding') and value < 100:
- percent = '{:.2f}'.format(value).rstrip('0').rstrip('.')
+ percent = f'{value:.2f}'.rstrip('0').rstrip('.')
return _('{state} {percent}%').format(state=state_i18n, percent=percent)
elif state == 'Error':
return _('{state}: {err_msg}').format(state=state_i18n, err_msg=message)
@@ -34,7 +31,7 @@ def fpcnt(value, state, message):
def fspeed_max(value, max_value=-1):
value = fspeed(value, shortform=True)
- return '%s (%s %s)' % (value, max_value, _('K/s')) if max_value > -1 else value
+ return '{} ({} {})'.format(value, max_value, _('K/s')) if max_value > -1 else value
def fdate_or_never(value):
@@ -73,7 +70,7 @@ def fseed_rank_or_dash(seed_rank, seeding_time):
def fpieces_num_size(num_pieces, piece_size):
- return '%s (%s)' % (num_pieces, fsize(piece_size, precision=0))
+ return f'{num_pieces} ({fsize(piece_size, precision=0)})'
def fcount(value):
diff --git a/deluge/ui/gtk3/toolbar.py b/deluge/ui/gtk3/toolbar.py
index 7bc029e4b..1b6952e74 100644
--- a/deluge/ui/gtk3/toolbar.py
+++ b/deluge/ui/gtk3/toolbar.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from gi.repository.Gtk import SeparatorToolItem, ToolButton
diff --git a/deluge/ui/gtk3/torrentdetails.py b/deluge/ui/gtk3/torrentdetails.py
index a586c2997..08c37a1de 100644
--- a/deluge/ui/gtk3/torrentdetails.py
+++ b/deluge/ui/gtk3/torrentdetails.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -9,8 +8,6 @@
"""The torrent details component shows info about the selected torrent."""
-from __future__ import unicode_literals
-
import logging
from collections import namedtuple
@@ -33,7 +30,7 @@ log = logging.getLogger(__name__)
TabWidget = namedtuple('TabWidget', ('obj', 'func', 'status_keys'))
-class Tab(object):
+class Tab:
def __init__(self, name=None, child_widget=None, tab_label=None):
self._name = name
self.is_visible = True
diff --git a/deluge/ui/gtk3/torrentview.py b/deluge/ui/gtk3/torrentview.py
index fcc6edfa8..16de16ea7 100644
--- a/deluge/ui/gtk3/torrentview.py
+++ b/deluge/ui/gtk3/torrentview.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -8,8 +7,6 @@
#
"""The torrent view component that lists all torrents in the session."""
-from __future__ import unicode_literals
-
import logging
from locale import strcoll
@@ -77,13 +74,13 @@ def eta_column_sort(model, iter1, iter2, data):
if v1 == v2:
return 0
if v1 == 0:
- return 1
- if v2 == 0:
return -1
- if v1 > v2:
+ if v2 == 0:
return 1
- if v2 > v1:
+ if v1 > v2:
return -1
+ if v2 > v1:
+ return 1
def seed_peer_column_sort(model, iter1, iter2, data):
@@ -107,7 +104,7 @@ def progress_sort(model, iter1, iter2, sort_column_id):
return cmp(progress1, progress2)
-class SearchBox(object):
+class SearchBox:
def __init__(self, torrentview):
self.torrentview = torrentview
mainwindow = component.get('MainWindow')
@@ -404,6 +401,13 @@ class TorrentView(ListView, component.Component):
status_field=['last_seen_complete'],
default=False,
)
+ self.add_func_column(
+ _('Last Transfer'),
+ funcs.cell_data_time,
+ [int],
+ status_field=['time_since_transfer'],
+ default=False,
+ )
self.add_texticon_column(
_('Tracker'),
function=funcs.cell_data_trackericon,
diff --git a/deluge/ui/gtk3/torrentview_data_funcs.py b/deluge/ui/gtk3/torrentview_data_funcs.py
index 8bd1f9c51..0b2545d8c 100644
--- a/deluge/ui/gtk3/torrentview_data_funcs.py
+++ b/deluge/ui/gtk3/torrentview_data_funcs.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import warnings
from functools import partial
@@ -17,7 +14,7 @@ import deluge.component as component
from .common import (
create_blank_pixbuf,
- get_pixbuf_at_size,
+ get_pixbuf,
icon_alert,
icon_checking,
icon_downloading,
@@ -42,7 +39,6 @@ ICON_STATE = {
# renderer. This is much cheaper than fetch the current value and test if
# it's equal.
func_last_value = {
- 'cell_data_time': None,
'cell_data_ratio_seeds_peers': None,
'cell_data_ratio_ratio': None,
'cell_data_ratio_avail': None,
@@ -86,7 +82,7 @@ def set_tracker_icon(tracker_icon, cell):
if tracker_icon:
pixbuf = tracker_icon.get_cached_icon()
if pixbuf is None:
- pixbuf = get_pixbuf_at_size(tracker_icon.get_filename(), 16)
+ pixbuf = get_pixbuf(tracker_icon.get_filename(), 16)
tracker_icon.set_cached_icon(pixbuf)
else:
pixbuf = create_blank_pixbuf()
@@ -162,7 +158,7 @@ def cell_data_speed(cell, model, row, data):
if speed > 0:
speed_str = common.fspeed(speed, shortform=True)
cell.set_property(
- 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split()))
+ 'markup', '{} <small>{}</small>'.format(*tuple(speed_str.split()))
)
else:
cell.set_property('text', '')
@@ -189,7 +185,7 @@ def cell_data_speed_limit(cell, model, row, data, cache_key):
if speed > 0:
speed_str = common.fspeed(speed * 1024, shortform=True)
cell.set_property(
- 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split()))
+ 'markup', '{} <small>{}</small>'.format(*tuple(speed_str.split()))
)
else:
cell.set_property('text', '')
@@ -222,10 +218,6 @@ def cell_data_peer(column, cell, model, row, data):
def cell_data_time(column, cell, model, row, data):
"""Display value as time, eg 1m10s"""
time = model.get_value(row, data)
- if func_last_value['cell_data_time'] == time:
- return
- func_last_value['cell_data_time'] = time
-
if time <= 0:
time_str = ''
else:
diff --git a/deluge/ui/gtk3/trackers_tab.py b/deluge/ui/gtk3/trackers_tab.py
index d83b9956c..d671471b0 100644
--- a/deluge/ui/gtk3/trackers_tab.py
+++ b/deluge/ui/gtk3/trackers_tab.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import deluge.component as component
@@ -22,9 +19,7 @@ log = logging.getLogger(__name__)
class TrackersTab(Tab):
def __init__(self):
- super(TrackersTab, self).__init__(
- 'Trackers', 'trackers_tab', 'trackers_tab_label'
- )
+ super().__init__('Trackers', 'trackers_tab', 'trackers_tab_label')
self.add_tab_widget('summary_next_announce', ftime, ('next_announce',))
self.add_tab_widget('summary_tracker', None, ('tracker_host',))
diff --git a/deluge/ui/hostlist.py b/deluge/ui/hostlist.py
index 09130192c..0dba1269e 100644
--- a/deluge/ui/hostlist.py
+++ b/deluge/ui/hostlist.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) Calum Lind 2017 <calumlind+deluge@gmail.com>
#
@@ -7,12 +6,10 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
import uuid
-from socket import gaierror, gethostbyname
+from socket import gaierror, getaddrinfo
from twisted.internet import defer
@@ -25,7 +22,7 @@ log = logging.getLogger(__name__)
DEFAULT_HOST = '127.0.0.1'
DEFAULT_PORT = 58846
-LOCALHOST = ('127.0.0.1', 'localhost')
+LOCALHOST = ('127.0.0.1', 'localhost', '::1')
def default_hostlist():
@@ -47,12 +44,14 @@ def validate_host_info(hostname, port):
"""
try:
- gethostbyname(hostname)
+ getaddrinfo(hostname, None)
except gaierror as ex:
raise ValueError('Host %s: %s', hostname, ex.args[1])
if not isinstance(port, int):
raise ValueError('Invalid port. Must be an integer')
+ if not 0 <= port <= 65535:
+ raise ValueError('Invalid port. Must be between 0-65535')
def migrate_hostlist(old_filename, new_filename):
@@ -87,7 +86,15 @@ def migrate_config_2_to_3(config):
return config
-class HostList(object):
+def mask_hosts_password(hosts):
+ """Replace passwords in hosts list with *'s for log output"""
+ if not hosts:
+ return hosts
+
+ return [list(host)[:-1] + ['*' * 10] for host in hosts]
+
+
+class HostList:
"""This class contains methods for adding, removing and looking up hosts in hostlist.conf."""
def __init__(self):
@@ -97,12 +104,13 @@ class HostList(object):
default_hostlist(),
config_dir=get_config_dir(),
file_version=3,
+ log_mask_funcs={'hosts': mask_hosts_password},
)
self.config.run_converter((1, 2), 3, migrate_config_2_to_3)
self.config.save()
def check_info_exists(self, hostname, port, username, skip_host_id=None):
- """Check for exising host entries with the same details.
+ """Check for existing host entries with the same details.
Args:
hostname (str): The IP or hostname of the deluge daemon.
@@ -210,30 +218,35 @@ class HostList(object):
return defer.succeed(status_offline)
try:
- ip = gethostbyname(host)
- except gaierror as ex:
- log.error('Error resolving host %s to ip: %s', host, ex.args[1])
+ ips = list({addrinfo[4][0] for addrinfo in getaddrinfo(host, None)})
+ except (gaierror, IndexError) as ex:
+ log.warning('Unable to resolve host %s to IP: %s', host, ex.args[1])
return defer.succeed(status_offline)
- host_conn_info = (
- ip,
- port,
- 'localclient' if not user and host in LOCALHOST else user,
- )
- if client.connected() and host_conn_info == client.connection_info():
- # Currently connected to host_id daemon.
- def on_info(info, host_id):
- log.debug('Client connected, query info: %s', info)
- return host_id, 'Connected', info
-
- return client.daemon.info().addCallback(on_info, host_id)
- else:
- # Attempt to connect to daemon with host_id details.
- c = Client()
- d = c.connect(host, port, skip_authentication=True)
- d.addCallback(on_connect, c, host_id)
- d.addErrback(on_connect_failed, host_id)
- return d
+ host_conn_list = [
+ (
+ host_ip,
+ port,
+ 'localclient' if not user and host_ip in LOCALHOST else user,
+ )
+ for host_ip in ips
+ ]
+
+ for host_conn_info in host_conn_list:
+ if client.connected() and host_conn_info == client.connection_info():
+ # Currently connected to host_id daemon.
+ def on_info(info, host_id):
+ log.debug('Client connected, query info: %s', info)
+ return host_id, 'Connected', info
+
+ return client.daemon.info().addCallback(on_info, host_id)
+ else:
+ # Attempt to connect to daemon with host_id details.
+ c = Client()
+ d = c.connect(host, port, skip_authentication=True)
+ d.addCallback(on_connect, c, host_id)
+ d.addErrback(on_connect_failed, host_id)
+ return d
def update_host(self, host_id, hostname, port, username, password):
"""Update the supplied host id with new connection details.
diff --git a/deluge/ui/sessionproxy.py b/deluge/ui/sessionproxy.py
index 5af8e79cd..6cb8550e6 100644
--- a/deluge/ui/sessionproxy.py
+++ b/deluge/ui/sessionproxy.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 Andrew Resch <andrewresch@gmail.com>
#
@@ -6,8 +5,6 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
from time import time
@@ -148,11 +145,17 @@ class SessionProxy(component.Component):
def on_status(result, torrent_id):
t = time()
- self.torrents[torrent_id][0] = t
- self.torrents[torrent_id][1].update(result)
- for key in keys_to_get:
- self.cache_times[torrent_id][key] = t
- return self.create_status_dict([torrent_id], keys)[torrent_id]
+ try:
+ self.torrents[torrent_id][0] = t
+ self.torrents[torrent_id][1].update(result)
+ for key in keys_to_get:
+ self.cache_times[torrent_id][key] = t
+ return self.create_status_dict([torrent_id], keys)[torrent_id]
+ except KeyError:
+ log.debug(
+ f'Status missing for torrent (removed?): {torrent_id}'
+ )
+ return {}
return d.addCallback(on_status, torrent_id)
else:
@@ -187,6 +190,7 @@ class SessionProxy(component.Component):
:rtype: dict
"""
+
# Helper functions and callbacks ---------------------------------------
def on_status(result, torrent_ids, keys):
# Update the internal torrent status dict with the update values
diff --git a/deluge/ui/tracker_icons.py b/deluge/ui/tracker_icons.py
index c10cd2f8e..5f619af63 100644
--- a/deluge/ui/tracker_icons.py
+++ b/deluge/ui/tracker_icons.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -7,14 +6,14 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
-from tempfile import mkstemp
+import tempfile
+from html.parser import HTMLParser
+from urllib.parse import urljoin, urlparse
from twisted.internet import defer, threads
-from twisted.web.error import PageRedirect
+from twisted.python.failure import Failure
from twisted.web.resource import ForbiddenResource, NoResource
from deluge.component import Component
@@ -23,12 +22,9 @@ from deluge.decorators import proxy
from deluge.httpdownloader import download_file
try:
- from html.parser import HTMLParser
- from urllib.parse import urljoin, urlparse
+ import chardet
except ImportError:
- # PY2 fallback
- from HTMLParser import HTMLParser
- from urlparse import urljoin, urlparse # pylint: disable=ungrouped-imports
+ chardet = None
try:
from PIL import Image
@@ -38,7 +34,7 @@ except ImportError:
log = logging.getLogger(__name__)
-class TrackerIcon(object):
+class TrackerIcon:
"""
Represents a tracker's icon
"""
@@ -207,17 +203,19 @@ class TrackerIcons(Component):
else:
# We need to fetch it
self.pending[host] = []
+ tmp_file = tempfile.mkstemp(prefix='deluge_trackericon_html.')
+ filename = tmp_file[1]
# Start callback chain
- d = self.download_page(host)
+ d = self.download_page(host, filename)
d.addCallbacks(
self.on_download_page_complete,
self.on_download_page_fail,
- errbackArgs=(host,),
)
d.addCallback(self.parse_html_page)
d.addCallbacks(
self.on_parse_complete, self.on_parse_fail, callbackArgs=(host,)
)
+ d.addBoth(self.del_tmp_file, tmp_file)
d.addCallback(self.download_icon, host)
d.addCallbacks(
self.on_download_icon_complete,
@@ -229,24 +227,38 @@ class TrackerIcons(Component):
d.addCallback(self.store_icon, host)
return d
- def download_page(self, host, url=None):
- """
- Downloads a tracker host's page
+ @staticmethod
+ def del_tmp_file(result, tmp_file):
+ """Remove tmp_file created when downloading tracker page"""
+ fd, filename = tmp_file
+ try:
+ os.close(fd)
+ os.remove(filename)
+ except OSError:
+ log.debug(f'Unable to delete temporary file: {filename}')
+
+ return result
+
+ def download_page(
+ self, host: str, filename: str, url: str = None
+ ) -> 'defer.Deferred[str]':
+ """Downloads a tracker host's page
+
If no url is provided, it bases the url on the host
- :param host: the tracker host
- :type host: string
- :param url: the (optional) url of the host
- :type url: string
- :returns: the filename of the tracker host's page
- :rtype: Deferred
+ Args:
+ host: The tracker host
+ filename: Location to download page
+ url: The url of the host
+
+ Returns:
+ The filename of the tracker host's page
"""
if not url:
url = self.host_to_url(host)
- log.debug('Downloading %s %s', host, url)
- tmp_fd, tmp_file = mkstemp(prefix='deluge_ticon.')
- os.close(tmp_fd)
- return download_file(url, tmp_file, force_filename=True, handle_redirects=False)
+
+ log.debug(f'Downloading {host} {url} to {filename}')
+ return download_file(url, filename, force_filename=True)
def on_download_page_complete(self, page):
"""
@@ -260,33 +272,18 @@ class TrackerIcons(Component):
log.debug('Finished downloading %s', page)
return page
- def on_download_page_fail(self, f, host):
- """
- Recovers from download error
+ def on_download_page_fail(self, failure: 'Failure') -> 'Failure':
+ """Runs any download failure clean-up functions
- :param f: the failure that occurred
- :type f: Failure
- :param host: the name of the host whose page failed to download
- :type host: string
- :returns: a Deferred if recovery was possible
- else the original failure
- :rtype: Deferred or Failure
- """
- error_msg = f.getErrorMessage()
- log.debug('Error downloading page: %s', error_msg)
- d = f
- if f.check(PageRedirect):
- # Handle redirect errors
- location = urljoin(self.host_to_url(host), error_msg.split(' to ')[1])
- self.redirects[host] = url_to_host(location)
- d = self.download_page(host, url=location)
- d.addCallbacks(
- self.on_download_page_complete,
- self.on_download_page_fail,
- errbackArgs=(host,),
- )
+ Args:
+ failure: The failure that occurred.
- return d
+ Returns:
+ The original failure.
+
+ """
+ log.debug(f'Error downloading page: {failure.getErrorMessage()}')
+ return failure
@proxy(threads.deferToThread)
def parse_html_page(self, page):
@@ -298,17 +295,19 @@ class TrackerIcons(Component):
:returns: a Deferred which callbacks a list of available favicons (url, type)
:rtype: Deferred
"""
- with open(page, 'r') as _file:
+ encoding = 'UTF-8'
+ if chardet:
+ with open(page, 'rb') as _file:
+ result = chardet.detect(_file.read())
+ encoding = result['encoding']
+
+ with open(page, encoding=encoding) as _file:
parser = FaviconParser()
for line in _file:
parser.feed(line)
if parser.left_head:
break
parser.close()
- try:
- os.remove(page)
- except OSError as ex:
- log.warning('Could not remove temp file: %s', ex)
return parser.get_icons()
@@ -382,7 +381,7 @@ class TrackerIcons(Component):
try:
with Image.open(icon_name):
pass
- except IOError as ex:
+ except OSError as ex:
raise InvalidIconError(ex)
else:
if not os.path.getsize(icon_name):
@@ -423,22 +422,7 @@ class TrackerIcons(Component):
error_msg = f.getErrorMessage()
log.debug('Error downloading icon from %s: %s', host, error_msg)
d = f
- if f.check(PageRedirect):
- # Handle redirect errors
- location = urljoin(self.host_to_url(host), error_msg.split(' to ')[1])
- d = self.download_icon(
- [(location, extension_to_mimetype(location.rpartition('.')[2]))]
- + icons,
- host,
- )
- if not icons:
- d.addCallbacks(
- self.on_download_icon_complete,
- self.on_download_icon_fail,
- callbackArgs=(host,),
- errbackArgs=(host,),
- )
- elif f.check(NoResource, ForbiddenResource) and icons:
+ if f.check(NoResource, ForbiddenResource) and icons:
d = self.download_icon(icons, host)
elif f.check(NoIconsError):
# No icons, try favicon.ico as an act of desperation
@@ -477,14 +461,17 @@ class TrackerIcons(Component):
# Requires Pillow(PIL) to resize.
if icon and Image:
filename = icon.get_filename()
+ remove_old = False
with Image.open(filename) as img:
if img.size > (16, 16):
new_filename = filename.rpartition('.')[0] + '.png'
img = img.resize((16, 16), Image.ANTIALIAS)
img.save(new_filename)
if new_filename != filename:
- os.remove(filename)
- icon = TrackerIcon(new_filename)
+ remove_old = True
+ if remove_old:
+ os.remove(filename)
+ icon = TrackerIcon(new_filename)
return icon
def store_icon(self, icon, host):
@@ -617,11 +604,13 @@ MIME_MAP = {
'image/png': 'png',
'image/vnd.microsoft.icon': 'ico',
'image/x-icon': 'ico',
+ 'image/svg+xml': 'svg',
'gif': 'image/gif',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'ico': 'image/vnd.microsoft.icon',
+ 'svg': 'image/svg+xml',
}
diff --git a/deluge/ui/ui.py b/deluge/ui/ui.py
index 0986ec777..338f8a8e0 100644
--- a/deluge/ui/ui.py
+++ b/deluge/ui/ui.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
@@ -7,9 +6,8 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
+import sys
import deluge.common
import deluge.configmanager
@@ -27,7 +25,7 @@ except ImportError:
return
-class UI(object):
+class UI:
"""
Base class for UI implementations.
@@ -60,7 +58,7 @@ class UI(object):
return self.__options
def start(self, parser=None):
- args = deluge.common.unicode_argv()[1:]
+ args = sys.argv[1:]
if parser is None:
parser = self.parser
self.__options = self.parse_args(parser, args)
diff --git a/deluge/ui/ui_entry.py b/deluge/ui/ui_entry.py
index 71ce83783..e185fda33 100644
--- a/deluge/ui/ui_entry.py
+++ b/deluge/ui/ui_entry.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2010 Pedro Algarvio <pedro@algarvio.me>
@@ -12,8 +11,6 @@
# user runs the command 'deluge'.
"""Main starting point for Deluge"""
-from __future__ import unicode_literals
-
import argparse
import logging
import os
@@ -100,7 +97,7 @@ def start_ui():
# If the UI is set as default, indicate this in help by prefixing with a star.
subactions = subparsers._get_subactions()
prefix = '*' if ui == default_ui else ' '
- subactions[-1].metavar = '%s %s' % (prefix, ui)
+ subactions[-1].metavar = f'{prefix} {ui}'
# Insert a default UI subcommand unless one of the ambiguous_args are specified
parser.set_default_subparser(default_ui, abort_opts=AMBIGUOUS_CMD_ARGS)
@@ -115,7 +112,7 @@ def start_ui():
try:
ui = ui_entrypoints[selected_ui](
- prog='%s %s' % (os.path.basename(sys.argv[0]), selected_ui), ui_args=ui_args
+ prog=f'{os.path.basename(sys.argv[0])} {selected_ui}', ui_args=ui_args
)
except KeyError:
log.error(
diff --git a/deluge/ui/web/__init__.py b/deluge/ui/web/__init__.py
index 0be7eedb9..3757e0b1c 100644
--- a/deluge/ui/web/__init__.py
+++ b/deluge/ui/web/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from deluge.ui.web.web import Web
diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py
index fa9504954..eacbbf526 100644
--- a/deluge/ui/web/auth.py
+++ b/deluge/ui/web/auth.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import hashlib
import logging
import os
@@ -60,11 +57,11 @@ def make_expires(timeout):
class Auth(JSONComponent):
"""
- The component that implements authentification into the JSON interface.
+ The component that implements authentication into the JSON interface.
"""
def __init__(self, config):
- super(Auth, self).__init__('Auth')
+ super().__init__('Auth')
self.worker = LoopingCall(self._clean_sessions)
self.config = config
diff --git a/deluge/ui/web/common.py b/deluge/ui/web/common.py
index 475f33565..32c29c8c0 100644
--- a/deluge/ui/web/common.py
+++ b/deluge/ui/web/common.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,19 +6,15 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import gettext
from mako.template import Template as MakoTemplate
-from deluge.common import PY2, get_version
+from deluge.common import get_version
def _(text):
text_local = gettext.gettext(text)
- if PY2:
- return text_local.decode('utf-8')
return text_local
diff --git a/deluge/ui/web/css/deluge.css b/deluge/ui/web/css/deluge.css
index c026b6d8e..946028639 100644
--- a/deluge/ui/web/css/deluge.css
+++ b/deluge/ui/web/css/deluge.css
@@ -6,6 +6,8 @@ body {
border: 0 none;
overflow: hidden;
height: 100%;
+ color: black;
+ background: white;
}
input {
diff --git a/deluge/ui/web/docs/template/resources/deluge.png b/deluge/ui/web/docs/template/resources/deluge.png
index e39cd0c7e..761f0601c 100644
--- a/deluge/ui/web/docs/template/resources/deluge.png
+++ b/deluge/ui/web/docs/template/resources/deluge.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/form.png b/deluge/ui/web/docs/template/resources/form.png
index f0945cf7f..ef32f38e7 100644
--- a/deluge/ui/web/docs/template/resources/form.png
+++ b/deluge/ui/web/docs/template/resources/form.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-lock.png b/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-lock.png
index 8b81e7ff2..883059742 100644
--- a/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-lock.png
+++ b/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-lock.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-unlock.png b/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-unlock.png
index 9dd5df34b..4ec318fd2 100644
--- a/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-unlock.png
+++ b/deluge/ui/web/docs/template/resources/images/default/grid/hmenu-unlock.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/panel/top-bottom.png b/deluge/ui/web/docs/template/resources/images/default/panel/top-bottom.png
index 578ffb609..27299d106 100644
--- a/deluge/ui/web/docs/template/resources/images/default/panel/top-bottom.png
+++ b/deluge/ui/web/docs/template/resources/images/default/panel/top-bottom.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/shadow-c.png b/deluge/ui/web/docs/template/resources/images/default/shadow-c.png
index d435f80ae..f66116ad3 100644
--- a/deluge/ui/web/docs/template/resources/images/default/shadow-c.png
+++ b/deluge/ui/web/docs/template/resources/images/default/shadow-c.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/shadow-lr.png b/deluge/ui/web/docs/template/resources/images/default/shadow-lr.png
index bb88b6f2b..2ed5f7a80 100644
--- a/deluge/ui/web/docs/template/resources/images/default/shadow-lr.png
+++ b/deluge/ui/web/docs/template/resources/images/default/shadow-lr.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/shadow.png b/deluge/ui/web/docs/template/resources/images/default/shadow.png
index 75c0eba3e..eddcd0b11 100644
--- a/deluge/ui/web/docs/template/resources/images/default/shadow.png
+++ b/deluge/ui/web/docs/template/resources/images/default/shadow.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/slider/slider-bg.png b/deluge/ui/web/docs/template/resources/images/default/slider/slider-bg.png
index 999919424..72c7d9ac9 100644
--- a/deluge/ui/web/docs/template/resources/images/default/slider/slider-bg.png
+++ b/deluge/ui/web/docs/template/resources/images/default/slider/slider-bg.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/slider/slider-thumb.png b/deluge/ui/web/docs/template/resources/images/default/slider/slider-thumb.png
index cd654a4c1..04792c55f 100644
--- a/deluge/ui/web/docs/template/resources/images/default/slider/slider-thumb.png
+++ b/deluge/ui/web/docs/template/resources/images/default/slider/slider-thumb.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-bg.png b/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-bg.png
index 121450c28..bb01e9b22 100644
--- a/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-bg.png
+++ b/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-bg.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-thumb.png b/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-thumb.png
index 7b3d7258a..4c7ebfcd5 100644
--- a/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-thumb.png
+++ b/deluge/ui/web/docs/template/resources/images/default/slider/slider-v-thumb.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/tabs/tab-strip-bg.png b/deluge/ui/web/docs/template/resources/images/default/tabs/tab-strip-bg.png
index fa8ab3f46..9b3a23a54 100644
--- a/deluge/ui/web/docs/template/resources/images/default/tabs/tab-strip-bg.png
+++ b/deluge/ui/web/docs/template/resources/images/default/tabs/tab-strip-bg.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/window/left-corners.png b/deluge/ui/web/docs/template/resources/images/default/window/left-corners.png
index 18e215de4..0d5f8a85c 100644
--- a/deluge/ui/web/docs/template/resources/images/default/window/left-corners.png
+++ b/deluge/ui/web/docs/template/resources/images/default/window/left-corners.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/window/left-right.png b/deluge/ui/web/docs/template/resources/images/default/window/left-right.png
index 97d04f304..9fea0e44b 100644
--- a/deluge/ui/web/docs/template/resources/images/default/window/left-right.png
+++ b/deluge/ui/web/docs/template/resources/images/default/window/left-right.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/window/right-corners.png b/deluge/ui/web/docs/template/resources/images/default/window/right-corners.png
index 101118b22..8f88f33f9 100644
--- a/deluge/ui/web/docs/template/resources/images/default/window/right-corners.png
+++ b/deluge/ui/web/docs/template/resources/images/default/window/right-corners.png
Binary files differ
diff --git a/deluge/ui/web/docs/template/resources/images/default/window/top-bottom.png b/deluge/ui/web/docs/template/resources/images/default/window/top-bottom.png
index a73923430..c6d63f683 100644
--- a/deluge/ui/web/docs/template/resources/images/default/window/top-bottom.png
+++ b/deluge/ui/web/docs/template/resources/images/default/window/top-bottom.png
Binary files differ
diff --git a/deluge/ui/web/icons/active.png b/deluge/ui/web/icons/active.png
index c9af82a5f..e5bab66cd 100644
--- a/deluge/ui/web/icons/active.png
+++ b/deluge/ui/web/icons/active.png
Binary files differ
diff --git a/deluge/ui/web/icons/add.png b/deluge/ui/web/icons/add.png
index 3bc06ca36..4b2c04743 100644
--- a/deluge/ui/web/icons/add.png
+++ b/deluge/ui/web/icons/add.png
Binary files differ
diff --git a/deluge/ui/web/icons/add_file.png b/deluge/ui/web/icons/add_file.png
index 6a057450f..80b41c813 100644
--- a/deluge/ui/web/icons/add_file.png
+++ b/deluge/ui/web/icons/add_file.png
Binary files differ
diff --git a/deluge/ui/web/icons/add_url.png b/deluge/ui/web/icons/add_url.png
index 74dc99f4d..3453f501a 100644
--- a/deluge/ui/web/icons/add_url.png
+++ b/deluge/ui/web/icons/add_url.png
Binary files differ
diff --git a/deluge/ui/web/icons/alert.png b/deluge/ui/web/icons/alert.png
index 703663813..49028db9c 100644
--- a/deluge/ui/web/icons/alert.png
+++ b/deluge/ui/web/icons/alert.png
Binary files differ
diff --git a/deluge/ui/web/icons/all.png b/deluge/ui/web/icons/all.png
index c63f8df1a..1a9ba9092 100644
--- a/deluge/ui/web/icons/all.png
+++ b/deluge/ui/web/icons/all.png
Binary files differ
diff --git a/deluge/ui/web/icons/back.png b/deluge/ui/web/icons/back.png
index fdcf9f060..cd955f590 100644
--- a/deluge/ui/web/icons/back.png
+++ b/deluge/ui/web/icons/back.png
Binary files differ
diff --git a/deluge/ui/web/icons/bottom.png b/deluge/ui/web/icons/bottom.png
index ca32c1f10..a57c743ba 100644
--- a/deluge/ui/web/icons/bottom.png
+++ b/deluge/ui/web/icons/bottom.png
Binary files differ
diff --git a/deluge/ui/web/icons/checking.png b/deluge/ui/web/icons/checking.png
index 6758e36e8..9961675c4 100644
--- a/deluge/ui/web/icons/checking.png
+++ b/deluge/ui/web/icons/checking.png
Binary files differ
diff --git a/deluge/ui/web/icons/connection_manager.png b/deluge/ui/web/icons/connection_manager.png
index a0333068f..f2f72c99a 100644
--- a/deluge/ui/web/icons/connection_manager.png
+++ b/deluge/ui/web/icons/connection_manager.png
Binary files differ
diff --git a/deluge/ui/web/icons/connections.png b/deluge/ui/web/icons/connections.png
index f0b660bd5..859df38d5 100644
--- a/deluge/ui/web/icons/connections.png
+++ b/deluge/ui/web/icons/connections.png
Binary files differ
diff --git a/deluge/ui/web/icons/create.png b/deluge/ui/web/icons/create.png
index 9008c98ac..375078c2a 100644
--- a/deluge/ui/web/icons/create.png
+++ b/deluge/ui/web/icons/create.png
Binary files differ
diff --git a/deluge/ui/web/icons/deluge-192.png b/deluge/ui/web/icons/deluge-192.png
index b49a31863..93c6feb44 100644
--- a/deluge/ui/web/icons/deluge-192.png
+++ b/deluge/ui/web/icons/deluge-192.png
Binary files differ
diff --git a/deluge/ui/web/icons/deluge-32.png b/deluge/ui/web/icons/deluge-32.png
index 6787fa39a..916f13632 100644
--- a/deluge/ui/web/icons/deluge-32.png
+++ b/deluge/ui/web/icons/deluge-32.png
Binary files differ
diff --git a/deluge/ui/web/icons/deluge-512.png b/deluge/ui/web/icons/deluge-512.png
index 866f6b939..464dd69f5 100644
--- a/deluge/ui/web/icons/deluge-512.png
+++ b/deluge/ui/web/icons/deluge-512.png
Binary files differ
diff --git a/deluge/ui/web/icons/deluge-apple-180.png b/deluge/ui/web/icons/deluge-apple-180.png
index 82f7be23f..527501fac 100644
--- a/deluge/ui/web/icons/deluge-apple-180.png
+++ b/deluge/ui/web/icons/deluge-apple-180.png
Binary files differ
diff --git a/deluge/ui/web/icons/deluge.png b/deluge/ui/web/icons/deluge.png
index 5afdbe4c6..1291dfaeb 100644
--- a/deluge/ui/web/icons/deluge.png
+++ b/deluge/ui/web/icons/deluge.png
Binary files differ
diff --git a/deluge/ui/web/icons/dht.png b/deluge/ui/web/icons/dht.png
index 363ee0c0f..2396bb132 100644
--- a/deluge/ui/web/icons/dht.png
+++ b/deluge/ui/web/icons/dht.png
Binary files differ
diff --git a/deluge/ui/web/icons/document.png b/deluge/ui/web/icons/document.png
index 5ee00182f..12b8b3e3a 100644
--- a/deluge/ui/web/icons/document.png
+++ b/deluge/ui/web/icons/document.png
Binary files differ
diff --git a/deluge/ui/web/icons/down.png b/deluge/ui/web/icons/down.png
index 68be2b311..3e81fbee8 100644
--- a/deluge/ui/web/icons/down.png
+++ b/deluge/ui/web/icons/down.png
Binary files differ
diff --git a/deluge/ui/web/icons/downloading.png b/deluge/ui/web/icons/downloading.png
index 24d6ffa38..e64aa5adc 100644
--- a/deluge/ui/web/icons/downloading.png
+++ b/deluge/ui/web/icons/downloading.png
Binary files differ
diff --git a/deluge/ui/web/icons/drive.png b/deluge/ui/web/icons/drive.png
index 6fd688eee..a358fa34e 100644
--- a/deluge/ui/web/icons/drive.png
+++ b/deluge/ui/web/icons/drive.png
Binary files differ
diff --git a/deluge/ui/web/icons/edit_trackers.png b/deluge/ui/web/icons/edit_trackers.png
index 80455ecc0..17f0d944d 100644
--- a/deluge/ui/web/icons/edit_trackers.png
+++ b/deluge/ui/web/icons/edit_trackers.png
Binary files differ
diff --git a/deluge/ui/web/icons/error.png b/deluge/ui/web/icons/error.png
index 20ad66a25..2ffb89fcc 100644
--- a/deluge/ui/web/icons/error.png
+++ b/deluge/ui/web/icons/error.png
Binary files differ
diff --git a/deluge/ui/web/icons/expand_all.png b/deluge/ui/web/icons/expand_all.png
index 050419f1c..013d55ad0 100644
--- a/deluge/ui/web/icons/expand_all.png
+++ b/deluge/ui/web/icons/expand_all.png
Binary files differ
diff --git a/deluge/ui/web/icons/favicon.ico b/deluge/ui/web/icons/favicon.ico
index 4e6f12436..176ca6688 100644
--- a/deluge/ui/web/icons/favicon.ico
+++ b/deluge/ui/web/icons/favicon.ico
Binary files differ
diff --git a/deluge/ui/web/icons/find_more.png b/deluge/ui/web/icons/find_more.png
index 199d73eae..e53a4ef57 100644
--- a/deluge/ui/web/icons/find_more.png
+++ b/deluge/ui/web/icons/find_more.png
Binary files differ
diff --git a/deluge/ui/web/icons/forward.png b/deluge/ui/web/icons/forward.png
index 2e5548996..95ee82203 100644
--- a/deluge/ui/web/icons/forward.png
+++ b/deluge/ui/web/icons/forward.png
Binary files differ
diff --git a/deluge/ui/web/icons/help.png b/deluge/ui/web/icons/help.png
index 0566ae067..e68c4586a 100644
--- a/deluge/ui/web/icons/help.png
+++ b/deluge/ui/web/icons/help.png
Binary files differ
diff --git a/deluge/ui/web/icons/high.png b/deluge/ui/web/icons/high.png
index 4b2b1ffac..6755979d6 100644
--- a/deluge/ui/web/icons/high.png
+++ b/deluge/ui/web/icons/high.png
Binary files differ
diff --git a/deluge/ui/web/icons/home.png b/deluge/ui/web/icons/home.png
index a319df64c..dda4941e9 100644
--- a/deluge/ui/web/icons/home.png
+++ b/deluge/ui/web/icons/home.png
Binary files differ
diff --git a/deluge/ui/web/icons/inactive.png b/deluge/ui/web/icons/inactive.png
index cae8b2c0a..e434dad9f 100644
--- a/deluge/ui/web/icons/inactive.png
+++ b/deluge/ui/web/icons/inactive.png
Binary files differ
diff --git a/deluge/ui/web/icons/install_plugin.png b/deluge/ui/web/icons/install_plugin.png
index aff31e77e..e50270478 100644
--- a/deluge/ui/web/icons/install_plugin.png
+++ b/deluge/ui/web/icons/install_plugin.png
Binary files differ
diff --git a/deluge/ui/web/icons/login.png b/deluge/ui/web/icons/login.png
index c06d0d119..ccc7ff3ad 100644
--- a/deluge/ui/web/icons/login.png
+++ b/deluge/ui/web/icons/login.png
Binary files differ
diff --git a/deluge/ui/web/icons/logout.png b/deluge/ui/web/icons/logout.png
index 2f53a65b4..1e9d5b83d 100644
--- a/deluge/ui/web/icons/logout.png
+++ b/deluge/ui/web/icons/logout.png
Binary files differ
diff --git a/deluge/ui/web/icons/low.png b/deluge/ui/web/icons/low.png
index ff669db7d..b9f1d2b04 100644
--- a/deluge/ui/web/icons/low.png
+++ b/deluge/ui/web/icons/low.png
Binary files differ
diff --git a/deluge/ui/web/icons/magnet.png b/deluge/ui/web/icons/magnet.png
index 61d6dabb7..6fc25ed69 100644
--- a/deluge/ui/web/icons/magnet.png
+++ b/deluge/ui/web/icons/magnet.png
Binary files differ
diff --git a/deluge/ui/web/icons/magnet_add.png b/deluge/ui/web/icons/magnet_add.png
index 37c1c36b0..f6010f54a 100644
--- a/deluge/ui/web/icons/magnet_add.png
+++ b/deluge/ui/web/icons/magnet_add.png
Binary files differ
diff --git a/deluge/ui/web/icons/magnet_copy.png b/deluge/ui/web/icons/magnet_copy.png
index a4be9d218..9b4ec6bbf 100644
--- a/deluge/ui/web/icons/magnet_copy.png
+++ b/deluge/ui/web/icons/magnet_copy.png
Binary files differ
diff --git a/deluge/ui/web/icons/move.png b/deluge/ui/web/icons/move.png
index ea83832c0..319cc166a 100644
--- a/deluge/ui/web/icons/move.png
+++ b/deluge/ui/web/icons/move.png
Binary files differ
diff --git a/deluge/ui/web/icons/no_download.png b/deluge/ui/web/icons/no_download.png
index 206d43660..e569b6a4f 100644
--- a/deluge/ui/web/icons/no_download.png
+++ b/deluge/ui/web/icons/no_download.png
Binary files differ
diff --git a/deluge/ui/web/icons/normal.png b/deluge/ui/web/icons/normal.png
index 2e5548996..95ee82203 100644
--- a/deluge/ui/web/icons/normal.png
+++ b/deluge/ui/web/icons/normal.png
Binary files differ
diff --git a/deluge/ui/web/icons/ok.png b/deluge/ui/web/icons/ok.png
index 33eb7db27..79195804c 100644
--- a/deluge/ui/web/icons/ok.png
+++ b/deluge/ui/web/icons/ok.png
Binary files differ
diff --git a/deluge/ui/web/icons/pause.png b/deluge/ui/web/icons/pause.png
index 8fdd6bc73..6e7b8c351 100644
--- a/deluge/ui/web/icons/pause.png
+++ b/deluge/ui/web/icons/pause.png
Binary files differ
diff --git a/deluge/ui/web/icons/preferences.png b/deluge/ui/web/icons/preferences.png
index 7d6deb246..e8e6a0666 100644
--- a/deluge/ui/web/icons/preferences.png
+++ b/deluge/ui/web/icons/preferences.png
Binary files differ
diff --git a/deluge/ui/web/icons/queue.png b/deluge/ui/web/icons/queue.png
index 3e4b4bee0..150ef8a43 100644
--- a/deluge/ui/web/icons/queue.png
+++ b/deluge/ui/web/icons/queue.png
Binary files differ
diff --git a/deluge/ui/web/icons/queued.png b/deluge/ui/web/icons/queued.png
index f9f74540f..74db4c5eb 100644
--- a/deluge/ui/web/icons/queued.png
+++ b/deluge/ui/web/icons/queued.png
Binary files differ
diff --git a/deluge/ui/web/icons/recheck.png b/deluge/ui/web/icons/recheck.png
index 199d73eae..e53a4ef57 100644
--- a/deluge/ui/web/icons/recheck.png
+++ b/deluge/ui/web/icons/recheck.png
Binary files differ
diff --git a/deluge/ui/web/icons/remove.png b/deluge/ui/web/icons/remove.png
index 3f911914d..70db1ca4e 100644
--- a/deluge/ui/web/icons/remove.png
+++ b/deluge/ui/web/icons/remove.png
Binary files differ
diff --git a/deluge/ui/web/icons/seeding.png b/deluge/ui/web/icons/seeding.png
index fce70a8d9..2d9dc58f7 100644
--- a/deluge/ui/web/icons/seeding.png
+++ b/deluge/ui/web/icons/seeding.png
Binary files differ
diff --git a/deluge/ui/web/icons/start.png b/deluge/ui/web/icons/start.png
index ff669db7d..b9f1d2b04 100644
--- a/deluge/ui/web/icons/start.png
+++ b/deluge/ui/web/icons/start.png
Binary files differ
diff --git a/deluge/ui/web/icons/top.png b/deluge/ui/web/icons/top.png
index 850656f3d..db9969d2b 100644
--- a/deluge/ui/web/icons/top.png
+++ b/deluge/ui/web/icons/top.png
Binary files differ
diff --git a/deluge/ui/web/icons/traffic.png b/deluge/ui/web/icons/traffic.png
index ecd87204d..b4ce5ea14 100644
--- a/deluge/ui/web/icons/traffic.png
+++ b/deluge/ui/web/icons/traffic.png
Binary files differ
diff --git a/deluge/ui/web/icons/up.png b/deluge/ui/web/icons/up.png
index 223e73326..9aa7c6437 100644
--- a/deluge/ui/web/icons/up.png
+++ b/deluge/ui/web/icons/up.png
Binary files differ
diff --git a/deluge/ui/web/icons/update.png b/deluge/ui/web/icons/update.png
index 0ff6d45a7..ee61b01e9 100644
--- a/deluge/ui/web/icons/update.png
+++ b/deluge/ui/web/icons/update.png
Binary files differ
diff --git a/deluge/ui/web/icons/upload_slots.png b/deluge/ui/web/icons/upload_slots.png
index 0e7000d81..294523eb5 100644
--- a/deluge/ui/web/icons/upload_slots.png
+++ b/deluge/ui/web/icons/upload_slots.png
Binary files differ
diff --git a/deluge/ui/web/icons/warning.png b/deluge/ui/web/icons/warning.png
index f66feda89..c81dc592f 100644
--- a/deluge/ui/web/icons/warning.png
+++ b/deluge/ui/web/icons/warning.png
Binary files differ
diff --git a/deluge/ui/web/js/deluge-all/AboutWindow.js b/deluge/ui/web/js/deluge-all/AboutWindow.js
index 15fede9b8..cfae7a862 100644
--- a/deluge/ui/web/js/deluge-all/AboutWindow.js
+++ b/deluge/ui/web/js/deluge-all/AboutWindow.js
@@ -104,8 +104,7 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, {
{
xtype: 'label',
style: 'padding-top: 5px; font-size: 12px;',
- html:
- '<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>',
+ html: '<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>',
},
]);
this.addButton(_('Close'), this.onCloseClick, this);
diff --git a/deluge/ui/web/js/deluge-all/AddTrackerWindow.js b/deluge/ui/web/js/deluge-all/AddTrackerWindow.js
index 8fbe0b221..aaf4a3ff9 100644
--- a/deluge/ui/web/js/deluge-all/AddTrackerWindow.js
+++ b/deluge/ui/web/js/deluge-all/AddTrackerWindow.js
@@ -10,7 +10,8 @@
Ext.ns('Deluge');
// Custom VType validator for tracker urls
-var trackerUrlTest = /(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
+var trackerUrlTest =
+ /(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
Ext.apply(Ext.form.VTypes, {
trackerUrl: function (val, field) {
return trackerUrlTest.test(val);
diff --git a/deluge/ui/web/js/deluge-all/ConnectionManager.js b/deluge/ui/web/js/deluge-all/ConnectionManager.js
index 5261726fb..2e61e22a2 100644
--- a/deluge/ui/web/js/deluge-all/ConnectionManager.js
+++ b/deluge/ui/web/js/deluge-all/ConnectionManager.js
@@ -162,13 +162,23 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
},
update: function () {
+ this.updating = setTimeout(this.update, 2000);
this.list.getStore().each(function (r) {
deluge.client.web.get_host_status(r.id, {
- success: this.onGetHostStatus,
+ success: this.onUpdate,
scope: this,
});
}, this);
},
+ onUpdate: function (host) {
+ if (!this.isVisible()) return;
+ this.onGetHostStatus(host);
+
+ if (this.updating) {
+ clearTimeout(this.updating);
+ }
+ this.updating = setTimeout(this.update, 2000);
+ },
/**
* Updates the buttons in the Connection Manager UI according to the
@@ -312,7 +322,10 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
// private
onHide: function () {
- if (this.running) window.clearInterval(this.running);
+ if (this.updating) {
+ window.clearTimeout(this.updating);
+ this.updating = undefined;
+ }
},
// private
@@ -396,8 +409,8 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
this.stopHostButton = bbar.items.get('cm-stop');
}
this.loadHosts();
- if (this.running) return;
- this.running = window.setInterval(this.update, 2000, this);
+ if (this.updating) return;
+ this.updating = window.setTimeout(this.update, 2000);
},
// private
diff --git a/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js b/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js
index 3e0cc9464..ddcd4abf9 100644
--- a/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js
+++ b/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js
@@ -51,7 +51,7 @@ Deluge.CopyMagnet = Ext.extend(Ext.Window, {
},
show: function (a) {
Deluge.CopyMagnet.superclass.show.call(this);
- const torrent = deluge.torrents.getSelected();
+ var torrent = deluge.torrents.getSelected();
deluge.client.core.get_magnet_uri(torrent.id, {
success: this.onRequestComplete,
scope: this,
diff --git a/deluge/ui/web/js/deluge-all/Deluge.js b/deluge/ui/web/js/deluge-all/Deluge.js
index 86cae6d89..260ad978f 100644
--- a/deluge/ui/web/js/deluge-all/Deluge.js
+++ b/deluge/ui/web/js/deluge-all/Deluge.js
@@ -25,11 +25,6 @@ Ext.state.Manager.setProvider(
// Add some additional functions to ext and setup some of the
// configurable parameters
Ext.apply(Ext, {
- escapeHTML: function (text) {
- text = String(text).replace('<', '&lt;').replace('>', '&gt;');
- return text.replace('&', '&amp;');
- },
-
isObjectEmpty: function (obj) {
for (var i in obj) {
return false;
diff --git a/deluge/ui/web/js/deluge-all/EditTrackersWindow.js b/deluge/ui/web/js/deluge-all/EditTrackersWindow.js
index f6733aaa6..178fd583f 100644
--- a/deluge/ui/web/js/deluge-all/EditTrackersWindow.js
+++ b/deluge/ui/web/js/deluge-all/EditTrackersWindow.js
@@ -57,6 +57,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, {
header: _('Tracker'),
width: 0.9,
dataIndex: 'url',
+ tpl: new Ext.XTemplate('{url:htmlEncode}'),
},
],
columnSort: {
diff --git a/deluge/ui/web/js/deluge-all/FilterPanel.js b/deluge/ui/web/js/deluge-all/FilterPanel.js
index b6e5ec5ca..f1fade120 100644
--- a/deluge/ui/web/js/deluge-all/FilterPanel.js
+++ b/deluge/ui/web/js/deluge-all/FilterPanel.js
@@ -171,5 +171,5 @@ Deluge.FilterPanel.templates = {
tracker_host:
'<div class="x-deluge-filter" style="background-image: url(' +
deluge.config.base +
- 'tracker/{filter});">{filter} ({count})</div>',
+ 'tracker/{filter});">{filter:htmlEncode} ({count})</div>',
};
diff --git a/deluge/ui/web/js/deluge-all/Formatters.js b/deluge/ui/web/js/deluge-all/Formatters.js
index 443bfdf56..6b09abee5 100644
--- a/deluge/ui/web/js/deluge-all/Formatters.js
+++ b/deluge/ui/web/js/deluge-all/Formatters.js
@@ -15,7 +15,23 @@
* @version 1.3
* @singleton
*/
-Deluge.Formatters = {
+Deluge.Formatters = (function () {
+ var charToEntity = {
+ '&': '&amp;',
+ '>': '&gt;',
+ '<': '&lt;',
+ '"': '&quot;',
+ "'": '&#39;',
+ };
+
+ var charToEntityRegex = new RegExp(
+ '(' + Object.keys(charToEntity).join('|') + ')',
+ 'g'
+ );
+ var htmlEncodeReplaceFn = function (match, capture) {
+ return charToEntity[capture];
+ };
+
/**
* Formats a date string in the date representation of the current locale,
* based on the systems timezone.
@@ -24,154 +40,162 @@ Deluge.Formatters = {
* @return {String} a string in the date representation of the current locale
* or "" if seconds < 0.
*/
- date: function (timestamp) {
- function zeroPad(num, count) {
- var numZeropad = num + '';
- while (numZeropad.length < count) {
- numZeropad = '0' + numZeropad;
+ return (Formatters = {
+ date: function (timestamp) {
+ function zeroPad(num, count) {
+ var numZeropad = num + '';
+ while (numZeropad.length < count) {
+ numZeropad = '0' + numZeropad;
+ }
+ return numZeropad;
+ }
+ timestamp = timestamp * 1000;
+ var date = new Date(timestamp);
+ return String.format(
+ '{0}/{1}/{2} {3}:{4}:{5}',
+ zeroPad(date.getDate(), 2),
+ zeroPad(date.getMonth() + 1, 2),
+ date.getFullYear(),
+ zeroPad(date.getHours(), 2),
+ zeroPad(date.getMinutes(), 2),
+ zeroPad(date.getSeconds(), 2)
+ );
+ },
+
+ /**
+ * Formats the bytes value into a string with KiB, MiB or GiB units.
+ *
+ * @param {Number} bytes the filesize in bytes
+ * @param {Boolean} showZero pass in true to displays 0 values
+ * @return {String} formatted string with KiB, MiB or GiB units.
+ */
+ size: function (bytes, showZero) {
+ if (!bytes && !showZero) return '';
+ bytes = bytes / 1024.0;
+
+ if (bytes < 1024) {
+ return bytes.toFixed(1) + ' KiB';
+ } else {
+ bytes = bytes / 1024;
}
- return numZeropad;
- }
- timestamp = timestamp * 1000;
- var date = new Date(timestamp);
- return String.format(
- '{0}/{1}/{2} {3}:{4}:{5}',
- zeroPad(date.getDate(), 2),
- zeroPad(date.getMonth() + 1, 2),
- date.getFullYear(),
- zeroPad(date.getHours(), 2),
- zeroPad(date.getMinutes(), 2),
- zeroPad(date.getSeconds(), 2)
- );
- },
-
- /**
- * Formats the bytes value into a string with KiB, MiB or GiB units.
- *
- * @param {Number} bytes the filesize in bytes
- * @param {Boolean} showZero pass in true to displays 0 values
- * @return {String} formatted string with KiB, MiB or GiB units.
- */
- size: function (bytes, showZero) {
- if (!bytes && !showZero) return '';
- bytes = bytes / 1024.0;
-
- if (bytes < 1024) {
- return bytes.toFixed(1) + ' KiB';
- } else {
- bytes = bytes / 1024;
- }
-
- if (bytes < 1024) {
- return bytes.toFixed(1) + ' MiB';
- } else {
- bytes = bytes / 1024;
- }
-
- return bytes.toFixed(1) + ' GiB';
- },
-
- /**
- * Formats the bytes value into a string with K, M or G units.
- *
- * @param {Number} bytes the filesize in bytes
- * @param {Boolean} showZero pass in true to displays 0 values
- * @return {String} formatted string with K, M or G units.
- */
- sizeShort: function (bytes, showZero) {
- if (!bytes && !showZero) return '';
- bytes = bytes / 1024.0;
- if (bytes < 1024) {
- return bytes.toFixed(1) + ' K';
- } else {
- bytes = bytes / 1024;
- }
+ if (bytes < 1024) {
+ return bytes.toFixed(1) + ' MiB';
+ } else {
+ bytes = bytes / 1024;
+ }
- if (bytes < 1024) {
- return bytes.toFixed(1) + ' M';
- } else {
- bytes = bytes / 1024;
- }
+ return bytes.toFixed(1) + ' GiB';
+ },
+
+ /**
+ * Formats the bytes value into a string with K, M or G units.
+ *
+ * @param {Number} bytes the filesize in bytes
+ * @param {Boolean} showZero pass in true to displays 0 values
+ * @return {String} formatted string with K, M or G units.
+ */
+ sizeShort: function (bytes, showZero) {
+ if (!bytes && !showZero) return '';
+ bytes = bytes / 1024.0;
+
+ if (bytes < 1024) {
+ return bytes.toFixed(1) + ' K';
+ } else {
+ bytes = bytes / 1024;
+ }
- return bytes.toFixed(1) + ' G';
- },
+ if (bytes < 1024) {
+ return bytes.toFixed(1) + ' M';
+ } else {
+ bytes = bytes / 1024;
+ }
- /**
- * Formats a string to display a transfer speed utilizing {@link #size}
- *
- * @param {Number} bytes the number of bytes per second
- * @param {Boolean} showZero pass in true to displays 0 values
- * @return {String} formatted string with KiB, MiB or GiB units.
- */
- speed: function (bytes, showZero) {
- return !bytes && !showZero ? '' : fsize(bytes, showZero) + '/s';
- },
+ return bytes.toFixed(1) + ' G';
+ },
+
+ /**
+ * Formats a string to display a transfer speed utilizing {@link #size}
+ *
+ * @param {Number} bytes the number of bytes per second
+ * @param {Boolean} showZero pass in true to displays 0 values
+ * @return {String} formatted string with KiB, MiB or GiB units.
+ */
+ speed: function (bytes, showZero) {
+ return !bytes && !showZero ? '' : fsize(bytes, showZero) + '/s';
+ },
+
+ /**
+ * Formats a string to show time in a human readable form.
+ *
+ * @param {Number} time the number of seconds
+ * @return {String} a formatted time string. will return '' if seconds == 0
+ */
+ timeRemaining: function (time) {
+ if (time <= 0) {
+ return '&infin;';
+ }
+ time = time.toFixed(0);
+ if (time < 60) {
+ return time + 's';
+ } else {
+ time = time / 60;
+ }
- /**
- * Formats a string to show time in a human readable form.
- *
- * @param {Number} time the number of seconds
- * @return {String} a formatted time string. will return '' if seconds == 0
- */
- timeRemaining: function (time) {
- if (time <= 0) {
- return '&infin;';
- }
- time = time.toFixed(0);
- if (time < 60) {
- return time + 's';
- } else {
- time = time / 60;
- }
-
- if (time < 60) {
- var minutes = Math.floor(time);
- var seconds = Math.round(60 * (time - minutes));
- if (seconds > 0) {
- return minutes + 'm ' + seconds + 's';
+ if (time < 60) {
+ var minutes = Math.floor(time);
+ var seconds = Math.round(60 * (time - minutes));
+ if (seconds > 0) {
+ return minutes + 'm ' + seconds + 's';
+ } else {
+ return minutes + 'm';
+ }
} else {
- return minutes + 'm';
+ time = time / 60;
}
- } else {
- time = time / 60;
- }
-
- if (time < 24) {
- var hours = Math.floor(time);
- var minutes = Math.round(60 * (time - hours));
- if (minutes > 0) {
- return hours + 'h ' + minutes + 'm';
+
+ if (time < 24) {
+ var hours = Math.floor(time);
+ var minutes = Math.round(60 * (time - hours));
+ if (minutes > 0) {
+ return hours + 'h ' + minutes + 'm';
+ } else {
+ return hours + 'h';
+ }
} else {
- return hours + 'h';
+ time = time / 24;
}
- } else {
- time = time / 24;
- }
-
- var days = Math.floor(time);
- var hours = Math.round(24 * (time - days));
- if (hours > 0) {
- return days + 'd ' + hours + 'h';
- } else {
- return days + 'd';
- }
- },
- /**
- * Simply returns the value untouched, for when no formatting is required.
- *
- * @param {Mixed} value the value to be displayed
- * @return the untouched value.
- */
- plain: function (value) {
- return value;
- },
-
- cssClassEscape: function (value) {
- return value.toLowerCase().replace('.', '_');
- },
-};
+ var days = Math.floor(time);
+ var hours = Math.round(24 * (time - days));
+ if (hours > 0) {
+ return days + 'd ' + hours + 'h';
+ } else {
+ return days + 'd';
+ }
+ },
+
+ /**
+ * Simply returns the value untouched, for when no formatting is required.
+ *
+ * @param {Mixed} value the value to be displayed
+ * @return the untouched value.
+ */
+ plain: function (value) {
+ return value;
+ },
+
+ cssClassEscape: function (value) {
+ return value.toLowerCase().replace('.', '_');
+ },
+
+ htmlEncode: function (value) {
+ return !value
+ ? value
+ : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
+ },
+ });
+})();
var fsize = Deluge.Formatters.size;
var fsize_short = Deluge.Formatters.sizeShort;
var fspeed = Deluge.Formatters.speed;
@@ -179,3 +203,4 @@ var ftime = Deluge.Formatters.timeRemaining;
var fdate = Deluge.Formatters.date;
var fplain = Deluge.Formatters.plain;
Ext.util.Format.cssClassEscape = Deluge.Formatters.cssClassEscape;
+Ext.util.Format.htmlEncode = Deluge.Formatters.htmlEncode;
diff --git a/deluge/ui/web/js/deluge-all/Sidebar.js b/deluge/ui/web/js/deluge-all/Sidebar.js
index eb08a898f..a6512b258 100644
--- a/deluge/ui/web/js/deluge-all/Sidebar.js
+++ b/deluge/ui/web/js/deluge-all/Sidebar.js
@@ -60,14 +60,16 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, {
this.doLayout();
this.panels[filter] = panel;
- panel.header.on('click', function (header) {
- if (!deluge.config.sidebar_multiple_filters) {
- deluge.ui.update();
- }
- if (!panel.list.getSelectionCount()) {
- panel.list.select(0);
- }
- });
+ if (panel.header) {
+ panel.header.on('click', function (header) {
+ if (!deluge.config.sidebar_multiple_filters) {
+ deluge.ui.update();
+ }
+ if (!panel.list.getSelectionCount()) {
+ panel.list.select(0);
+ }
+ });
+ }
this.fireEvent('filtercreate', this, panel);
panel.updateStates(states);
diff --git a/deluge/ui/web/js/deluge-all/TorrentGrid.js b/deluge/ui/web/js/deluge-all/TorrentGrid.js
index f664c765c..5db7e9fbc 100644
--- a/deluge/ui/web/js/deluge-all/TorrentGrid.js
+++ b/deluge/ui/web/js/deluge-all/TorrentGrid.js
@@ -17,7 +17,7 @@
return String.format(
'<div class="torrent-name x-deluge-{0}">{1}</div>',
r.data['state'].toLowerCase(),
- value
+ Ext.util.Format.htmlEncode(value)
);
}
function torrentSpeedRenderer(value) {
@@ -61,13 +61,15 @@
return String.format(
'<div style="background: url(' +
deluge.config.base +
- 'tracker/{0}) no-repeat; padding-left: 20px;">{0}</div>',
- value
+ 'tracker/{0}) no-repeat; background-size: contain; padding-left: 20px;">{0}</div>',
+ Ext.util.Format.htmlEncode(value)
);
}
function etaSorter(eta) {
- return eta * -1;
+ if (eta === 0) return Number.MAX_VALUE;
+ if (eta <= -1) return Number.MAX_SAFE_INTEGER;
+ return eta;
}
function dateOrNever(date) {
@@ -75,7 +77,9 @@
}
function timeOrInf(time) {
- return time < 0 ? '&infin;' : ftime(time);
+ if (time === 0) return '';
+ if (time <= -1) return '&infin;';
+ return ftime(time);
}
/**
@@ -320,6 +324,8 @@
{ name: 'ratio', type: 'float' },
{ name: 'distributed_copies', type: 'float' },
{ name: 'time_added', type: 'int' },
+ { name: 'last_seen_complete', type: 'int' },
+ { name: 'completed_time', type: 'int' },
{ name: 'tracker_host' },
{ name: 'download_location' },
{ name: 'total_done', type: 'int' },
diff --git a/deluge/ui/web/js/deluge-all/UI.js b/deluge/ui/web/js/deluge-all/UI.js
index cc877d597..f7edc84b1 100644
--- a/deluge/ui/web/js/deluge-all/UI.js
+++ b/deluge/ui/web/js/deluge-all/UI.js
@@ -134,14 +134,24 @@ deluge.ui = {
deluge.details.update();
},
- onConnectionError: function (error) {},
+ onConnectionError: function (error) {
+ if (this.checking) {
+ clearTimeout(this.checking);
+ }
+ this.checking = setTimeout(this.checkConnection, 2000);
+ },
onConnectionSuccess: function (result) {
+ if (this.checking) {
+ clearTimeout(this.checking);
+ this.checking = undefined;
+ }
+ this.running = setTimeout(this.update, 2000);
+ this.update();
deluge.statusbar.setStatus({
iconCls: 'x-deluge-statusbar icon-ok',
text: _('Connection restored'),
});
- clearInterval(this.checking);
if (!result) {
deluge.connectionManager.show();
}
@@ -159,9 +169,13 @@ deluge.ui = {
deluge.statusbar.setStatus({
text: _('Lost connection to webserver'),
});
- this.checking = setInterval(this.checkConnection, 2000);
+ this.checking = setTimeout(this.checkConnection, 2000);
}
this.errorCount++;
+ if (this.running) {
+ clearTimeout(this.running);
+ this.running = undefined;
+ }
},
/**
@@ -170,10 +184,15 @@ deluge.ui = {
* Updates the various components in the interface.
*/
onUpdate: function (data) {
+ if (this.running) {
+ clearTimeout(this.running);
+ this.running = undefined;
+ }
if (!data['connected']) {
deluge.connectionManager.disconnect(true);
return;
}
+ this.running = setTimeout(this.update, 2000);
if (deluge.config.show_session_speed) {
document.title =
@@ -201,7 +220,7 @@ deluge.ui = {
*/
onConnect: function () {
if (!this.running) {
- this.running = setInterval(this.update, 2000);
+ this.running = setTimeout(this.update, 2000);
this.update();
}
deluge.client.web.get_plugins({
@@ -280,8 +299,8 @@ deluge.ui = {
*/
stop: function () {
if (this.running) {
- clearInterval(this.running);
- this.running = false;
+ clearTimeout(this.running);
+ this.running = undefined;
deluge.torrents.getStore().removeAll();
}
},
diff --git a/deluge/ui/web/js/deluge-all/add/AddWindow.js b/deluge/ui/web/js/deluge-all/add/AddWindow.js
index a4aff067b..f5f2fdf07 100644
--- a/deluge/ui/web/js/deluge-all/add/AddWindow.js
+++ b/deluge/ui/web/js/deluge-all/add/AddWindow.js
@@ -64,20 +64,6 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, {
this.addButton(_('Cancel'), this.onCancelClick, this);
this.addButton(_('Add'), this.onAddClick, this);
- function torrentRenderer(value, p, r) {
- if (r.data['info_hash']) {
- return String.format(
- '<div class="x-deluge-add-torrent-name">{0}</div>',
- value
- );
- } else {
- return String.format(
- '<div class="x-deluge-add-torrent-name-loading">{0}</div>',
- value
- );
- }
- }
-
this.list = new Ext.list.ListView({
store: new Ext.data.SimpleStore({
fields: [
@@ -91,8 +77,10 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, {
id: 'torrent',
width: 150,
sortable: true,
- renderer: torrentRenderer,
dataIndex: 'text',
+ tpl: new Ext.XTemplate(
+ '<div class="x-deluge-add-torrent-name">{text:htmlEncode}</div>'
+ ),
},
],
stripeRows: true,
diff --git a/deluge/ui/web/js/deluge-all/add/FilesTab.js b/deluge/ui/web/js/deluge-all/add/FilesTab.js
index fed52282d..d712c023d 100644
--- a/deluge/ui/web/js/deluge-all/add/FilesTab.js
+++ b/deluge/ui/web/js/deluge-all/add/FilesTab.js
@@ -28,6 +28,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, {
header: _('Filename'),
width: 295,
dataIndex: 'filename',
+ tpl: new Ext.XTemplate('{filename:htmlEncode}'),
},
{
header: _('Size'),
diff --git a/deluge/ui/web/js/deluge-all/add/OptionsPanel.js b/deluge/ui/web/js/deluge-all/add/OptionsPanel.js
index 6b75686a3..365b00190 100644
--- a/deluge/ui/web/js/deluge-all/add/OptionsPanel.js
+++ b/deluge/ui/web/js/deluge-all/add/OptionsPanel.js
@@ -134,9 +134,8 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, {
nodes,
function (node) {
if (node.attributes.fileindex < 0) return;
- var priorities = this.form.optionsManager.get(
- 'file_priorities'
- );
+ var priorities =
+ this.form.optionsManager.get('file_priorities');
priorities[node.attributes.fileindex] = newValue;
this.form.optionsManager.update('file_priorities', priorities);
},
diff --git a/deluge/ui/web/js/deluge-all/details/DetailsTab.js b/deluge/ui/web/js/deluge-all/details/DetailsTab.js
index fdb4f7f0d..f1da178b1 100644
--- a/deluge/ui/web/js/deluge-all/details/DetailsTab.js
+++ b/deluge/ui/web/js/deluge-all/details/DetailsTab.js
@@ -91,7 +91,9 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, {
for (var field in this.fields) {
if (!Ext.isDefined(data[field])) continue; // This is a field we are not responsible for.
if (data[field] == this.oldData[field]) continue;
- this.fields[field].dom.innerHTML = Ext.escapeHTML(data[field]);
+ this.fields[field].dom.innerHTML = Ext.util.Format.htmlEncode(
+ data[field]
+ );
}
this.oldData = data;
},
diff --git a/deluge/ui/web/js/deluge-all/details/FilesTab.js b/deluge/ui/web/js/deluge-all/details/FilesTab.js
index edc388d19..36ef968dd 100644
--- a/deluge/ui/web/js/deluge-all/details/FilesTab.js
+++ b/deluge/ui/web/js/deluge-all/details/FilesTab.js
@@ -18,6 +18,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, {
header: _('Filename'),
width: 330,
dataIndex: 'filename',
+ tpl: new Ext.XTemplate('{filename:htmlEncode}'),
},
{
header: _('Size'),
@@ -213,7 +214,9 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, {
{
success: function () {
Ext.each(nodes, function (node) {
- node.setColumnValue(3, baseItem.filePriority);
+ node.attributes.priority =
+ baseItem.filePriority;
+ node.ui.updateColumns();
});
},
scope: this,
diff --git a/deluge/ui/web/js/deluge-all/details/OptionsTab.js b/deluge/ui/web/js/deluge-all/details/OptionsTab.js
index 7e59cba4e..f8a08be9d 100644
--- a/deluge/ui/web/js/deluge-all/details/OptionsTab.js
+++ b/deluge/ui/web/js/deluge-all/details/OptionsTab.js
@@ -86,7 +86,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, {
xtype: 'number',
decimalPrecision: 1,
minValue: -1,
- maxValue: 99999,
+ maxValue: 9999999,
},
});
this.fieldsets.bandwidth.add({
@@ -113,7 +113,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, {
xtype: 'number',
decimalPrecision: 1,
minValue: -1,
- maxValue: 99999,
+ maxValue: 9999999,
},
});
this.fieldsets.bandwidth.add({
diff --git a/deluge/ui/web/js/deluge-all/details/PeersTab.js b/deluge/ui/web/js/deluge-all/details/PeersTab.js
index 66d4a4b95..a1919630d 100644
--- a/deluge/ui/web/js/deluge-all/details/PeersTab.js
+++ b/deluge/ui/web/js/deluge-all/details/PeersTab.js
@@ -73,7 +73,7 @@
header: _('Client'),
width: 125,
sortable: true,
- renderer: fplain,
+ renderer: 'htmlEncode',
dataIndex: 'client',
},
{
diff --git a/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js b/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js
index 4c3720198..563dedd3c 100644
--- a/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js
+++ b/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js
@@ -40,7 +40,7 @@ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, {
defaultType: 'spinnerfield',
defaults: {
minValue: -1,
- maxValue: 99999,
+ maxValue: 9999999,
},
style: 'margin-bottom: 0px; padding-bottom: 0px;',
autoHeight: true,
@@ -117,8 +117,7 @@ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, {
border: false,
title: '',
defaultType: 'checkbox',
- style:
- 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;',
+ style: 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;',
autoHeight: true,
});
om.bind(
diff --git a/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js b/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js
index 1787826b7..da205c29b 100644
--- a/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js
+++ b/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js
@@ -40,7 +40,7 @@ Deluge.preferences.Daemon = Ext.extend(Ext.form.FormPanel, {
value: 58846,
decimalPrecision: 0,
minValue: -1,
- maxValue: 99999,
+ maxValue: 65535,
})
);
diff --git a/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js b/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js
index b6b76ebde..a5a7909f9 100644
--- a/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js
+++ b/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js
@@ -88,6 +88,33 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, {
})
);
+ var themePanel = this.add({
+ xtype: 'fieldset',
+ border: false,
+ title: _('Theme'),
+ style: 'margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px',
+ autoHeight: true,
+ labelWidth: 1,
+ defaultType: 'checkbox',
+ });
+ this.theme = om.bind(
+ 'theme',
+ themePanel.add({
+ xtype: 'combo',
+ name: 'theme',
+ labelSeparator: '',
+ mode: 'local',
+ width: 200,
+ store: new Ext.data.ArrayStore({
+ fields: ['id', 'text'],
+ }),
+ editable: false,
+ triggerAction: 'all',
+ valueField: 'id',
+ displayField: 'text',
+ })
+ );
+
fieldset = this.add({
xtype: 'fieldset',
border: false,
@@ -140,7 +167,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, {
fieldLabel: _('Session Timeout:'),
decimalPrecision: 0,
minValue: -1,
- maxValue: 99999,
+ maxValue: Number.MAX_SAFE_INTEGER || Number.MAX_VALUE,
})
);
om.bind(
@@ -217,6 +244,24 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, {
icon: Ext.MessageBox.QUESTION,
});
}
+ if ('theme' in changed) {
+ deluge.client.web.set_theme(changed['theme']);
+ Ext.Msg.show({
+ title: _('WebUI Theme Changed'),
+ msg: _(
+ 'Do you want to refresh the page now to use the new theme?'
+ ),
+ buttons: {
+ yes: _('Refresh'),
+ no: _('Close'),
+ },
+ multiline: false,
+ fn: function (btnText) {
+ if (btnText === 'yes') location.reload();
+ },
+ icon: Ext.MessageBox.QUESTION,
+ });
+ }
}
if (this.oldPassword.getValue() || this.newPassword.getValue()) {
this.onPasswordChange();
@@ -237,6 +282,11 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, {
this.language.setValue(this.optionsManager.get('language'));
},
+ onGotThemes: function (info, obj, response, request) {
+ this.theme.store.loadData(info);
+ this.theme.setValue(this.optionsManager.get('theme'));
+ },
+
onPasswordChange: function () {
var newPassword = this.newPassword.getValue();
if (newPassword != this.confirmPassword.getValue()) {
@@ -295,6 +345,10 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, {
success: this.onGotLanguages,
scope: this,
});
+ deluge.client.webutils.get_themes({
+ success: this.onGotThemes,
+ scope: this,
+ });
},
onSSLCheck: function (e, checked) {
diff --git a/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js b/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js
index ed2abdcdc..4cfed016b 100644
--- a/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js
+++ b/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js
@@ -46,7 +46,6 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, {
columns: [
{
id: 'name',
- renderer: fplain,
dataIndex: 'name',
},
],
diff --git a/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js b/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js
index 31eca735c..ee761aa6f 100644
--- a/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js
+++ b/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js
@@ -80,9 +80,8 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, {
// Generate the column configs with the correct width setting
for (var i = 0; i < numCols; i++) {
var cc = Ext.apply({ items: [] }, colCfg);
- cc[
- this.columns[i] <= 1 ? 'columnWidth' : 'width'
- ] = this.columns[i];
+ cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] =
+ this.columns[i];
if (this.defaults) {
cc.defaults = Ext.apply(
cc.defaults || {},
diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py
index ffdee342c..5f4b3dcb8 100644
--- a/deluge/ui/web/json_api.py
+++ b/deluge/ui/web/json_api.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Damien Churchill <damoxc@gmail.com>
#
@@ -7,8 +6,7 @@
# See LICENSE for more details.
#
-from __future__ import division, unicode_literals
-
+import cgi
import json
import logging
import os
@@ -16,7 +14,6 @@ import shutil
import tempfile
from base64 import b64encode
from types import FunctionType
-from xml.sax.saxutils import escape as xml_escape
from twisted.internet import defer, reactor
from twisted.internet.defer import Deferred, DeferredList
@@ -39,7 +36,7 @@ log = logging.getLogger(__name__)
class JSONComponent(component.Component):
def __init__(self, name, interval=1, depend=None):
- super(JSONComponent, self).__init__(name, interval, depend)
+ super().__init__(name, interval, depend)
self._json = component.get('JSON')
self._json.register_object(self, name)
@@ -146,7 +143,7 @@ class JSON(resource.Resource, component.Component):
params = request_data['params']
request_id = request_data['id']
except KeyError as ex:
- message = 'Invalid JSON request, missing param %s in %s' % (
+ message = 'Invalid JSON request, missing param {} in {}'.format(
ex,
request_data,
)
@@ -167,7 +164,7 @@ class JSON(resource.Resource, component.Component):
except Exception as ex:
log.error('Error calling method `%s`: %s', method, ex)
log.exception(ex)
- error = {'message': '%s: %s' % (ex.__class__.__name__, str(ex)), 'code': 3}
+ error = {'message': f'{ex.__class__.__name__}: {str(ex)}', 'code': 3}
return request_id, result, error
@@ -184,7 +181,7 @@ class JSON(resource.Resource, component.Component):
"""
log.error(reason)
response['error'] = {
- 'message': '%s: %s' % (reason.__class__.__name__, str(reason)),
+ 'message': f'{reason.__class__.__name__}: {str(reason)}',
'code': 4,
}
return self._send_response(request, response)
@@ -194,7 +191,7 @@ class JSON(resource.Resource, component.Component):
Handler to take the json data as a string and pass it on to the
_handle_request method for further processing.
"""
- content_type = request.getHeader(b'content-type').decode()
+ content_type, _ = cgi.parse_header(request.getHeader(b'content-type').decode())
if content_type != 'application/json':
message = 'Invalid JSON request content-type: %s' % content_type
raise JSONException(message)
@@ -221,7 +218,7 @@ class JSON(resource.Resource, component.Component):
'id': None,
'error': {
'code': 5,
- 'message': '%s: %s' % (reason.__class__.__name__, str(reason)),
+ 'message': f'{reason.__class__.__name__}: {str(reason)}',
},
}
return self._send_response(request, response)
@@ -288,7 +285,7 @@ class JSON(resource.Resource, component.Component):
FILES_KEYS = ['files', 'file_progress', 'file_priorities']
-class EventQueue(object):
+class EventQueue:
"""
This class subscribes to events from the core and stores them until all
the subscribed listeners have received the events.
@@ -378,10 +375,8 @@ class WebApi(JSONComponent):
methods available from the core RPC.
"""
- XSS_VULN_KEYS = ['name', 'message', 'comment', 'tracker_status', 'peers']
-
def __init__(self):
- super(WebApi, self).__init__('Web', depend=['SessionProxy'])
+ super().__init__('Web', depend=['SessionProxy'])
self.hostlist = HostList()
self.core_config = CoreConfig()
self.event_queue = EventQueue()
@@ -518,7 +513,7 @@ class WebApi(JSONComponent):
return d
def got_stats(stats):
- ui_info['stats']['num_connections'] = stats['num_peers']
+ ui_info['stats']['num_connections'] = stats['peer.num_peers_connected']
ui_info['stats']['upload_rate'] = stats['payload_upload_rate']
ui_info['stats']['download_rate'] = stats['payload_download_rate']
ui_info['stats']['download_protocol_rate'] = (
@@ -527,9 +522,9 @@ class WebApi(JSONComponent):
ui_info['stats']['upload_protocol_rate'] = (
stats['upload_rate'] - stats['payload_upload_rate']
)
- ui_info['stats']['dht_nodes'] = stats['dht_nodes']
+ ui_info['stats']['dht_nodes'] = stats['dht.dht_nodes']
ui_info['stats']['has_incoming_connections'] = stats[
- 'has_incoming_connections'
+ 'net.has_incoming_connections'
]
def got_filters(filters):
@@ -555,13 +550,13 @@ class WebApi(JSONComponent):
d3 = client.core.get_session_status(
[
- 'num_peers',
+ 'peer.num_peers_connected',
'payload_download_rate',
'payload_upload_rate',
'download_rate',
'upload_rate',
- 'dht_nodes',
- 'has_incoming_connections',
+ 'dht.dht_nodes',
+ 'net.has_incoming_connections',
]
)
d3.addCallback(got_stats)
@@ -584,7 +579,7 @@ class WebApi(JSONComponent):
paths = []
info = {}
for index, torrent_file in enumerate(files):
- path = xml_escape(torrent_file['path'])
+ path = torrent_file['path']
paths.append(path)
torrent_file['progress'] = file_progress[index]
torrent_file['priority'] = file_priorities[index]
@@ -605,7 +600,10 @@ class WebApi(JSONComponent):
progresses = dirinfo.setdefault('progresses', [])
progresses.append(torrent_file['size'] * torrent_file['progress'] / 100)
- dirinfo['progress'] = sum(progresses) / dirinfo['size'] * 100
+ if dirinfo['size'] > 0:
+ dirinfo['progress'] = sum(progresses) / dirinfo['size'] * 100
+ else:
+ dirinfo['progress'] = 100
dirinfo['path'] = dirname
dirname = os.path.dirname(dirname)
@@ -621,25 +619,10 @@ class WebApi(JSONComponent):
file_tree.walk(walk)
d.callback(file_tree.get_tree())
- def _on_torrent_status(self, torrent, d):
- for key in self.XSS_VULN_KEYS:
- try:
- if key == 'peers':
- for peer in torrent[key]:
- peer['client'] = xml_escape(peer['client'])
- else:
- torrent[key] = xml_escape(torrent[key])
- except KeyError:
- pass
- d.callback(torrent)
-
@export
def get_torrent_status(self, torrent_id, keys):
"""Get the status for a torrent, filtered by status keys."""
- main_deferred = Deferred()
- d = component.get('SessionProxy').get_torrent_status(torrent_id, keys)
- d.addCallback(self._on_torrent_status, main_deferred)
- return main_deferred
+ return component.get('SessionProxy').get_torrent_status(torrent_id, keys)
@export
def get_torrent_files(self, torrent_id):
@@ -999,6 +982,16 @@ class WebApi(JSONComponent):
"""
return self.event_queue.get_events(__request__.session_id)
+ @export
+ def set_theme(self, theme):
+ """
+ Sets a new Theme to the WebUI
+
+ Args:
+ theme (str): the theme to apply
+ """
+ component.get('DelugeWeb').set_theme(theme)
+
class WebUtils(JSONComponent):
"""
@@ -1006,7 +999,7 @@ class WebUtils(JSONComponent):
"""
def __init__(self):
- super(WebUtils, self).__init__('WebUtils')
+ super().__init__('WebUtils')
@export
def get_languages(self):
@@ -1017,3 +1010,13 @@ class WebUtils(JSONComponent):
list: of tuples ``[(lang-id, language-name), ...]``
"""
return get_languages()
+
+ @export
+ def get_themes(self):
+ """
+ Get the available themes
+
+ Returns:
+ list: of themes ``[theme1, theme2, ...]``
+ """
+ return component.get('DelugeWeb').get_themes()
diff --git a/deluge/ui/web/pluginmanager.py b/deluge/ui/web/pluginmanager.py
index 24f20ce94..2da5b6177 100644
--- a/deluge/ui/web/pluginmanager.py
+++ b/deluge/ui/web/pluginmanager.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import logging
import os
@@ -74,22 +71,20 @@ class PluginManager(PluginManagerBase, component.Component):
scripts = component.get('Scripts')
for script in info['scripts']:
- scripts.remove_script(
- '%s/%s' % (name.lower(), os.path.basename(script).lower())
- )
+ scripts.remove_script(f'{name.lower()}/{os.path.basename(script).lower()}')
for script in info['debug_scripts']:
scripts.remove_script(
- '%s/%s' % (name.lower(), os.path.basename(script).lower()), 'debug'
+ f'{name.lower()}/{os.path.basename(script).lower()}', 'debug'
)
scripts.remove_script(
- '%s/%s' % (name.lower(), os.path.basename(script).lower()), 'dev'
+ f'{name.lower()}/{os.path.basename(script).lower()}', 'dev'
)
- super(PluginManager, self).disable_plugin(name)
+ super().disable_plugin(name)
def enable_plugin(self, name):
- super(PluginManager, self).enable_plugin(name)
+ super().enable_plugin(name)
# Get the plugin instance
try:
@@ -105,17 +100,15 @@ class PluginManager(PluginManagerBase, component.Component):
scripts = component.get('Scripts')
for script in info['scripts']:
log.debug('adding script %s for %s', name, os.path.basename(script))
- scripts.add_script(
- '%s/%s' % (name.lower(), os.path.basename(script)), script
- )
+ scripts.add_script(f'{name.lower()}/{os.path.basename(script)}', script)
for script in info['debug_scripts']:
log.debug('adding debug script %s for %s', name, os.path.basename(script))
scripts.add_script(
- '%s/%s' % (name.lower(), os.path.basename(script)), script, 'debug'
+ f'{name.lower()}/{os.path.basename(script)}', script, 'debug'
)
scripts.add_script(
- '%s/%s' % (name.lower(), os.path.basename(script)), script, 'dev'
+ f'{name.lower()}/{os.path.basename(script)}', script, 'dev'
)
def start(self):
@@ -151,11 +144,10 @@ class PluginManager(PluginManagerBase, component.Component):
info = gather_info(plugin)
info['name'] = name
info['scripts'] = [
- 'js/%s/%s' % (name.lower(), os.path.basename(s)) for s in info['scripts']
+ f'js/{name.lower()}/{os.path.basename(s)}' for s in info['scripts']
]
info['debug_scripts'] = [
- 'js/%s/%s' % (name.lower(), os.path.basename(s))
- for s in info['debug_scripts']
+ f'js/{name.lower()}/{os.path.basename(s)}' for s in info['debug_scripts']
]
del info['script_directories']
return info
diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py
index d730e72e9..5fbdd4eae 100644
--- a/deluge/ui/web/server.py
+++ b/deluge/ui/web/server.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Damien Churchill <damoxc@gmail.com>
#
@@ -7,14 +6,13 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import fnmatch
import json
import logging
import mimetypes
import os
import tempfile
+from pathlib import Path
from twisted.application import internet, service
from twisted.internet import defer, reactor
@@ -23,8 +21,7 @@ from twisted.web.resource import EncodingResourceWrapper
from deluge import common, component, configmanager
from deluge.common import is_ipv6
-from deluge.core.rpcserver import check_ssl_keys
-from deluge.crypto_utils import get_context_factory
+from deluge.crypto_utils import check_ssl_keys, get_context_factory
from deluge.i18n import set_language, setup_translation
from deluge.ui.tracker_icons import TrackerIcons
from deluge.ui.web.auth import Auth
@@ -77,6 +74,20 @@ def rpath(*paths):
return common.resource_filename('deluge.ui.web', os.path.join(*paths))
+def absolute_base_url(base):
+ """Returns base as absolute URL for links"""
+ if not base:
+ base = '/'
+
+ if not base.startswith('/'):
+ base = '/' + base
+
+ if not base.endswith('/'):
+ base += '/'
+
+ return base
+
+
class GetText(resource.Resource):
def render(self, request):
request.setHeader(b'content-type', b'text/javascript; encoding=utf-8')
@@ -131,14 +142,12 @@ class Upload(resource.Resource):
request.setHeader(b'content-type', b'text/html')
request.setResponseCode(http.OK)
- return json.dumps({'success': bool(filenames), 'files': filenames}).encode(
- 'utf8'
- )
+ return json.dumps({'success': bool(filenames), 'files': filenames}).encode()
class Render(resource.Resource):
def __init__(self):
- resource.Resource.__init__(self)
+ super().__init__()
# Make a list of all the template files to check requests against.
self.template_files = fnmatch.filter(os.listdir(rpath('render')), '*.html')
@@ -167,7 +176,7 @@ class Render(resource.Resource):
class Tracker(resource.Resource):
def __init__(self):
- resource.Resource.__init__(self)
+ super().__init__()
try:
self.tracker_icons = component.get('TrackerIcons')
except KeyError:
@@ -182,7 +191,7 @@ class Tracker(resource.Resource):
request.setHeader(
b'cache-control', b'public, must-revalidate, max-age=86400'
)
- request.setHeader(b'content-type', icon.get_mimetype().encode('utf8'))
+ request.setHeader(b'content-type', icon.get_mimetype().encode())
request.setResponseCode(http.OK)
request.write(icon.get_data())
request.finish()
@@ -202,7 +211,7 @@ class Flag(resource.Resource):
return self
def render(self, request):
- flag = request.country.decode('utf-8').lower() + '.png'
+ flag = request.country.decode().lower() + '.png'
path = ('ui', 'data', 'pixmaps', 'flags', flag)
filename = common.resource_filename('deluge', os.path.join(*path))
if os.path.exists(filename):
@@ -376,7 +385,7 @@ class ScriptResource(resource.Resource, component.Component):
order_file = os.path.join(root, '.order')
if os.path.isfile(order_file):
- with open(order_file, 'r') as _file:
+ with open(order_file) as _file:
for line in _file:
if line.startswith('+ '):
order_filename = line.split()[1]
@@ -451,7 +460,6 @@ class Themes(static.File):
class TopLevel(resource.Resource):
-
__stylesheets = [
'css/ext-all-notheme.css',
'css/ext-extensions.css',
@@ -459,7 +467,7 @@ class TopLevel(resource.Resource):
]
def __init__(self):
- resource.Resource.__init__(self)
+ super().__init__()
self.putChild(b'css', LookupResource('Css', rpath('css')))
if os.path.isfile(rpath('js', 'gettext.js')):
@@ -479,7 +487,8 @@ class TopLevel(resource.Resource):
self.putChild(
b'ui_images',
LookupResource(
- 'UI_Images', common.resource_filename('deluge.ui.data', 'pixmaps')
+ 'UI_Images',
+ common.resource_filename('deluge.ui', os.path.join('data', 'pixmaps')),
),
)
@@ -531,15 +540,28 @@ class TopLevel(resource.Resource):
self.putChild(b'themes', Themes(rpath('themes')))
self.putChild(b'tracker', Tracker())
- theme = component.get('DelugeWeb').config['theme']
- if not os.path.isfile(rpath('themes', 'css', 'xtheme-%s.css' % theme)):
- theme = CONFIG_DEFAULTS.get('theme')
- self.__stylesheets.insert(1, 'themes/css/xtheme-%s.css' % theme)
-
@property
def stylesheets(self):
return self.__stylesheets
+ def get_themes(self):
+ themes_dir = Path(rpath('themes', 'css'))
+ themes = [
+ theme.stem.split('xtheme-')[1] for theme in themes_dir.glob('xtheme-*.css')
+ ]
+ themes = [(theme, _(theme.capitalize())) for theme in themes]
+ return themes
+
+ def set_theme(self, theme: str):
+ if not os.path.isfile(rpath('themes', 'css', f'xtheme-{theme}.css')):
+ theme = CONFIG_DEFAULTS.get('theme')
+ self.__theme = f'themes/css/xtheme-{theme}.css'
+
+ # Only one xtheme CSS, ordered last to override other styles.
+ if 'xtheme-' in self.stylesheets[-1]:
+ self.__stylesheets.pop()
+ self.__stylesheets.append(self.__theme)
+
def add_script(self, script):
"""
Adds a script to the server so it is included in the <head> element
@@ -562,43 +584,34 @@ class TopLevel(resource.Resource):
self.__scripts.remove(script)
self.__debug_scripts.remove(script)
- def getChild(self, path, request): # NOQA: N802
- if not path:
- return self
- else:
- return resource.Resource.getChild(self, path, request)
-
def getChildWithDefault(self, path, request): # NOQA: N802
# Calculate the request base
- header = request.getHeader(b'x-deluge-base')
- base = header.decode('utf-8') if header else component.get('DelugeWeb').base
-
- # validate the base parameter
- if not base:
- base = '/'
+ header = request.getHeader('x-deluge-base')
+ config_base = component.get('DelugeWeb').base
+ base = header if header else config_base
- if base[0] != '/':
- base = '/' + base
+ first_request = not hasattr(request, 'base')
+ request.base = absolute_base_url(base).encode()
- if base[-1] != '/':
- base += '/'
+ base_resource = first_request and path.decode() == config_base.strip('/')
- request.base = base.encode('utf-8')
+ if not path or base_resource:
+ return self
- return resource.Resource.getChildWithDefault(self, path, request)
+ return super().getChildWithDefault(path, request)
def render(self, request):
uri_true = ('true', 'yes', 'on', '1')
uri_false = ('false', 'no', 'off', '0')
debug_arg = None
- req_dbg_arg = request.args.get('debug', [b''])[-1].decode().lower()
+ req_dbg_arg = request.args.get(b'debug', [b''])[-1].decode().lower()
if req_dbg_arg in uri_true:
debug_arg = True
elif req_dbg_arg in uri_false:
debug_arg = False
- dev_arg = request.args.get('dev', [b''])[-1].decode().lower() in uri_true
+ dev_arg = request.args.get(b'dev', [b''])[-1].decode().lower() in uri_true
dev_ver = 'dev' in common.get_version()
script_type = 'normal'
@@ -653,11 +666,11 @@ class DelugeWeb(component.Component):
Args:
options (argparse.Namespace): The web server options.
- daemon (bool): If True run web server as a seperate daemon process (starts a twisted
+ daemon (bool): If True run web server as a separate daemon process (starts a twisted
reactor). If False shares the process and twisted reactor from WebUI plugin or tests.
"""
- component.Component.__init__(self, 'DelugeWeb', depend=['Web'])
+ super().__init__('DelugeWeb', depend=['Web'])
self.config = configmanager.ConfigManager(
'web.conf', defaults=CONFIG_DEFAULTS, file_version=2
)
@@ -684,9 +697,7 @@ class DelugeWeb(component.Component):
elif options.no_ssl:
self.https = False
- if self.base != '/':
- # Strip away slashes and serve on the base path as well as root path
- self.top_level.putChild(self.base.strip('/'), self.top_level)
+ self.top_level.set_theme(self.config['theme'])
setup_translation()
@@ -698,7 +709,7 @@ class DelugeWeb(component.Component):
self.auth = Auth(self.config)
self.daemon = daemon
- # Initalize the plugins
+ # Initialize the plugins
self.plugins = PluginManager()
def _on_language_changed(self, key, value):
@@ -746,8 +757,8 @@ class DelugeWeb(component.Component):
def start_normal(self):
self.socket = reactor.listenTCP(self.port, self.site, interface=self.interface)
ip = self.socket.getHost().host
- ip = '[%s]' % ip if is_ipv6(ip) else ip
- log.info('Serving at http://%s:%s%s', ip, self.port, self.base)
+ ip = f'[{ip}]' if is_ipv6(ip) else ip
+ log.info(f'Serving at http://{ip}:{self.port}{self.base}')
def start_ssl(self):
check_ssl_keys()
@@ -763,8 +774,8 @@ class DelugeWeb(component.Component):
interface=self.interface,
)
ip = self.socket.getHost().host
- ip = '[%s]' % ip if is_ipv6(ip) else ip
- log.info('Serving at https://%s:%s%s', ip, self.port, self.base)
+ ip = f'[{ip}]' if is_ipv6(ip) else ip
+ log.info(f'Serving at https://{ip}:{self.port}{self.base}')
def stop(self):
log.info('Shutting down webserver')
@@ -794,6 +805,12 @@ class DelugeWeb(component.Component):
config['language'] = CONFIG_DEFAULTS['language']
return config
+ def get_themes(self):
+ return self.top_level.get_themes()
+
+ def set_theme(self, theme: str):
+ self.top_level.set_theme(theme)
+
if __name__ == '__builtin__':
deluge_web = DelugeWeb()
diff --git a/deluge/ui/web/themes/images/access/grid/hmenu-lock.png b/deluge/ui/web/themes/images/access/grid/hmenu-lock.png
index 5d33c09d9..194756e9f 100644
--- a/deluge/ui/web/themes/images/access/grid/hmenu-lock.png
+++ b/deluge/ui/web/themes/images/access/grid/hmenu-lock.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/access/grid/hmenu-unlock.png b/deluge/ui/web/themes/images/access/grid/hmenu-unlock.png
index 0371ca47e..435bd74fe 100644
--- a/deluge/ui/web/themes/images/access/grid/hmenu-unlock.png
+++ b/deluge/ui/web/themes/images/access/grid/hmenu-unlock.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/access/slider/slider-bg.png b/deluge/ui/web/themes/images/access/slider/slider-bg.png
index 1ba12ad94..d645fa8ae 100644
--- a/deluge/ui/web/themes/images/access/slider/slider-bg.png
+++ b/deluge/ui/web/themes/images/access/slider/slider-bg.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/access/slider/slider-thumb.png b/deluge/ui/web/themes/images/access/slider/slider-thumb.png
index 1712edfd2..99e0e14e1 100644
--- a/deluge/ui/web/themes/images/access/slider/slider-thumb.png
+++ b/deluge/ui/web/themes/images/access/slider/slider-thumb.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/access/slider/slider-v-thumb.png b/deluge/ui/web/themes/images/access/slider/slider-v-thumb.png
index df9cfc96b..cfae8cd8e 100644
--- a/deluge/ui/web/themes/images/access/slider/slider-v-thumb.png
+++ b/deluge/ui/web/themes/images/access/slider/slider-v-thumb.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/access/window/right-corners.png b/deluge/ui/web/themes/images/access/window/right-corners.png
index 1111bb4de..e3faa016b 100644
--- a/deluge/ui/web/themes/images/access/window/right-corners.png
+++ b/deluge/ui/web/themes/images/access/window/right-corners.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/access/window/top-bottom.png b/deluge/ui/web/themes/images/access/window/top-bottom.png
index 179747b2b..26d74212d 100644
--- a/deluge/ui/web/themes/images/access/window/top-bottom.png
+++ b/deluge/ui/web/themes/images/access/window/top-bottom.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/grid/hmenu-lock.png b/deluge/ui/web/themes/images/default/grid/hmenu-lock.png
index 5d33c09d9..194756e9f 100644
--- a/deluge/ui/web/themes/images/default/grid/hmenu-lock.png
+++ b/deluge/ui/web/themes/images/default/grid/hmenu-lock.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/grid/hmenu-unlock.png b/deluge/ui/web/themes/images/default/grid/hmenu-unlock.png
index 0371ca47e..435bd74fe 100644
--- a/deluge/ui/web/themes/images/default/grid/hmenu-unlock.png
+++ b/deluge/ui/web/themes/images/default/grid/hmenu-unlock.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/slider/slider-bg.png b/deluge/ui/web/themes/images/default/slider/slider-bg.png
index 6825d7357..ca5009390 100644
--- a/deluge/ui/web/themes/images/default/slider/slider-bg.png
+++ b/deluge/ui/web/themes/images/default/slider/slider-bg.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/slider/slider-thumb.png b/deluge/ui/web/themes/images/default/slider/slider-thumb.png
index d16ed52eb..c1abf95b5 100644
--- a/deluge/ui/web/themes/images/default/slider/slider-thumb.png
+++ b/deluge/ui/web/themes/images/default/slider/slider-thumb.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/slider/slider-v-thumb.png b/deluge/ui/web/themes/images/default/slider/slider-v-thumb.png
index cc86cb7ed..c80e1458b 100644
--- a/deluge/ui/web/themes/images/default/slider/slider-v-thumb.png
+++ b/deluge/ui/web/themes/images/default/slider/slider-v-thumb.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/window/right-corners.png b/deluge/ui/web/themes/images/default/window/right-corners.png
index 7c400076b..2440d2ffd 100644
--- a/deluge/ui/web/themes/images/default/window/right-corners.png
+++ b/deluge/ui/web/themes/images/default/window/right-corners.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/default/window/top-bottom.png b/deluge/ui/web/themes/images/default/window/top-bottom.png
index 3b5f86b25..d6428270f 100644
--- a/deluge/ui/web/themes/images/default/window/top-bottom.png
+++ b/deluge/ui/web/themes/images/default/window/top-bottom.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/gray/slider/slider-thumb.png b/deluge/ui/web/themes/images/gray/slider/slider-thumb.png
index 6ec16677e..ebe1b2ec3 100644
--- a/deluge/ui/web/themes/images/gray/slider/slider-thumb.png
+++ b/deluge/ui/web/themes/images/gray/slider/slider-thumb.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/gray/window/left-corners.png b/deluge/ui/web/themes/images/gray/window/left-corners.png
index f9f44ad09..a32c8064e 100644
--- a/deluge/ui/web/themes/images/gray/window/left-corners.png
+++ b/deluge/ui/web/themes/images/gray/window/left-corners.png
Binary files differ
diff --git a/deluge/ui/web/themes/images/gray/window/right-corners.png b/deluge/ui/web/themes/images/gray/window/right-corners.png
index e0a58d39a..fed4d38fa 100644
--- a/deluge/ui/web/themes/images/gray/window/right-corners.png
+++ b/deluge/ui/web/themes/images/gray/window/right-corners.png
Binary files differ
diff --git a/deluge/ui/web/web.py b/deluge/ui/web/web.py
index 4d0624791..ae428c754 100644
--- a/deluge/ui/web/web.py
+++ b/deluge/ui/web/web.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
#
@@ -7,8 +6,6 @@
# See LICENSE for more details.
#
-from __future__ import print_function, unicode_literals
-
import logging
from twisted.internet.error import CannotListenError
@@ -20,11 +17,10 @@ log = logging.getLogger(__name__)
class Web(UI):
-
cmd_description = """Web-based user interface (http://localhost:8112)"""
def __init__(self, *args, **kwargs):
- super(Web, self).__init__(
+ super().__init__(
'web', *args, description='Starts the Deluge Web interface', **kwargs
)
self.__server = None
@@ -67,7 +63,7 @@ class Web(UI):
return self.__server
def start(self):
- super(Web, self).start()
+ super().start()
from deluge.ui.web import server
diff --git a/docs/man/deluge-console.1 b/docs/man/deluge-console.1
index 75ab1f0bf..edffba5ac 100644
--- a/docs/man/deluge-console.1
+++ b/docs/man/deluge-console.1
@@ -10,7 +10,7 @@ deluge-console - A BitTorrent client console interface
Deluge utilizes a client/server model, with \fBdeluged\fR being the daemon process and \fBdeluge-console\fR being used to launch a curses console user-interface.
.P
.SS Console Commands:
-You can pass console commands directly from the command line and use semi-colon (\fB;\fR) seperator to run multiple commands. Enclosing the commands with quotes may also be required
+You can pass console commands directly from the command line and use semi-colon (\fB;\fR) separator to run multiple commands. Enclosing the commands with quotes may also be required
for example:
\fBdeluge-console 'add <torrent>; info <torrent_id>'\fR
diff --git a/docs/requirements.txt b/docs/requirements.txt
index c8a0d0546..3da1967f8 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,5 @@
-sphinx==2.0.*
-recommonmark==0.4.*
-sphinx_rtd_theme
-sphinxcontrib-spelling
+sphinx==7.2.*
+myst-parser==2.0.*
+sphinx_rtd_theme==2.0.*
+sphinxcontrib-spelling==8.0.*
+sphinx-autodoc-typehints==1.25.*
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 450e802aa..0e4a41914 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Deluge documentation build configuration file
#
@@ -10,13 +9,11 @@
# All configuration values have a default value; values that are commented out
# serve to show the default value.
+import builtins
import os
import sys
from datetime import date
-from recommonmark.states import DummyStateMachine
-from recommonmark.transform import AutoStructify
-from six.moves import builtins
from sphinx.ext import apidoc
from sphinx.ext.autodoc import ClassDocumenter, bool_option
@@ -50,6 +47,8 @@ extensions = [
'sphinx.ext.napoleon',
'sphinx.ext.coverage',
'sphinxcontrib.spelling',
+ 'myst_parser',
+ 'sphinx_autodoc_typehints',
]
napoleon_include_init_with_doc = True
@@ -59,7 +58,6 @@ napoleon_use_rtype = False
templates_path = ['_templates']
# The suffix of source filenames.
-source_parsers = {'.md': 'recommonmark.parser.CommonMarkParser'}
source_suffix = ['.rst', '.md']
# The master toctree document.
@@ -221,45 +219,13 @@ latex_documents = [
# Autodoc section
# ---------------
-class Mock(object):
- __all__ = []
-
- def __init__(self, *args, **kwargs):
- pass
-
- def __call__(self, *args, **kwargs):
- return ''
-
- @classmethod
- def __getattr__(cls, name):
- if name in ('__file__', '__path__', 'xdg_config_home'):
- return '/dev/null'
- elif name[0] == name[0].upper():
- mock_type = type(name, (), {})
- mock_type.__module__ = __name__
- return mock_type
- else:
- return Mock()
-
- def __add__(self, other):
- return other
-
- def __or__(self, __):
- return Mock()
-
-
-# Use custom mock as autodoc_mock_imports fails to handle these modules.
-MOCK_MODULES = ['deluge._libtorrent', 'xdg', 'xdg.BaseDirectory']
-
-for mod_name in MOCK_MODULES:
- sys.modules[mod_name] = Mock()
-
-# Must add these for autodoc to import packages successully
+# Must add these for autodoc to import packages successfully
builtins.__dict__['_'] = lambda x: x
builtins.__dict__['_n'] = lambda s, p, n: s if n == 1 else p
autodoc_mock_imports = [
+ 'deluge._libtorrent',
'twisted',
'rencode',
'OpenSSL',
@@ -295,19 +261,6 @@ def maybe_skip_member(app, what, name, obj, skip, options):
return True
-# Monkey patch to fix recommonmark 0.4 doc reference issues.
-orig_run_role = DummyStateMachine.run_role
-
-
-def run_role(self, name, options=None, content=None):
- if name == 'doc':
- name = 'any'
- return orig_run_role(self, name, options, content)
-
-
-DummyStateMachine.run_role = run_role
-
-
# Run the sphinx-apidoc to create package/modules rst files for autodoc.
def run_apidoc(__):
cur_dir = os.path.abspath(os.path.dirname(__file__))
@@ -329,5 +282,3 @@ def run_apidoc(__):
def setup(app):
app.connect('builder-inited', run_apidoc)
app.connect('autodoc-skip-member', maybe_skip_member)
- app.add_config_value('recommonmark_config', {}, True)
- app.add_transform(AutoStructify)
diff --git a/docs/source/contributing/index.md b/docs/source/contributing/index.md
index afdead24d..8c07a76b1 100644
--- a/docs/source/contributing/index.md
+++ b/docs/source/contributing/index.md
@@ -3,7 +3,11 @@
Deluge is an open-source project, and relies on its community of users to keep
getting better.
-- [Code contributions](code.md)
-- [Running tests](testing.md)
-- [Documentation contributions](documentation.md)
-- [Translation contributions](translations.md)
+```{toctree}
+:titlesonly:
+
+code
+testing
+documentation
+translations
+```
diff --git a/docs/source/contributing/testing.md b/docs/source/contributing/testing.md
index 8d9d1f31d..beb30a41c 100644
--- a/docs/source/contributing/testing.md
+++ b/docs/source/contributing/testing.md
@@ -1,10 +1,6 @@
# Running tests
-Deluge testing is implemented using Trial which is Twisted's testing framework
-and an extension of Python's unittest.
-
-See Twisted website for documentation on [Twisted Trial](http://twistedmatrix.com/trac/wiki/TwistedTrial)
-and [Writing tests using Trial](http://twistedmatrix.com/documents/current/core/howto/testing.html).
+Testing uses [PyTest] framework and [PyTest-Twisted] to handle Twisted framework.
## Testing
@@ -12,16 +8,6 @@ The tests are located in the source folder under `deluge/tests`.
The tests are run from the project root directory.
View the unit test coverage at: [deluge-torrent.github.io](http://deluge-torrent.github.io)
-### Trial
-
-Here are some examples that show running all the tests through to selecting an
-individual test.
-
- trial deluge
- trial deluge.tests.test_client
- trial deluge.tests.test_client.ClientTestCase
- trial deluge.tests.test_client.ClientTestCase.test_connect_localclient
-
### Pytest
pytest deluge/tests
@@ -38,27 +24,27 @@ Running the tests for a specific plugin (requires [pytest](https://pypi.python.o
All the tests for Deluge can be run using [Tox](https://pypi.python.org/pypi/tox)
-#### See available targets:
+### See available targets:
tox -l
- py27
py3
lint
docs
-#### Run specific test:
+### Run specific test:
tox -e py3
-#### Verify code with pre-commit:
+### Verify code with pre-commit:
tox -e lint
-## Travis CI
+## CI
+
+Deluge develop branch is tested automatically by GitHub actions.
-Deluge develop branch is tested automatically by [Travis].
-When creating a pull request (PR) on [github], Travis will automatically run
-the unit tests with the code in the PR.
+When creating a pull request (PR) on [github], units tests will be automatically be run.
-[travis]: https://travis-ci.org/deluge-torrent/deluge
[github]: https://github.com/deluge-torrent/deluge/pulls
+[pytest]: https://docs.pytest.org/en/
+[pytest-twisted]: https://github.com/pytest-dev/pytest-twisted
diff --git a/docs/source/depends.md b/docs/source/depends.md
new file mode 120000
index 000000000..974bd08b5
--- /dev/null
+++ b/docs/source/depends.md
@@ -0,0 +1 @@
+../../DEPENDS.md \ No newline at end of file
diff --git a/docs/source/devguide/how-to/index.md b/docs/source/devguide/how-to/index.md
index 93a43fce1..e63c7713b 100644
--- a/docs/source/devguide/how-to/index.md
+++ b/docs/source/devguide/how-to/index.md
@@ -4,7 +4,11 @@ A collection of guides for specific issues or to cover more detail than the tuto
## Web JSON-RPC
-- [Connect to JSON-RPC using curl](curl-jsonrpc.md)
+```{toctree}
+:titlesonly:
+
+Connect to JSON-RPC using curl <curl-jsonrpc>
+```
## Plugins
@@ -12,4 +16,8 @@ A collection of guides for specific issues or to cover more detail than the tuto
- [Create a plugin](create-plugin.md)
-->
-- [Update 1.3 plugin for 2.0](update-1.3-plugin.md)
+```{toctree}
+:titlesonly:
+
+Update 1.3 plugin for 2.0 <update-1.3-plugin>
+```
diff --git a/docs/source/devguide/how-to/update-1.3-plugin.md b/docs/source/devguide/how-to/update-1.3-plugin.md
index 6374a0c0d..9ce6ae14c 100644
--- a/docs/source/devguide/how-to/update-1.3-plugin.md
+++ b/docs/source/devguide/how-to/update-1.3-plugin.md
@@ -11,7 +11,7 @@ compatible with 2.0 and this guide aims to helps with that process.
### Python version matching
-Ensure your code is both Python 2.7 and Python >=3.5 compatible.
+Ensure your code is Python >=3.6 compatible.
In `1.3-stable` the plugins that were built with a specific version of Python
would only be loaded if the system Python also matched.
@@ -19,19 +19,6 @@ would only be loaded if the system Python also matched.
This has change in Deluge 2.0 and it will load any Python version of plugin
eggs so compatibility is essential for end-users not to encounter issues.
-### Six
-
-Use [six] to assist with compatibility.
-
-[six]: https://pythonhosted.org/six/
-
-### Unicode literals
-
-Add the following to files to ensure strings and bytes separation so there
-are no surprises when running on Python 3.
-
- from __future__ import unicode_literals
-
## GTK 3 addition
In order to support both Deluge 1.3 and 2.0 all existing plugin GTK UI files
diff --git a/docs/source/devguide/index.md b/docs/source/devguide/index.md
index bde04b3e0..436a72a11 100644
--- a/docs/source/devguide/index.md
+++ b/docs/source/devguide/index.md
@@ -2,7 +2,12 @@
This is a guide to help with developing Deluge.
-- [Tutorials](tutorials/index.md)
-- [How-to guides](how-to/index.md)
-- [Packaging](packaging/index.md)
-- [Changelog](../changelog.md)
+```{toctree}
+:titlesonly:
+
+Tutorials <tutorials/index>
+how-to/index
+Packaging <packaging/index>
+../changelog
+../depends
+```
diff --git a/docs/source/devguide/packaging/index.md b/docs/source/devguide/packaging/index.md
index 9da6a27d7..aa7293ab4 100644
--- a/docs/source/devguide/packaging/index.md
+++ b/docs/source/devguide/packaging/index.md
@@ -1,5 +1,9 @@
# Packaging documentation
-- [Release checklist](release.md)
-- [Launchpad recipe](launchpad-recipe.md)
-- [Windows Packaging](windows.md)
+```{toctree}
+:titlesonly:
+
+release
+launchpad-recipe
+windows
+```
diff --git a/docs/source/devguide/packaging/release.md b/docs/source/devguide/packaging/release.md
index 892b5fcde..e02a09af3 100644
--- a/docs/source/devguide/packaging/release.md
+++ b/docs/source/devguide/packaging/release.md
@@ -15,7 +15,7 @@
python setup.py sdist bdist_wheel
-- Upload to PyPi (currently only accepts `tar.gz`):
+- Upload to PyPi (only accepts `tar.gz`):
twine upload dist/deluge-2.0.0.tar.gz dist/deluge-2.0.0-py3-none-any.whl
@@ -23,21 +23,26 @@
cd dist; sha256sum deluge-2.0.0.tar.xz > deluge-2.0.0.tar.xz.sha256
-- Upload source tarballs and packages to `download.deluge-torrent.org`.
+- Upload source tarballs and packages to `ftp-osl.osuosl.org`.
+
- Ensure file permissions are global readable: `0644`
- Sub-directories correspond to _major.minor_ version e.g. all `2.0.x` patch
releases are stored in `source/2.0`.
- - Change release version in `version` files.
- - Run `trigger-deluge` to sync OSUOSL FTP site.
+ - Change release version in `version*` files, create a new version file for
+ any major releases.
+ - SSH into OSUOSL FTP site and run `trigger-deluge` to sync files.
+
- Create packages (Ubuntu, Windows, OSX).
- Ubuntu: <https://code.launchpad.net/~deluge-team/+recipe/stable-releases>
+ - Ensure launchpad git repo has sync'd to get latest version
+ - Update version in recipe (reset any dash number to 0)
+ - Check for new distribution series needing selected.
+ - Request build for selected series.
## Post-release
- Update with version, hashes and release notes:
- - Publish docs on [ReadTheDocs].
- - Forum announcement.
- - IRC welcome message.
+ - Publish new docs version on [ReadTheDocs].
- [Wikipedia]
- Close Trac milestone and add new milestone version for future tickets.
- Ensure all stable branch commits are also applied to development branch.
@@ -46,4 +51,4 @@
[wikipedia]: http://en.wikipedia.org/wiki/Deluge_%28software%29
[launchpad]: https://translations.launchpad.net/deluge
[translation]: ../../contributing/translations.md
-[release notes]: ../../release/index.md
+[release notes]: ../../releases/index.md
diff --git a/docs/source/devguide/tutorials/index.md b/docs/source/devguide/tutorials/index.md
index 36a8abf84..2a4725231 100644
--- a/docs/source/devguide/tutorials/index.md
+++ b/docs/source/devguide/tutorials/index.md
@@ -2,4 +2,9 @@
A list of articles to help developers get started with Deluge.
-- [Development setup](01-setup.md)
+```{toctree}
+:numbered: 1
+:titlesonly:
+
+Development setup <01-setup>
+```
diff --git a/docs/source/how-to/index.md b/docs/source/how-to/index.md
index 9ef1ddde6..7005a3414 100644
--- a/docs/source/how-to/index.md
+++ b/docs/source/how-to/index.md
@@ -4,7 +4,11 @@ A collection of guides covering common issues that might be encountered using De
## GTK UI
-- [Set default torrent application](set-mime-type.md)
+```{toctree}
+:titlesonly:
+
+Set default torrent application <set-mime-type>
+```
## Deluge as a service
@@ -14,6 +18,10 @@ shutdown and automatically restart them if they crash.
The Deluge daemon deluged and Web UI deluge-web can both be run as services.
-- [Create systemd services for Linux](systemd-service.md)
-- [Create launchd services for macOS](launchd-service.md)
-- [Create NSSM services for Windows](nssm-service.md)
+```{toctree}
+:titlesonly:
+
+Create systemd services for Linux <systemd-service>
+Create launchd services for macOS <launchd-service>
+Create NSSM services for Windows <nssm-service>
+```
diff --git a/docs/source/how-to/launchd-service.md b/docs/source/how-to/launchd-service.md
index b988a19e3..53c21bef5 100644
--- a/docs/source/how-to/launchd-service.md
+++ b/docs/source/how-to/launchd-service.md
@@ -12,7 +12,7 @@ and will need modified if using other installation methods e.g. `Deluge.app`.
Create the file `/Library/LaunchDaemons/org.deluge-torrent.deluged.plist`
containing the following:
-```eval_rst
+```{eval-rst}
.. literalinclude:: ../../../packaging/osx/launchd/org.deluge-torrent.deluged.plist
:language: xml
```
@@ -29,7 +29,7 @@ sudo launchctl start org.deluge-torrent.deluged
Create the file `/Library/LaunchDaemons/org.deluge-torrent.deluge-web.plist`
containing the following:
-```eval_rst
+```{eval-rst}
.. literalinclude:: ../../../packaging/osx/launchd/org.deluge-torrent.deluge-web.plist
:language: xml
```
diff --git a/docs/source/how-to/systemd-service.md b/docs/source/how-to/systemd-service.md
index 8c7ac01e8..611ecc13d 100644
--- a/docs/source/how-to/systemd-service.md
+++ b/docs/source/how-to/systemd-service.md
@@ -30,7 +30,7 @@ sudo adduser <username> deluge
Create the file `/etc/systemd/system/deluged.service` containing the following:
-```eval_rst
+```{eval-rst}
.. literalinclude:: ../../../packaging/systemd/deluged.service
:language: ini
```
@@ -47,7 +47,7 @@ sudo mkdir /etc/systemd/system/deluged.service.d/
Then create a user file `/etc/systemd/system/deluged.service.d/user.conf` with
the following contents:
-```eval_rst
+```{eval-rst}
.. literalinclude:: ../../../packaging/systemd/user.conf
:language: ini
```
@@ -82,7 +82,7 @@ after changes.
Create the file `/etc/systemd/system/deluge-web.service` containing the following:
-```eval_rst
+```{eval-rst}
.. literalinclude:: ../../../packaging/systemd/deluge-web.service
:language: ini
```
@@ -99,7 +99,7 @@ sudo mkdir /etc/systemd/system/deluge-web.service.d/
Then create a user file `/etc/systemd/system/deluge-web.service.d/user.conf` with
the following contents:
-```eval_rst
+```{eval-rst}
.. literalinclude:: ../../../packaging/systemd/user.conf
:language: ini
```
diff --git a/docs/source/intro/01-install.md b/docs/source/intro/01-install.md
index dea4e9c59..15e99456b 100644
--- a/docs/source/intro/01-install.md
+++ b/docs/source/intro/01-install.md
@@ -1,59 +1,133 @@
# Installing Deluge
-These are the instructions for installing Deluge. Consider them a work-in-progress and
-feel free to make suggestions for improvement.
+Instructions for installing Deluge.
-## <i class="icon-ubuntu"></i> Ubuntu
+## <i class="fa fa-linux"></i> Linux
-### PPA
+### <i class="icon-ubuntu"></i> Ubuntu
-Until the stable PPA is updated, the development version of Deluge can be used:
+One-click [**Install**](https://tinyurl.com/installdeluge)
- sudo add-apt-repository -u ppa:deluge-team/stable
- sudo apt install deluge
+```
+sudo apt install deluge
+```
-### <i class="icon-python"></i> PyPi
+[Package Details](https://packages.ubuntu.com/deluge)
-To install from Python PyPi, Deluge requires the following system installed packages:
+### <i class="icon-fedora"></i> Fedora
- sudo apt install python3-pip python3-libtorrent python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-appindicator3
+```
+sudo dnf install deluge
+```
+
+[Package Details](https://src.fedoraproject.org/rpms/deluge)
+
+### <i class="icon-archlinux"></i> Arch
+
+```
+pacman -S deluge-gtk
+```
+
+[Arch Wiki](https://wiki.archlinux.org/title/Deluge)
+
+### <i class="icon-suse"></i> OpenSUSE
+
+[**1 Click Install**](http://packman.links2linux.org/install/deluge)
+
+[Package Details](https://software.opensuse.org/package/deluge)
+
+### <i class="icon-gentoo"></i> Gentoo
+
+[Package Details](https://packages.gentoo.org/packages/net-p2p/deluge)
+
+### Flatpak
+
+One-click [**Install**](https://dl.flathub.org/repo/appstream/org.deluge_torrent.deluge.flatpakref)
+
+[Package Details](https://flathub.org/apps/details/org.deluge_torrent.deluge)
+
+## <i class="fa fa-windows"></i> Windows
+
+Download [installer](https://ftp.osuosl.org/pub/deluge/windows/?C=M;O=D)
+
+Availble for Windows 7, 8 & 10 for both 32-bit and 64-bit OSes.
+
+## <i class="fa fa-apple"></i> macOS
+
+Unfortunately no official installer package currently available.
+
+See [Alternative Installs](#alternative-installs)
+
+## <i class="icon-freebsd"></i> FreeBSD
+
+```
+pkg add deluge
+```
+
+[Package details](https://www.freshports.org/net-p2p/deluge/)
+
+## <i class="icon-python"></i> PyPi
Install with pip:
pip install deluge
-Install with all optional dependecies:
+Install with all [optional dependencies][depends]:
pip install deluge[all]
-## <i class="fa fa-windows"></i> Windows
+Will require system installed packages such as libtorent and GTK3. See [DEPENDS]
-Unfortunately due to move to GTK3 and Python 3 there is no installer package currently
-available for Windows.
+e.g. on Ubuntu/Debian install these packages:
-Intrepid users can install Deluge from separate packages as detailed in [issue #3201].
+ sudo apt install python3-pip python3-libtorrent python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-appindicator3
-## <i class="fa fa-apple"></i> macOS
+## Alternative Installs
+
+### Ubuntu PPA
+
+The [stable PPA] contains the latest releases.
+
+ sudo add-apt-repository -u ppa:deluge-team/stable
+ sudo apt install deluge
+
+The [development PPA] contains daily builds from the `develop` branch.
+
+ sudo add-apt-repository -u ppa:deluge-team/develop
+ sudo apt install deluge
+
+### macOS Community
+
+#### Unofficial `.app` packages
+
+Check sticky topics in [MacOS Forum]
+
+#### Macports
+
+```
+sudo port install deluge
+```
+
+[Package Details](https://ports.macports.org/port/deluge/)
-There is no `.app` package currently for macOS, but can try Deluge with [Homebrew].
+#### Homebrew
1. Install [Homebrew]
-2. Open a terminal.
-3. Run the following to install required packages:
+1. Open a terminal to install required packages:
brew install pygobject3 gtk+3 adwaita-icon-theme
brew install libtorrent-rasterbar
-4. To fix translations:
+1. To fix translations:
brew link gettext --force
-5. Install Deluge:
+1. Install Deluge:
- pip3 install deluge
+ pip install deluge
-[develop ppa]: https://launchpad.net/~deluge-team/+archive/ubuntu/develop/
+[development ppa]: https://launchpad.net/~deluge-team/+archive/ubuntu/develop/
+[stable ppa]: https://launchpad.net/~deluge-team/+archive/ubuntu/stable/
[homebrew]: https://brew.sh/
-[python 3.6]: https://www.python.org/downloads/release/python-368/
-[gvsbuild]: https://ci.appveyor.com/api/buildjobs/b0y2sttcq3t1071q/artifacts/gvsbuild-vs14-x64.tar.gz
-[issue #3201]: https://dev.deluge-torrent.org/ticket/3201#comment:9
+[macos forum]: https://forum.deluge-torrent.org/viewforum.php?f=13
+[depends]: ../depends.md
diff --git a/docs/source/intro/index.md b/docs/source/intro/index.md
index 8342cec1e..ff0f8412e 100644
--- a/docs/source/intro/index.md
+++ b/docs/source/intro/index.md
@@ -3,7 +3,12 @@
This is a starting point if you are new to Deluge where we will walk
you through getting up and running with our BitTorrent client.
-- [1. Installing Deluge](01-install.md)
+```{toctree}
+:numbered: 1
+:titlesonly:
+
+01-install
+```
<!--
2. Using Deluge
diff --git a/docs/source/releases/index.md b/docs/source/releases/index.md
index d72588465..69778a036 100644
--- a/docs/source/releases/index.md
+++ b/docs/source/releases/index.md
@@ -3,8 +3,11 @@
A summary of the important changes in major releases of Deluge. For more details see
the [changelog] or the [git commit log].
-- [Changelog]
-- [Deluge 2.0 release notes](2.0.md)
+```{toctree}
+:titlesonly:
+
+../changelog
+2.0
+```
[git commit log]: http://git.deluge-torrent.org/deluge/log/?h=master
-[changelog]: ../changelog.md
diff --git a/gen_web_gettext.py b/gen_web_gettext.py
index fac509736..80186e938 100755
--- a/gen_web_gettext.py
+++ b/gen_web_gettext.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2012 Damien Churchill <damoxc@gmail.com>
#
@@ -10,8 +9,6 @@
"""Script to parse javascript files for translation strings and generate gettext.js"""
-from __future__ import print_function, unicode_literals
-
import os
import re
@@ -119,4 +116,4 @@ if __name__ == '__main__':
print('Possible missed text for translation markup:')
for text, filenames in missed_markup.iteritems():
for filename_lineno in filenames:
- print('{0:<58} {1}'.format(':'.join(filename_lineno), text))
+ print('{:<58} {}'.format(':'.join(filename_lineno), text))
diff --git a/generate_pot.py b/generate_pot.py
index f5cad5b62..fdc7baec3 100755
--- a/generate_pot.py
+++ b/generate_pot.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2011-2013 Calum Lind <calumlind@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
@@ -11,8 +10,6 @@
"""Parses Python and Javascript code for translation strings to create the 'deluge.pot' template for translators"""
-from __future__ import print_function, unicode_literals
-
import os
import re
from datetime import datetime
@@ -42,7 +39,7 @@ xgettext_cmd = [
]
to_translate = []
-for (dirpath, dirnames, filenames) in os.walk('deluge'):
+for dirpath, dirnames, filenames in os.walk('deluge'):
for filename in filenames:
if dirpath not in EXCLUSIONS and not RE_EXC_PLUGIN_BUILD.match(dirpath):
filepath = os.path.join(dirpath, filename)
@@ -70,13 +67,13 @@ call(xgettext_cmd)
# find javascript files
js_to_translate = []
-for (dirpath, dirnames, filenames) in os.walk(WEBUI_JS_DIR):
+for dirpath, dirnames, filenames in os.walk(WEBUI_JS_DIR):
for filename in filenames:
if os.path.splitext(filename)[1] == '.js':
js_to_translate.append(os.path.join(dirpath, filename))
# find render html files
-for (dirpath, dirnames, filenames) in os.walk(WEBUI_RENDER_DIR):
+for dirpath, dirnames, filenames in os.walk(WEBUI_RENDER_DIR):
for filename in filenames:
if os.path.splitext(filename)[1] == '.html':
js_to_translate.append(os.path.join(dirpath, filename))
@@ -91,7 +88,7 @@ with open(INFILES_LIST, 'w') as f:
call(xgettext_cmd + ['--language=Python', '-j'])
# Replace YEAR and PACKAGE in the copyright message
-with open(POT_FILEPATH, 'r') as f:
+with open(POT_FILEPATH) as f:
lines = f.readlines()
with open(POT_FILEPATH, 'w') as f:
for line in lines:
diff --git a/minify_web_js.py b/minify_web_js.py
index ff459e795..614794a97 100755
--- a/minify_web_js.py
+++ b/minify_web_js.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Calum Lind <calumlind@gmail.com>
# Copyright (C) 2010 Damien Churchill <damoxc@gmail.com>
@@ -15,18 +14,16 @@ Usage: python minify_web_js.py deluge/ui/web/js/deluge-all
"""
-from __future__ import print_function, unicode_literals
-
import fileinput
import fnmatch
import os
import subprocess
import sys
-from distutils.spawn import find_executable
+from shutil import which
closure_cmd = None
for cmd in ['closure-compiler', 'closure']:
- if find_executable(cmd):
+ if which(cmd):
closure_cmd = cmd
break
@@ -50,14 +47,14 @@ def minify_closure(file_in, file_out):
return False
-# Closure outputs smallest files but it is a java-based command, so have slimit
+# Closure outputs smallest files but java-based command, can use rJSmin
# as a python-only fallback.
#
-# deluge-all.js: Closure 127K, Slimit: 143K, JSMin: 162K
+# deluge-all.js: Closure 131K, rJSmin: 148K
#
if not closure_cmd:
try:
- from slimit import minify as minify
+ from rjsmin import jsmin as minify
except ImportError:
print('Warning: No minifying command found.')
minify = None
@@ -72,7 +69,7 @@ def source_files_list(source_dir):
order_file = os.path.join(root, '.order')
if os.path.isfile(order_file):
- with open(order_file, 'r') as _file:
+ with open(order_file) as _file:
for line in _file:
if line.startswith('+ '):
order_filename = line.split()[1]
@@ -99,7 +96,7 @@ def minify_file(file_debug, file_minified):
return minify_closure(file_debug, file_minified)
elif minify:
with open(file_minified, 'w') as file_out:
- with open(file_debug, 'r') as file_in:
+ with open(file_debug) as file_in:
file_out.write(minify(file_in.read()))
return True
@@ -119,6 +116,8 @@ def minify_js_dir(source_dir):
print('Minifying %s' % source_dir)
if not minify_file(file_debug_js, file_minified_js):
print('Warning: Failed minifying files %s, debug only' % source_dir)
+ if os.path.isfile(file_minified_js):
+ os.remove(file_minified_js)
if __name__ == '__main__':
diff --git a/msgfmt.py b/msgfmt.py
index c0e093ab6..0d5367c3b 100755
--- a/msgfmt.py
+++ b/msgfmt.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: iso-8859-1 -*-
# Written by Martin v. Lwis <loewis@informatik.hu-berlin.de>
# Plural forms support added by alexander smishlajev <alex@tycobka.lv>
"""
@@ -25,8 +24,6 @@ Options:
--version
Display version information and exit.
"""
-from __future__ import print_function, unicode_literals
-
import array
import ast
import getopt
@@ -103,10 +100,7 @@ def generate():
0,
0,
) # size and offset of hash table
- if sys.version_info.major == 2:
- output += array.array(b'i', offsets).tostring()
- else:
- output += array.array('i', offsets).tobytes()
+ output += array.array('i', offsets).tobytes()
output += ids.encode('utf8')
output += strs.encode('utf8')
return output
@@ -127,11 +121,9 @@ def make(filename, outfile):
outfile = os.path.splitext(infile)[0] + '.mo'
try:
- import io
-
- with io.open(infile, encoding='utf8') as _file:
+ with open(infile, encoding='utf8') as _file:
lines = _file.readlines()
- except IOError as msg:
+ except OSError as msg:
print(msg, file=sys.stderr)
sys.exit(1)
@@ -181,9 +173,6 @@ def make(filename, outfile):
if not line:
continue
line = ast.literal_eval(line)
- # Python 2 ast.literal_eval returns bytes.
- if isinstance(line, bytes):
- line = line.decode('utf8')
if section == section_id:
msgid += line
elif section == section_str:
@@ -202,7 +191,7 @@ def make(filename, outfile):
try:
with open(outfile, 'wb') as _file:
_file.write(output)
- except IOError as msg:
+ except OSError as msg:
print(msg, file=sys.stderr)
diff --git a/packaging/source/make_release.py b/packaging/source/make_release.py
index e1cee0036..277d1cedc 100755
--- a/packaging/source/make_release.py
+++ b/packaging/source/make_release.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright 2014 Calum Lind <calumlind@gmail.com>
#
@@ -7,18 +6,11 @@
# 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.path
-import sys
from hashlib import sha256
from subprocess import call, check_output
-PY2 = sys.version_info.major == 2
-
sdist_formats = 'xztar'
-if PY2:
- sdist_formats = 'tar'
version = check_output(['python', 'version.py']).strip().decode()
@@ -53,7 +45,7 @@ else:
# Calculate shasum and add to sha256sums.txt
with open(tarxz_path, 'rb') as _file:
- sha256sum = '%s %s' % (
+ sha256sum = '{} {}'.format(
sha256(_file.read()).hexdigest(),
os.path.basename(tarxz_path),
)
diff --git a/packaging/systemd/deluge-web.service b/packaging/systemd/deluge-web.service
index b74c0ada8..7904db31a 100644
--- a/packaging/systemd/deluge-web.service
+++ b/packaging/systemd/deluge-web.service
@@ -1,7 +1,7 @@
[Unit]
Description=Deluge Bittorrent Client Web Interface
Documentation=man:deluge-web
-After=network-online.target deluged.service
+After=deluged.service
Wants=deluged.service
[Service]
diff --git a/packaging/systemd/user/deluge-web.service b/packaging/systemd/user/deluge-web.service
new file mode 100644
index 000000000..2c3557eb6
--- /dev/null
+++ b/packaging/systemd/user/deluge-web.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Deluge Bittorrent Client Web Interface
+Documentation=man:deluge-web
+After=deluged.service
+Wants=deluged.service
+
+[Service]
+UMask=027
+
+ExecStart=/usr/bin/deluge-web -d
+
+Restart=on-failure
+Slice=background.slice
+
+[Install]
+WantedBy=default.target
diff --git a/packaging/systemd/user/deluged.service b/packaging/systemd/user/deluged.service
new file mode 100644
index 000000000..b026dee7b
--- /dev/null
+++ b/packaging/systemd/user/deluged.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Deluge Bittorrent Client Daemon
+Documentation=man:deluged
+
+[Service]
+UMask=007
+ExecStart=/usr/bin/deluged -d
+Restart=on-failure
+TimeoutStopSec=300
+Slice=background.slice
+
+[Install]
+WantedBy=default.target
diff --git a/packaging/win/README.md b/packaging/win/README.md
new file mode 100644
index 000000000..272792c3f
--- /dev/null
+++ b/packaging/win/README.md
@@ -0,0 +1,34 @@
+= Deluge Installer for Windows =
+
+Instructions for building the Deluge NSIS Installer for Windows Vista/7/8/8.1/10/11.
+
+== Dependencies ==
+
+- Deluge build: https://deluge.readthedocs.io/en/latest/depends.html
+- PyInstaller: https://pypi.org/project/pyinstaller/
+- NSIS: http://nsis.sourceforge.net/Download
+
+== Build Steps ==
+
+1. Build and Install Deluge on Windows.
+2. Run pyinstaller from the deluge\packaging\win directory:
+
+ `pyinstaller --clean delugewin.spec --distpath freeze`
+
+ The result is a PyInstaller version of Deluge in `packaging\win\freeze`.
+
+3. Run the NSIS scripts:
+
+ `python setup_nsis.py`
+
+ 64-bit python:
+
+ `makensis /Darch=x64 deluge-win-installer.nsi`
+
+ 32-bit python:
+
+ `makensis /Darch=x86 deluge-win-installer.nsi`
+
+ Note: If you don't specify arch defaults to trying x64
+
+The result is a standalone installer in the `packaging\win` directory.
diff --git a/packaging/win32/deluge-win32-installer.nsi b/packaging/win/deluge-win-installer.nsi
index cd481335d..1f9c2e271 100644
--- a/packaging/win32/deluge-win32-installer.nsi
+++ b/packaging/win/deluge-win-installer.nsi
@@ -9,7 +9,7 @@
#
# Script version; displayed when running the installer
-!define DELUGE_INSTALLER_VERSION "1.0"
+!define DELUGE_INSTALLER_VERSION "2.0"
# Deluge program information
!define PROGRAM_NAME "Deluge"
@@ -21,10 +21,17 @@
!define PROGRAM_WEB_SITE "http://deluge-torrent.org"
!define LICENSE_FILEPATH "..\..\LICENSE"
-# Python files generated with bbfreeze
-!define BUILD_DIR "build-win32"
-!define BBFREEZE_DIR "${BUILD_DIR}\deluge-bbfreeze-${PROGRAM_VERSION}"
+!include FileFunc.nsh
+
+!ifndef arch
+!define INSTALLER_FILENAME "deluge-${PROGRAM_VERSION}-win64-setup.exe"
+!endif
+!If "${arch}" == "x64"
+!define INSTALLER_FILENAME "deluge-${PROGRAM_VERSION}-win64-setup.exe"
+!EndIf
+!If "${arch}" == "x86"
!define INSTALLER_FILENAME "deluge-${PROGRAM_VERSION}-win32-setup.exe"
+!EndIf
# Set default compressor
SetCompressor /FINAL /SOLID lzma
@@ -69,8 +76,6 @@ Var StartMenuFolder
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder
# Run installation
!insertmacro MUI_PAGE_INSTFILES
-# Popup Message if VC Redist missing
-Page Custom VCRedistMessage
# Display 'finished' page
!insertmacro MUI_PAGE_FINISH
# Uninstaller pages
@@ -105,45 +110,6 @@ Function finishpageaction
CreateShortCut "$DESKTOP\Deluge.lnk" "$INSTDIR\deluge.exe"
FunctionEnd
-# Test if Visual Studio Redistributables 2008 SP1 installed and returns -1 if none installed
-Function CheckVCRedist2008
- Push $R0
- ClearErrors
- ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}" "Version"
- IfErrors 0 +2
- StrCpy $R0 "-1"
-
- Push $R1
- ClearErrors
- ReadRegDword $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{9BE518E6-ECC6-35A9-88E4-87755C07200F}" "Version"
- IfErrors 0 VSRedistInstalled
- StrCpy $R1 "-1"
-
- StrCmp $R0 "-1" +3 0
- Exch $R0
- Goto VSRedistInstalled
- StrCmp $R1 "-1" +3 0
- Exch $R1
- Goto VSRedistInstalled
- # else
- Push "-1"
- VSRedistInstalled:
-FunctionEnd
-
-Function VCRedistMessage
- Call CheckVCRedist2008
- Pop $R0
- StrCmp $R0 "-1" 0 end
- MessageBox MB_YESNO|MB_ICONEXCLAMATION "Deluge requires an MSVC package to run \
- but the recommended package does not appear to be installed:$\r$\n$\r$\n\
- Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)$\r$\n$\r$\n\
- Would you like to download it now?" /SD IDNO IDYES clickyes
- Goto end
- clickyes:
- ExecShell open "https://www.microsoft.com/en-us/download/details.aspx?id=26368"
- end:
-FunctionEnd
-
# --- Installation sections ---
!define PROGRAM_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROGRAM_NAME}"
!define PROGRAM_UNINST_ROOT_KEY "HKLM"
@@ -151,8 +117,17 @@ FunctionEnd
BrandingText "${PROGRAM_NAME} Windows Installer v${DELUGE_INSTALLER_VERSION}"
Name "${PROGRAM_NAME} ${PROGRAM_VERSION}"
-OutFile "${BUILD_DIR}\${INSTALLER_FILENAME}"
-InstallDir "$PROGRAMFILES\Deluge"
+OutFile "${INSTALLER_FILENAME}"
+
+!ifndef arch
+InstallDir "$PROGRAMFILES64\Deluge"
+!endif
+!If "${arch}" == "x64"
+InstallDir "$PROGRAMFILES64\Deluge"
+!endIf
+!If "${arch}" == "x86"
+InstallDir "$PROGRAMFILES32\Deluge"
+!endIf
ShowInstDetails show
ShowUnInstDetails show
@@ -212,6 +187,7 @@ LangString DESC_Section3 ${LANG_ENGLISH} "Select this option to let Deluge handl
Section -Uninstaller
WriteUninstaller ${PROGRAM_UNINST_FILENAME}
WriteRegStr ${PROGRAM_UNINST_ROOT_KEY} "${PROGRAM_UNINST_KEY}" "DisplayName" "$(^Name)"
+ WriteRegStr ${PROGRAM_UNINST_ROOT_KEY} "${PROGRAM_UNINST_KEY}" "DisplayVersion" ${PROGRAM_VERSION}
WriteRegStr ${PROGRAM_UNINST_ROOT_KEY} "${PROGRAM_UNINST_KEY}" "UninstallString" ${PROGRAM_UNINST_FILENAME}
SectionEnd
@@ -227,8 +203,8 @@ Section Uninstall
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder
SetShellVarContext all
Delete "$SMPROGRAMS\$StartMenuFolder\Deluge.lnk"
+ Delete "$SMPROGRAMS\$StartMenuFolder\Website.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Deluge.lnk"
- Delete "$SMPROGRAMS\$StartMenuFolder\Deluge Website.lnk"
RmDir "$SMPROGRAMS\$StartMenuFolder"
DeleteRegKey /ifempty HKCR "Software\Deluge"
diff --git a/packaging/win/delugewin.spec b/packaging/win/delugewin.spec
new file mode 100644
index 000000000..b6b557365
--- /dev/null
+++ b/packaging/win/delugewin.spec
@@ -0,0 +1,183 @@
+# -*- mode: python -*-
+import os
+
+from PyInstaller.utils.hooks import (
+ collect_data_files,
+ collect_submodules,
+ copy_metadata,
+)
+
+datas = []
+binaries = []
+hiddenimports = ['pygame', 'ifaddr']
+
+# Collect Meta Data
+datas += copy_metadata('deluge', recursive=True)
+datas += copy_metadata('service-identity', recursive=True)
+
+# Add Deluge Hidden Imports
+hiddenimports += collect_submodules('deluge')
+
+# Add stdlib as Hidden Imports.
+# This is filtered list that excludes some common examples or stuff not useful in
+# plugins (such as tty, mailbox, turtledemo etc.).
+# It is safe to assume that 90% of that list would already be included anyway.
+stdlib = [
+ 'string',
+ 're',
+ 'unicodedata',
+ 'struct',
+ 'codecs',
+ 'datetime',
+ 'zoneinfo',
+ 'calendar',
+ 'collections',
+ 'array',
+ 'weakref',
+ 'types',
+ 'copy',
+ 'enum',
+ 'numbers',
+ 'math',
+ 'cmath',
+ 'decimal',
+ 'fractions',
+ 'random',
+ 'statistics',
+ 'itertools',
+ 'functools',
+ 'operator',
+ 'pathlib',
+ 'fileinput',
+ 'stat',
+ 'tempfile',
+ 'glob',
+ 'fnmatch',
+ 'shutil',
+ 'pickle',
+ 'copyreg',
+ 'shelve',
+ 'marshal',
+ 'dom',
+ 'sqlite3',
+ 'zlib',
+ 'gzip',
+ 'bz2',
+ 'lzma',
+ 'csv',
+ 'hashlib',
+ 'hmac',
+ 'secrets',
+ 'os',
+ 'io',
+ 'time',
+ 'logging',
+ 'platform',
+ 'errno',
+ 'queue',
+ 'socket',
+ 'ssl',
+ 'email',
+ 'json',
+ 'mimetypes',
+ 'base64',
+ 'binhex',
+ 'binascii',
+ 'quopri',
+ 'uu',
+ 'html',
+ 'xml',
+ 'urllib',
+ 'http',
+ 'ftplib',
+ 'smtplib',
+ 'uuid',
+ 'xmlrpc.client',
+ 'ipaddress',
+ 'locale',
+ 'sys',
+]
+for module in stdlib:
+ hiddenimports += collect_submodules(module, filter=lambda name: 'test' not in name)
+
+# Add Hidden Imports for Plugins
+hiddenimports += collect_submodules('twisted', filter=lambda name: 'test' not in name)
+datas += copy_metadata('twisted', recursive=True)
+
+# Copy UI/Plugin and translation files to where pyinstaller expects
+package_data = collect_data_files('deluge')
+datas += package_data
+
+icon = [src for src, dest in package_data if src.endswith('deluge.ico')][0]
+
+# List of executables to produce
+executables = {
+ 'deluge-script.pyw': {'name': 'deluge', 'console': False, 'gtk': True},
+ 'deluge-gtk-script.pyw': {'name': 'deluge-gtk', 'console': False, 'gtk': True},
+ 'deluge-debug-script.py': {'name': 'deluge-debug', 'console': True, 'gtk': True},
+ 'deluge-console-script.py': {
+ 'name': 'deluge-console',
+ 'console': True,
+ 'gtk': False,
+ },
+ 'deluged-script.pyw': {'name': 'deluged', 'console': False, 'gtk': False},
+ 'deluged-debug-script.py': {'name': 'deluged-debug', 'console': True, 'gtk': False},
+ 'deluge-web-debug-script.py': {
+ 'name': 'deluge-web-debug',
+ 'console': True,
+ 'gtk': False,
+ },
+ 'deluge-web-script.pyw': {'name': 'deluge-web', 'console': False, 'gtk': False},
+}
+
+analysis = {}
+exe = {}
+coll = []
+
+# Perform analysis
+for e, d in executables.items():
+ runtime_hooks = []
+ if d['gtk']:
+ runtime_hooks += [os.path.join(SPECPATH, 'pyi_rth_gtk_csd.py')]
+
+ analysis[e] = Analysis(
+ [os.path.abspath(os.path.join(HOMEPATH, os.pardir, os.pardir, 'Scripts', e))],
+ pathex=[],
+ binaries=binaries,
+ datas=datas,
+ hiddenimports=hiddenimports,
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=runtime_hooks,
+ excludes=[],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=None,
+ noarchive=False,
+ )
+
+# Executable
+for e, d in executables.items():
+ exe[e] = EXE(
+ PYZ(analysis[e].pure, analysis[e].zipped_data, cipher=None),
+ analysis[e].scripts,
+ [],
+ exclude_binaries=True,
+ name=d['name'],
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ icon=icon,
+ console=d['console'],
+ disable_windowed_traceback=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+ )
+
+# Collect
+for e, d in executables.items():
+ coll += exe[e], analysis[e].binaries, analysis[e].zipfiles, analysis[e].datas
+
+COLLECT(*coll, strip=False, upx=True, upx_exclude=[], name='Deluge')
diff --git a/packaging/win32/installer-side.bmp b/packaging/win/installer-side.bmp
index 58c2bed5b..58c2bed5b 100644
--- a/packaging/win32/installer-side.bmp
+++ b/packaging/win/installer-side.bmp
Binary files differ
diff --git a/packaging/win32/installer-top.bmp b/packaging/win/installer-top.bmp
index 1e1a94cd7..1e1a94cd7 100644
--- a/packaging/win32/installer-top.bmp
+++ b/packaging/win/installer-top.bmp
Binary files differ
diff --git a/packaging/win/pyi_rth_gtk_csd.py b/packaging/win/pyi_rth_gtk_csd.py
new file mode 100644
index 000000000..345122dd6
--- /dev/null
+++ b/packaging/win/pyi_rth_gtk_csd.py
@@ -0,0 +1,3 @@
+import os
+
+os.environ['GTK_CSD'] = os.getenv('GTK_CSD', '0')
diff --git a/packaging/win/setup_nsis.py b/packaging/win/setup_nsis.py
new file mode 100644
index 000000000..f34a941e1
--- /dev/null
+++ b/packaging/win/setup_nsis.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2015 Calum Lind <calumlind@gmail.com>
+# Copyright (C) 2010 Damien Churchill <damoxc@gmail.com>
+# Copyright (C) 2009-2010 Andrew Resch <andrewresch@gmail.com>
+# Copyright (C) 2009 Jesper Lund <mail@jesperlund.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+import os
+
+import deluge.common
+
+# Get build_version from installed deluge.
+build_version = deluge.common.get_version()
+build_dir = os.path.join('freeze', 'Deluge')
+
+# Copy version info to file for nsis script.
+with open('VERSION.tmp', 'w') as ver_file:
+ ver_file.write('build_version = "%s"' % build_version)
+
+# Create the install and uninstall file list for NSIS.
+filedir_list = []
+for root, dirnames, filenames in os.walk(build_dir):
+ dirnames.sort()
+ filenames.sort()
+ filedir_list.append((root[len(build_dir) :], filenames))
+
+with open('install_files.nsh', 'w') as f:
+ f.write('; Files to install\n')
+ for dirname, files in filedir_list:
+ if not dirname:
+ dirname = os.sep
+ f.write('\nSetOutPath "$INSTDIR%s"\n' % dirname)
+ for filename in files:
+ f.write('File ' + build_dir + os.path.join(dirname, filename) + '\n')
+
+with open('uninstall_files.nsh', 'w') as f:
+ f.write('; Files to uninstall\n')
+ for dirname, files in reversed(filedir_list):
+ f.write('\n')
+ if not dirname:
+ dirname = os.sep
+ for filename in files:
+ f.write('Delete "$INSTDIR%s"\n' % os.path.join(dirname, filename))
+ f.write('RMDir "$INSTDIR%s"\n' % dirname)
diff --git a/packaging/win32/DelugeStart Theme/etc/gtk-2.0/gtkrc b/packaging/win32/DelugeStart Theme/etc/gtk-2.0/gtkrc
deleted file mode 100644
index 3539a6289..000000000
--- a/packaging/win32/DelugeStart Theme/etc/gtk-2.0/gtkrc
+++ /dev/null
@@ -1,25 +0,0 @@
-gtk-theme-name = "DelugeStart"
-gtk-icon-theme-name = "Tango"
-gtk-fallback-icon-theme = "hicolor"
-gtk-alternative-button-order = 1
-gtk-alternative-sort-arrows = 1
-gtk-auto-mnemonics = 1
-gtk-show-input-method-menu = 0
-gtk-show-unicode-menu = 0
-
-#gtk-toolbar-icon-size = small-toolbar
-gtk-button-images = 0
-gtk-menu-images = 1
-
-style "notebook"
-{
- xthickness = 1
- ythickness = 1
-}
-widget_class "*<GtkNotebook>" style "notebook"
-
-style "user-font"
-{
- font_name="9"
-}
-widget_class "*" style "user-font"
diff --git a/packaging/win32/DelugeStart Theme/lib/gtk-2.0/2.10.0/engines/libmurrine.dll b/packaging/win32/DelugeStart Theme/lib/gtk-2.0/2.10.0/engines/libmurrine.dll
deleted file mode 100644
index 12134fc0a..000000000
--- a/packaging/win32/DelugeStart Theme/lib/gtk-2.0/2.10.0/engines/libmurrine.dll
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check1.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check1.png
deleted file mode 100644
index 8cdd1cb23..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check1.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check2.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check2.png
deleted file mode 100644
index a484a7019..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check2.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check3.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check3.png
deleted file mode 100644
index faafa4748..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check3.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check4.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check4.png
deleted file mode 100644
index bbaa5af19..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check4.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check5.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check5.png
deleted file mode 100644
index e6b81d80c..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check5.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/checklight.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/checklight.png
deleted file mode 100644
index b00483813..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/checklight.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option1.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option1.png
deleted file mode 100644
index eba2cbf67..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option1.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option2.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option2.png
deleted file mode 100644
index 22779c3d1..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option2.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option3.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option3.png
deleted file mode 100644
index ecbb7e4d2..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option3.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option4.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option4.png
deleted file mode 100644
index 42aed6679..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option4.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Icons/close.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Icons/close.png
deleted file mode 100644
index d9b0608f2..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Icons/close.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-horiz.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-horiz.png
deleted file mode 100644
index 4c5468fd9..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-horiz.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-vert.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-vert.png
deleted file mode 100644
index 4c5468fd9..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-vert.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-insens.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-insens.png
deleted file mode 100644
index 1e60f4bef..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-insens.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-prelight.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-prelight.png
deleted file mode 100644
index db46ba089..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-prelight.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz.png
deleted file mode 100644
index fe5babaaf..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-insens.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-insens.png
deleted file mode 100644
index ea9bd9999..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-insens.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-prelight.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-prelight.png
deleted file mode 100644
index 35ed7cf75..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-prelight.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert.png
deleted file mode 100644
index 2ffe36930..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-horiz.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-horiz.png
deleted file mode 100644
index 25a99c204..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-horiz.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-vert.png b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-vert.png
deleted file mode 100644
index 094364fa5..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-vert.png
+++ /dev/null
Binary files differ
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/gtkrc b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/gtkrc
deleted file mode 100644
index de4001742..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/gtkrc
+++ /dev/null
@@ -1,519 +0,0 @@
-# DelugeStart ( based upon ANewStart by alecive )
-# Licensed under the GPL.
-# Requires Murrine v0.91.0
-
-gtk_color_scheme = "bg_color:#F5F5F5\nselected_bg_color:#8DCCF0\nbase_color:#FFFFFF" # Background, base
-gtk_color_scheme = "fg_color:#3C4343\nselected_fg_color:#1E2222\ntext_color:#3C4343" # Foreground, text
-#gtk_color_scheme = "sidebar_color:#DEDEDE" # Custom colors
-gtk_color_scheme = "tooltip_bg_color:#B7DB67\ntooltip_fg_color:#F5F5F5" # Tooltips
-gtk_color_scheme = "theme_color_04:#a9a49c\ntheme_color_26:#292421\ntheme_color_27:#C7C7C7" # Other colors
-#gtk_color_scheme = "link_color:#08C" # Hyperlinks
-
-gtk-icon-sizes = "panel-menu=23,23:panel=21,21:gtk-button=17,17:gtk-large-toolbar=22,22"
-
-gtk-button-images = 0 # Disables icons for buttons with text
-gtk-toolbar-style = 0 # Disables text in toolbar
-gtk-auto-mnemonics = 1 # Disables lines under menu items
-
-style "default"
-{
- xthickness = 1
- ythickness = 1
-
- GtkArrow::arrow-scaling = 0.6
- GtkComboBox::arrow-scaling = 0.2
-
- GtkScrolledWindow ::scrollbar-spacing = 0
- GtkScrolledWindow ::scrollbar-within-bevel = 0
-
- GtkScrollbar::slider_width = 11
- GtkScrollbar::has-backward-stepper = 0
- GtkScrollbar::has-forward-stepper = 0
- GtkScrollbar::min-slider-length = 30
-
- GtkButton::child-displacement-x = 1
- GtkButton::child-displacement-y = 1
- GtkButton::default-border = { 0, 0, 0, 0 }
-
- GtkCheckButton::indicator-size = 12
-
- GtkPaned::handle-size = 6
-
- GtkRange::trough-border = 1
- GtkRange::stepper-size = 12
- GtkRange::trough-under-steppers = 1
- GtkRange::slider-width = 14
-
- GtkScale::slider-length = 14
- GtkScale::slider-width = 14
- GtkScale::trough-side-details = 1
- GtkScale::trough-border = 1
-
- GtkExpander::expander-size = 14
- GtkTreeView::expander-size = 14
- GtkTreeView::indent-expanders = 0
-
- GtkMenu::horizontal-offset = 2
- GtkMenu::vertical-offset = 2
- GtkMenu::horizontal-padding = 2
- GtkMenu::vertical-padding = 2
-
- GtkMenuItem::arrow-spacing = 0
-
- GtkMenuBar::internal-padding = 2
- GtkMenuBar::shadow_type = GTK_SHADOW_NONE
-
- #set to the same as roundness, used for better hotspot selection of tabs
- GtkNotebook::tab-curvature = 3
- GtkNotebook::tab-overlap = -1
-
- GtkToolbar::internal-padding = 2
- GtkToolbar::horizontal-padding = 0
- GtkToolbar::vertical-padding = 0
- GtkToolbar::shadow_type = GTK_SHADOW_NONE #gtk.SHADOW_IN, gtk.SHADOW_OUT, gtk.SHADOW_ETCHED_IN or gtk.SHADOW_ETCHED_OUT
-
- WnckTasklist::fade-overlay-rect = 0
- # The following line hints to gecko (and possibly other appliations)
- # that the entry should be drawn transparently on the canvas.
- # Without this, gecko will fill in the background of the entry.
- # GtkEntry::honors-transparent-bg-hint = 1
-
- GtkEntry::progress-border = { 2, 2, 2, 2 }
-
- fg[NORMAL] = @fg_color
- fg[PRELIGHT] = @fg_color
- fg[SELECTED] = @selected_fg_color
- fg[ACTIVE] = @fg_color
- fg[INSENSITIVE] = darker (@bg_color)
-
- bg[NORMAL] = @bg_color
- bg[PRELIGHT] = shade (1.04, @bg_color)
- bg[SELECTED] = @selected_bg_color
- bg[INSENSITIVE] = @bg_color
- bg[ACTIVE] = shade (0.9, @bg_color)
-
- base[NORMAL] = @base_color
- base[PRELIGHT] = shade (0.95, @bg_color)
- base[ACTIVE] = mix (0.7, @selected_bg_color, @bg_color)
- base[SELECTED] = @selected_bg_color
- base[INSENSITIVE] = @bg_color
-
- text[NORMAL] = @text_color
- text[PRELIGHT] = @text_color
- text[ACTIVE] = @selected_fg_color
- text[SELECTED] = @selected_fg_color
- text[INSENSITIVE] = darker (@bg_color)
-
- engine "murrine"
- {
- animation = TRUE # FALSE = disabled, TRUE = enabled
- arrowstyle = 1
-# border_shades = { 1.0, 1.0} # draw a gradient on the border.
-# border_colors = { "#E6DDD5", "#E6DDD5" }
- colorize_scrollbar = FALSE # FALSE = disabled, TRUE = enabled
- comboboxstyle = 0 # colorize the GtkComboBox below the arrow.
- contrast = .85 # 0.8 for less contrast, more than 1.0 for more contrast on borders
-# focus_color = @selected_bg_color
-# glazestyle = 5 # 0 = flat, 1 = curved, 2 = concave, 3 = top-curved, 4 = beryl
- glazestyle = 4 # 0 = flat, 1 = curved, 2 = concave, 3 = top-curved, 4 = beryl
- glow_shade = 1.15 # sets glow amount for buttons or widgets
- glowstyle = 0 # 0 = top, 1 = bottom, 2 = top and bottom, 3 = center (vertical), 4 = center (horizontal)
- gradient_shades = { 1.13, 1.02, 1.00, 1.02 } # default: {1.1,1.0,1.0,1.1}
- highlight_shade = 1.0 # set highlight amount for buttons or widgets
- lightborder_shade = 1.2 # sets lightborder amount for buttons or widgets
- lightborderstyle = 1 # 0 = lightborder on top side, 1 = lightborder on all sides
- listviewheaderstyle = 1 # 0 = flat, 1 = glassy, 2 = raised
- listviewstyle = 0 # 0 = nothing, 1 = dotted
- menubaritemstyle = 0 # 0 = menuitem look, 1 = button look
- menubarstyle = 0 # 0 = flat, 1 = glassy, 2 = gradient, 3 = striped
- menuitemstyle = 1 # 0 = flat, 1 = glassy, 2 = striped
- menustyle = 0 # 0 = no vertical menu stripe, 1 = display vertical menu stripe
- prelight_shade = 1.20 #to select the shade level used in the scrollbar's slider, GtkComboBox with comboboxstyle = 1 and in the prelight state with gradient_colors.
- progressbarstyle = 0 # 0 = no stripes, 1 = diagonal stripes, 2 = vertical stripes
- reliefstyle = 4 # 0 = flat, 1 = inset, 2 = shadow, = 3 for a gradient on shadow, = 4 for a stronger shadow.
- rgba = FALSE # FALSE = disabled, TRUE = enabled
- roundness = 2 # 0 = squared, 1 = old default, more will increase roundness
- scrollbarstyle = 0 # 0 = nothing, 1 = circles, 2 = handles, 3 = diagonal stripes, 4 = diagonal stripes and handles, 5 = horizontal stripes, 6 = horizontal stripes and handles
- shadow_shades = { 0.8, 2.2 }
- sliderstyle = 0 # 0 = nothing added, 1 = handles
- spinbuttonstyle = 1
- stepperstyle = 1 # 0 = standard, 1 = integrated stepper handles, 2 = squared steppers with a rounded slider
-# textstyle = 1
- trough_shades = {1.1,0.87}
- toolbarstyle = 0 # 0 = flat, 1 = glassy, 2 = gradient
- separatorstyle = 1 # 0 = solid line, 1 = smooth separator
- }
-}
-
-style "wide"
-{
- xthickness = 2
- ythickness = 2
-}
-
-style "wider"
-{
- xthickness = 3
- ythickness = 3
-}
-
-style "dark"
-{
- #bg[NORMAL] = @bg_color
- #bg[SELECTED] = @selected_bg_color
- #bg[PRELIGHT] = @selected_bg_color
- #bg[ACTIVE] = @selected_bg_color
- #bg[INSENSITIVE] = @bg_color
-
-
- #fg[NORMAL] = @bg_color
- #fg[PRELIGHT] = @selected_fg_color
- #fg[SELECTED] = @selected_fg_color
- #fg[ACTIVE] = @selected_fg_color
- #fg[INSENSITIVE] = @selected_fg_color
-
-}
-
-style "button" = "wide"
-{
- engine "murrine"
- {
- roundness = 3
- gradient_shades = { 1.06, 0.95, 1.06, 0.95}
- border_shades = { .7, .6}
- border_colors = { @bg_color, @bg_color }
-
- lightborderstyle = 1
- lightborder_shade = 1.26
-
- shadow_shades = {2.5,2.2}
- reliefstyle = 4
- }
-}
-
-style "entry" {
- xthickness = 3
- ythickness = 3
-
- bg[SELECTED] = mix (0.4, @selected_bg_color, @base_color)
- fg[SELECTED] = @text_color
-
- engine "murrine"
- {
- focus_color = @selected_bg_color
- lightborder_shade = 1.06
- glow_shade = 1.9
- }
-}
-
-style "terminal"
-{
- text[NORMAL] = darker(@selected_bg_color)
- base[NORMAL] = @bg_color
-
- TerminalScreen::background-darkness = 0.99
-}
-
-style "toolbar"
-{
- ythickness = 0
- engine "murrine"
- {
- gradient_shades = {1.00,0.95,0.95,0.90}
- toolbarstyle = 1
- }
-}
-
-style "toolbar-toggle" = "toolbar"
-{
- text[NORMAL] = @text_color
- text[PRELIGHT] = @text_color
- text[ACTIVE] = @selected_fg_color
- text[SELECTED] = @selected_fg_color
- text[INSENSITIVE] = darker (@bg_color)
-
- engine "murrine"
- {
- toolbarstyle = 0
- }
-}
-
-style "dark-toolbar" = "dark"
-{
- xthickness = 0
- ythickness = 2
-
- engine "murrine"
- {
- border_shades = {1.2, 1.0} # draw a gradient on the border.
- border_colors = { "#62635E", "#62635E" }
- glowstyle = 0
- gradient_shades = {1.1,1.0,1.0,0.7}
- highlight_shade = 1.0
- lightborder_shade = 1.0
- reliefstyle = 1 # 0 = flat, 1 = inset, 2 = shadow, = 3 for a gradient on shadow, = 4 for a stronger shadow.
- }
-}
-
-style "dark-toolbar-sep" = "dark-toolbar"
-{
- xthickness = 2
-}
-
-style "panel"
-{
- xthickness = 0
- ythickness = 0
-
- bg[NORMAL] = "#E3E3E3" # # Default top/bottom panel background
- bg[PRELIGHT] = "#E3E3E3" # @bg_color # panel prelight
-
- engine "murrine"
- {
- #border_shades = {1.2, 1.0} # draw a gradient on the border.
- #border_colors = { "#2D2416", "#2D2416" }
- roundness = 1
- }
-}
-
-style "panel-button" = "panel"
-{
- engine "murrine" {
- roundness = 1
- border_colors = {"#7C7C7C", "#7C7C7C"}
- border_shades = {1.0, 1.0} # draw a gradient on the border.
- gradient_shades = {1.0,1.0,1.0,1.0}
- }
-}
-
-# Based on the default style so that the colors from the button
-# style are overriden again.
-style "treeview-header" = "default"
-{
- xthickness = 1
- ythickness = 1
-
- bg[NORMAL] = "#F2F1F0"
- bg[PRELIGHT] = shade (1.04, "#F2F1F0")
- bg[ACTIVE] = shade (0.96, "#F2F1F0")
- bg[INSENSITIVE] = "#F2F1F0"
-
- engine "murrine" {
- textstyle = 1
- border_shades = {0.90, 0.78}
- glowstyle = 5
- glazestyle = 1
- contrast = 0.8
- lightborder_shade = 1.16
- textstyle = 1
- glow_shade = 1.0
- }
-}
-
-style "progressbar"
-{
- xthickness = 0
- ythickness = 0
-
-
- bg[ACTIVE] = @bg_color
- fg[PRELIGHT] = @selected_fg_color
-
- engine "murrine" {
- trough_shades = {0.9, 0.98}
- roundness = 2
- lightborderstyle = 1
- lightborder_shade = 1.26
- border_shades = {0.8, 0.8}
- gradient_shades = {0.95, 1.1, 0.95, 1.1}
- #trough_border_shades = {0.9, 0.9}
- }
-}
-
-style "statusbar"
-{
- ythickness = 0
- xthickness = 0
-}
-
-style "comboboxentry"
-{
- ythickness = 3
- xthickness = 3
-
- engine "murrine"
- {
- contrast = .8
- }
-}
-
-style "spinbutton"
-{
-}
-
-style "scale"
-{
- bg[ACTIVE] = @bg_color
- bg[PRELIGHT] = shade(1.1, @bg_color)
- fg[PRELIGHT] = @selected_fg_color
-
- engine "murrine" {
- trough_shades = {0.9, 0.98}
- roundness = 6
- lightborderstyle = 1
- lightborder_shade = 1.26
- border_shades = {0.8, 0.8}
- gradient_shades = {0.95, 1.1, 0.95, 1.1}
- #trough_border_shades = {0.9, 0.9}
- highlight_shade = 1.02
- contrast = 1.1
- reliefstyle = 1
- }
-}
-
-style "frame"
-{
-}
-
-style "frame-title" = "frame"
-{
- fg[NORMAL] = lighter (@fg_color)
-}
-
-style "font"
-{
- font_name="9"
-}
-widget_class "*" style "font"
-
-#########################################
-# Matches
-#########################################
-
-# default style is applied to every widget
-class "GtkWidget" style "default"
-
-# Increase the x/ythickness in some widgets
-class "GtkRange" style "default"
-class "GtkFrame" style "frame"
-class "GtkSeparator" style "wide"
-class "GtkEntry" style "entry"
-class "GtkStatusbar" style "statusbar"
-
-widget_class "*<GtkComboBoxEntry>*" style "comboboxentry"
-widget_class "*<GtkCombo>*" style "comboboxentry"
-
-# Toolbar default: light
-class "*HandleBox" style "toolbar"
-class "GtkToolbar" style "toolbar"
-widget_class "*HandleBox" style "toolbar"
-widget_class "*<GtkToolbar>.*" style "toolbar"
-
-#
-# Toolbar exceptions:
-# Browser-type and viewer-type applications get a dark toolbar.
-# Everything below the toolbar for these apps are the content. This will make
-# a separation on function (toolbar) and content (client area)
-
-# Work around for http://bugzilla.gnome.org/show_bug.cgi?id=382646
-style "text-is-fg-color-workaround"
-{
- text[NORMAL] = @fg_color
- text[PRELIGHT] = mix (0.8, @fg_color, '#ffffff')
- text[SELECTED] = @selected_fg_color
- text[ACTIVE] = @fg_color
- text[INSENSITIVE] = darker (@bg_color)
-}
-
-style "text-is-fg-color-workaround-dark"
-{
- #Make it work with this theme!
- text[NORMAL] = @bg_color
- text[PRELIGHT] = mix (1.0, @bg_color, '#ffffff')
-}
-
-# Workaround style for menus where the text color is used instead of the fg color.
-style "menuitem-text-is-fg-color-workaround" {
- text[NORMAL] = @fg_color
- text[PRELIGHT] = @fg_color
- text[SELECTED] = @selected_fg_color
- text[ACTIVE] = @fg_color
- text[INSENSITIVE] = darker (@bg_color)
-}
-
-# Work around for http://bugzilla.gnome.org/show_bug.cgi?id=382646
-# Note that this work around assumes that the combobox is _not_ in appears-as-list mode.
-widget_class "*.<GtkComboBox>.<GtkCellView>"style "text-is-fg-color-workaround"
-# This is the part of the workaround that fixes the menus
-widget "*.gtk-combobox-popup-menu.*" style "menuitem-text-is-fg-color-workaround"
-
-widget "*fullscreen-toolbar" style "dark-toolbar"
-widget "*fullscreen-toolbar.*" style "dark-toolbar"
-widget "*fullscreen-toolbar*.GtkComboBox.GtkCellView" style "text-is-fg-color-workaround-dark"
-
-class "GtkSpinButton" style "spinbutton"
-class "GtkScale" style "scale"
-class "GtkVScale" style "scale"
-class "GtkHScale" style "scale"
-class "GtkButton" style "button"
-
-# General matching following, the order is choosen so that the right styles override each other
-# eg. progressbar needs to be more important then the menu match.
-
-widget_class "*<GtkFrame>" style "frame"
-widget_class "*.<GtkFrame>.<GtkLabel>" style "frame-title"
-
-widget_class "*<GtkStatusbar>*" style "wider"
-widget_class "*<GtkProgressBar>" style "progressbar"
-
-# Treeview header
-widget_class "*.<GtkTreeView>.<GtkButton>" style "treeview-header"
-widget_class "*.<GtkCTree>.<GtkButton>" style "treeview-header"
-widget_class "*.<GtkList>.<GtkButton>" style "treeview-header"
-widget_class "*.<GtkCList>.<GtkButton>" style "treeview-header"
-
-###################################################
-# Special cases and work arounds
-###################################################
-
-# Work around the usage of GtkLabel inside GtkListItems to display text.
-# This breaks because the label is shown on a background that is based on the
-# base color set.
-style "fg-is-text-color-workaround"
-{
- fg[NORMAL] = @text_color
- fg[PRELIGHT] = @text_color
- fg[ACTIVE] = @selected_fg_color
- fg[SELECTED] = @selected_fg_color
- fg[INSENSITIVE] = darker (@bg_color)
-}
-widget_class "*<GtkListItem>*" style "fg-is-text-color-workaround"
-# The same problem also exists for GtkCList and GtkCTree
-# Only match GtkCList and not the parent widgets, because that would also change the headers.
-widget_class "*<GtkCList>" style "fg-is-text-color-workaround"
-
-style "dialog" = "dark"
-{
- bg[NORMAL] = mix(0.4, @selected_bg_color, shade(0.7, @bg_color))
- fg[NORMAL] = shade(0.5, @fg_color)
- text[NORMAL] = shade(0.5, @text_color)
-}
-style "dialog-button" = "dark"
-{
- bg[NORMAL] = shade(0.15, @bg_color)
- bg[PRELIGHT] = shade(0.18, @bg_color)
-}
-
-include"styles/checkradiobutton"
-include"styles/menu-menubar"
-include"styles/notebook"
-include"styles/scrollbar"
-include"styles/tooltips"
-
-style "gnome-color-chooser-combobox"
-{
- text[NORMAL] = @fg_color
- text[PRELIGHT] = mix (0.8, @fg_color, '#ffffff')
-}
-widget_class "*.<GtkComboBox>.<GtkCellView>" style "gnome-color-chooser-combobox"
-class "TerminalScreen" style "terminal"
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/checkradiobutton b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/checkradiobutton
deleted file mode 100644
index c62a9a8ab..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/checkradiobutton
+++ /dev/null
@@ -1,179 +0,0 @@
-style "checkbutton" = "default"
-{
- engine "pixmap"
- {
- image
- {
- function = CHECK
- recolorable = TRUE
- state = NORMAL
- shadow = OUT
- overlay_file = "Check-Radio/check1.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = PRELIGHT
- shadow = OUT
- overlay_file = "Check-Radio/check3.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = ACTIVE
- shadow = OUT
- overlay_file = "Check-Radio/check3.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = INSENSITIVE
- shadow = OUT
- overlay_file = "Check-Radio/check1.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = NORMAL
- shadow = IN
- overlay_file = "Check-Radio/check2.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = PRELIGHT
- shadow = IN
- overlay_file = "Check-Radio/check4.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = ACTIVE
- shadow = IN
- overlay_file = "Check-Radio/check4.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = CHECK
- recolorable = TRUE
- state = INSENSITIVE
- shadow = IN
- overlay_file = "Check-Radio/check5.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = FLAT_BOX
- recolorable = TRUE
- stretch = TRUE
- file = "Check-Radio/checklight.png"
- border = { 2, 2, 2, 2 }
- }
- }
-}
-
-style "radiobutton" = "default"
-{
- engine "pixmap"
- {
- image
- {
- function = OPTION
- recolorable = TRUE
- state = NORMAL
- shadow = OUT
- overlay_file = "Check-Radio/option1.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = PRELIGHT
- shadow = OUT
- overlay_file = "Check-Radio/option3.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = ACTIVE
- shadow = OUT
- overlay_file = "Check-Radio/option3.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = INSENSITIVE
- shadow = OUT
- overlay_file = "Check-Radio/option1.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = NORMAL
- shadow = IN
- overlay_file = "Check-Radio/option2.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = PRELIGHT
- shadow = IN
- overlay_file = "Check-Radio/option4.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = ACTIVE
- shadow = IN
- overlay_file = "Check-Radio/option4.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = OPTION
- recolorable = TRUE
- state = INSENSITIVE
- shadow = IN
- overlay_file = "Check-Radio/option1.png"
- overlay_stretch = FALSE
- }
- image
- {
- function = FLAT_BOX
- recolorable = TRUE
- stretch = TRUE
- file = "Check-Radio/checklight.png"
- border = { 2, 2, 2, 2 }
- }
- }
-}
-
-
-class "GtkRadioButton" style "radiobutton"
-class "GtkRadioMenuItem" style "radiobutton"
-class "GtkCheckButton" style "checkbutton"
-class "GtkCheckMenuItem" style "checkbutton"
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/menu-menubar b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/menu-menubar
deleted file mode 100644
index a15363a9b..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/menu-menubar
+++ /dev/null
@@ -1,50 +0,0 @@
-style "menu" = "default"
-{
- xthickness = 2
- ythickness = 2
-
- bg[PRELIGHT] = shade (1.04, @selected_bg_color)
-}
-
-style "menuitem"
-{
- xthickness = 0
- ythickness = 0
-
- engine "murrine" {
- roundness = 1
- border_colors = {"#7C7C7C", "#7C7C7C"}
- border_shades = {1.0, 1.0} # draw a gradient on the border.
- gradient_shades = {1.0,1.0,1.0,1.0}
- }
-}
-
-style "menubar" = "dark-toolbar"
-{
- ythickness = 0
- xthickness = 0
-
- bg[PRELIGHT] = shade (1.04, @selected_bg_color)
-
- engine "murrine" {
- roundness = 1
- border_shades = {1.0, 1.0} # draw a gradient on the border.
- border_colors = {"#7C7C7C", "#7C7C7C"}
- glowstyle = 0
- gradient_shades = {1.0,1.0,1.0,1.0}
- highlight_shade = 1.0
- lightborder_shade = 1.0
- reliefstyle = 0 # 0 = flat, 1 = inset, 2 = shadow, = 3 for a gradient on shadow, = 4 for a stronger shadow.
- }
-}
-
-class "GtkMenu" style "menu"
-class "GtkMenuBar*" style "menubar"
-class "GtkMenuItem" style "menuitem"
-class "GtkTearoffMenuItem" style "menuitem"
-
-widget_class "*GtkMenu.*" style "menu"
-widget_class "*MenuBar.*" style "menubar"
-widget_class "*.<MenuItem>." style "menuitem"
-# The panel menubar
-widget_class "*Panel*<GtkMenuBar>*" style "menubar"
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/notebook b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/notebook
deleted file mode 100644
index 4c52077ed..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/notebook
+++ /dev/null
@@ -1,29 +0,0 @@
-style "notebook-close" {
- stock["gtk-close"] = {
- { "Icons/close.png", *, *, * }
- }
-}
-
-style "notebook" = "wider" {
-
- bg[NORMAL] = shade (1.0615, @bg_color)
- bg[ACTIVE] = shade (0.85, @bg_color)
-
- engine "murrine" {
-
- lightborder_shade = 1.1
- highlight_shade = 1.01
- }
-}
-
-style "notebookthin" = "notebook" {
-
- xthickness = 2
- ythickness = 2
-}
-
-widget_class "*<GtkNotebook>*<GtkEventBox>" style "notebook"
-widget_class "*<GtkNotebook>*<GtkDrawingArea>" style "notebook"
-widget_class "*<GtkNotebook>*<GtkLayout>" style "notebook"
-widget_class "*<GtkNotebook>" style "notebook"
-widget_class "*<GtkNotebook>*" style "notebook-close"
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/scrollbar b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/scrollbar
deleted file mode 100644
index aa39423f5..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/scrollbar
+++ /dev/null
@@ -1,125 +0,0 @@
-style "scrollbar" = "default"
-{
- engine "pixmap"
- {
- image
- {
- function = BOX
- recolorable = TRUE
- detail = "trough"
- file = "Scrollbars/trough-scrollbar-horiz.png"
- border = { 20, 20, 0, 0 }
- stretch = TRUE
- orientation = HORIZONTAL
- }
- image
- {
- function = BOX
- recolorable = TRUE
- detail = "trough"
- file = "Scrollbars/trough-scrollbar-vert.png"
- border = { 0, 0, 20, 20 }
- stretch = TRUE
- orientation = VERTICAL
- }
-###########x SLIDERS ##################x
-
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = NORMAL
- file = "Scrollbars/slider-horiz.png"
- border = { 10, 10, 0, 0 }
- stretch = TRUE
- orientation = HORIZONTAL
- }
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = ACTIVE
- shadow = IN
- file = "Scrollbars/slider-horiz.png"
- border = { 10, 10, 0, 0 }
- stretch = TRUE
- orientation = HORIZONTAL
-
- }
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = PRELIGHT
- file = "Scrollbars/slider-horiz-prelight.png"
- border = { 10, 10, 0, 0 }
- stretch = TRUE
- orientation = HORIZONTAL
-
- }
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = INSENSITIVE
- file = "Scrollbars/slider-horiz-insens.png"
- border = { 10, 10, 0, 0 }
- stretch = TRUE
- orientation = HORIZONTAL
-
- }
-#############x verticals################xx
-
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = NORMAL
- file = "Scrollbars/slider-vert.png"
- border = { 0, 0, 10, 10 }
- stretch = TRUE
- orientation = VERTICAL
-
- }
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = ACTIVE
- shadow = IN
- file = "Scrollbars/slider-vert.png"
- border = { 0, 0, 10, 10 }
- stretch = TRUE
- orientation = VERTICAL
-
- }
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = PRELIGHT
- file = "Scrollbars/slider-vert-prelight.png"
- border = { 0, 0, 10, 10 }
- stretch = TRUE
- orientation = VERTICAL
-
- }
- image
- {
- function = SLIDER
- recolorable = TRUE
- state = INSENSITIVE
- file = "Scrollbars/slider-vert-insens.png"
- border = { 0, 0, 10, 10 }
- stretch = TRUE
- orientation = VERTICAL
-
- }
-
-########### END SLIDERS ##################
- }
-}
-
-class "GtkScrollbar" style "scrollbar"
-class "GtkVScrollbar" style "scrollbar"
-class "GtkHScrollbar" style "scrollbar"
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/tooltips b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/tooltips
deleted file mode 100644
index 6658f062f..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/styles/tooltips
+++ /dev/null
@@ -1,21 +0,0 @@
-style "tooltips"
-{
- xthickness = 4
- ythickness = 4
-
- GtkWidget::new-tooltip-style = 1
-
- bg[NORMAL] = @tooltip_bg_color
- fg[NORMAL] = @tooltip_fg_color
-}
-
-
-widget "gtk-tooltips" style "tooltips"
-class "*GtkTooltips*" style "tooltips"
-widget_class "*Tooltips*" style "tooltips"
-widget "*.nautilus-extra-view-widget" style:highest "tooltips"
-style "nautilusrename" {
-# fg[NORMAL] = "#e1e1e1"
-}
-
-widget_class "*.EelEditableLabel" style "nautilusrename"
diff --git a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/index.theme b/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/index.theme
deleted file mode 100644
index d5d631b0f..000000000
--- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/index.theme
+++ /dev/null
@@ -1,12 +0,0 @@
-[Desktop Entry]
-Version=1.2
-Encoding=UTF-8
-Name=DelugeStart
-Type=X-GNOME-Metatheme
-Comment=DelugeStart theme by alecive
-
-[X-GNOME-Metatheme]
-GtkTheme=DelugeStart
-IconTheme=AwOken-303030
-CursorTheme=DMZ-White
-ButtonLayout=menu:minimize,maximize,close
diff --git a/packaging/win32/Win32 README.txt b/packaging/win32/Win32 README.txt
deleted file mode 100644
index d74e02278..000000000
--- a/packaging/win32/Win32 README.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-= Deluge Installer for Windows =
-
-Instructions for building the Deluge NSIS Installer for Windows XP/Vista/7.
-
-== Dependencies ==
- * Deluge build: http://dev.deluge-torrent.org/wiki/Installing/Source#WindowsDependencies
- * Bbfreeze: http://pypi.python.org/pypi/bbfreeze
- * NSIS: http://nsis.sourceforge.net/Download
-
-== Build Steps ==
-
- 1. Build and Install Deluge on Windows.
-
- 2. Run the bbfreeze script from the win32 directory:
-
- python deluge-bbfreeze.py
-
- The result is a bbfreeze'd version of Deluge in `build-win32/deluge-bbfreeze-build_version`.
-
- 3. Run the NSIS script (right-click and choose `Compile with NSIS`)
-
- The result is a standalone installer in the `build-win32` directory.
diff --git a/packaging/win32/deluge-bbfreeze.py b/packaging/win32/deluge-bbfreeze.py
deleted file mode 100644
index 5cd3e35a7..000000000
--- a/packaging/win32/deluge-bbfreeze.py
+++ /dev/null
@@ -1,264 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2012-2015 Calum Lind <calumlind@gmail.com>
-# Copyright (C) 2010 Damien Churchill <damoxc@gmail.com>
-# Copyright (C) 2009-2010 Andrew Resch <andrewresch@gmail.com>
-# Copyright (C) 2009 Jesper Lund <mail@jesperlund.com>
-#
-# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
-# the additional special exception to link portions of this program with the OpenSSL library.
-# See LICENSE for more details.
-#
-# isort:skip_file
-from __future__ import print_function
-
-import glob
-import os
-import re
-import shutil
-import sys
-
-
-import bbfreeze
-import gtk
-from win32verstamp import stamp
-
-import deluge.common
-
-
-class VersionInfo(object):
- def __init__(
- self,
- version,
- internalname=None,
- originalfilename=None,
- comments=None,
- company=None,
- description=None,
- _copyright=None,
- trademarks=None,
- product=None,
- dll=False,
- debug=False,
- verbose=True,
- ):
- parts = version.split('.')
- while len(parts) < 4:
- parts.append('0')
- self.version = '.'.join(parts)
- self.internal_name = internalname
- self.original_filename = originalfilename
- self.comments = comments
- self.company = company
- self.description = description
- self.copyright = _copyright
- self.trademarks = trademarks
- self.product = product
- self.dll = dll
- self.debug = debug
- self.verbose = verbose
-
-
-DEBUG = False
-if len(sys.argv) == 2 and sys.argv[1].lower() == 'debug':
- DEBUG = True
-
-# Get build_version from installed deluge.
-build_version = deluge.common.get_version()
-python_path = os.path.dirname(sys.executable)
-if python_path.endswith('Scripts'):
- python_path = python_path[:-8]
-gtk_root = os.path.join(gtk.__path__[0], '..', 'runtime')
-build_dir = os.path.join('build-win32', 'deluge-bbfreeze-' + build_version)
-
-if DEBUG:
- print('Python Path: %s' % python_path)
- print('Gtk Path: %s' % gtk_root)
- print('bbfreeze Output Path: %s' % build_dir)
-
-print('Freezing Deluge %s...' % build_version)
-# Disable printing to console for bbfreezing.
-if not DEBUG:
- sys.stdout = open(os.devnull, 'w')
-
-# Include python modules not picked up automatically by bbfreeze.
-includes = (
- 'libtorrent',
- 'cairo',
- 'pangocairo',
- 'atk',
- 'pango',
- 'twisted.internet.utils',
- 'gio',
- 'gzip',
- 'email.mime.multipart',
- 'email.mime.text',
- '_cffi_backend',
-)
-excludes = ('numpy', 'OpenGL', 'psyco', 'win32ui', 'unittest')
-
-
-def recipe_gtk_override(mf):
- # Override bbfreeze function so that it includes all gtk libraries
- # in the installer so users don't require a separate GTK+ installation.
- return True
-
-
-bbfreeze.recipes.recipe_gtk_and_friends = recipe_gtk_override
-
-# Workaround for "ImportError: The 'packaging' package is required" with setuptools > 18.8.
-# (https://github.com/pypa/setuptools/issues/517)
-bbfreeze.recipes.recipe_pkg_resources = bbfreeze.recipes.include_whole_package(
- 'pkg_resources'
-)
-
-fzr = bbfreeze.Freezer(build_dir, includes=includes, excludes=excludes)
-fzr.include_py = False
-fzr.setIcon(
- os.path.join(
- os.path.dirname(deluge.common.__file__), 'ui', 'data', 'pixmaps', 'deluge.ico'
- )
-)
-
-# TODO: Can/should we grab the script list from setup.py entry_points somehow.
-
-# Hide cmd console popup for these console entries force gui_script True.
-force_gui = ['deluge-web', 'deluged']
-
-for force_script in force_gui:
- script_path = os.path.join(python_path, 'Scripts', force_script + '-script.py')
- shutil.copy(script_path, script_path.replace('script', 'debug-script'))
-
-script_list = []
-for script in glob.glob(os.path.join(python_path, 'Scripts\\deluge*-script.py*')):
- # Copy the scripts to remove the '-script' suffix before adding to freezer.
- new_script = script.replace('-script', '')
- shutil.copy(script, new_script)
-
- gui_script = False
- script_splitext = os.path.splitext(os.path.basename(new_script))
- if script_splitext[1] == '.pyw' or script_splitext[0] in force_gui:
- gui_script = True
- try:
- fzr.addScript(new_script, gui_only=gui_script)
- script_list.append(new_script)
- except Exception:
- os.remove(script)
-
-# Start the freezing process.
-fzr()
-
-# Clean up the duplicated scripts.
-for script in script_list:
- os.remove(script)
-
-# Exclude files which are already included in GTK or Windows. Also exclude unneeded pygame dlls.
-exclude_dlls = (
- 'MSIMG32.dll',
- 'MSVCR90.dll',
- 'MSVCP90.dll',
- 'MSVCR120.dll',
- 'POWRPROF.dll',
- 'DNSAPI.dll',
- 'USP10.dll',
- 'MPR.dll',
- 'jpeg.dll',
- 'libfreetype-6.dll',
- 'libpng12-0.dll',
- 'libtiff.dll',
- 'SDL_image.dll',
- 'SDL_ttf.dll',
-)
-for exclude_dll in exclude_dlls:
- try:
- os.remove(os.path.join(build_dir, exclude_dll))
- except OSError:
- pass
-
-# Re-enable printing.
-if not DEBUG:
- sys.stdout = sys.__stdout__
-
-# Copy gtk locale files.
-gtk_locale = os.path.join(gtk_root, 'share/locale')
-locale_include_list = ['gtk20.mo', 'locale.alias']
-
-
-def ignored_files(adir, ignore_filenames):
- return [
- ignore_file
- for ignore_file in ignore_filenames
- if not os.path.isdir(os.path.join(adir, ignore_file))
- and ignore_file not in locale_include_list
- ]
-
-
-shutil.copytree(
- gtk_locale, os.path.join(build_dir, 'share/locale'), ignore=ignored_files
-)
-
-# Copy gtk theme files.
-theme_include_list = [
- [gtk_root, 'share/icons/hicolor/index.theme'],
- [gtk_root, 'lib/gtk-2.0/2.10.0/engines'],
- [gtk_root, 'share/themes/MS-Windows'],
- ['DelugeStart Theme', 'lib/gtk-2.0/2.10.0/engines/libmurrine.dll'],
- ['DelugeStart Theme', 'share/themes/DelugeStart'],
- ['DelugeStart Theme', 'etc/gtk-2.0/gtkrc'],
-]
-for path_root, path in theme_include_list:
- full_path = os.path.join(path_root, path)
- if os.path.isdir(full_path):
- shutil.copytree(full_path, os.path.join(build_dir, path))
- else:
- dst_dir = os.path.join(build_dir, os.path.dirname(path))
- try:
- os.makedirs(dst_dir)
- except OSError:
- pass
- shutil.copy(full_path, dst_dir)
-
-# Add version information to exe files.
-for script in script_list:
- script_exe = os.path.splitext(os.path.basename(script))[0] + '.exe'
- # Don't add to dev build versions.
- if not re.search('[a-zA-Z_-]', build_version):
- version_info = VersionInfo(
- build_version,
- description='Deluge Bittorrent Client',
- company='Deluge Team',
- product='Deluge',
- _copyright='Deluge Team',
- )
- stamp(os.path.join(build_dir, script_exe), version_info)
-
-# Copy version info to file for nsis script.
-with open('VERSION.tmp', 'w') as ver_file:
- ver_file.write('build_version = "%s"' % build_version)
-
-# Create the install and uninstall file list for NSIS.
-filedir_list = []
-for root, dirnames, filenames in os.walk(build_dir):
- dirnames.sort()
- filenames.sort()
- filedir_list.append((root[len(build_dir) :], filenames))
-
-with open('install_files.nsh', 'w') as f:
- f.write('; Files to install\n')
- for dirname, files in filedir_list:
- if not dirname:
- dirname = os.sep
- f.write('\nSetOutPath "$INSTDIR%s"\n' % dirname)
- for filename in files:
- f.write('File "${BBFREEZE_DIR}%s"\n' % os.path.join(dirname, filename))
-
-with open('uninstall_files.nsh', 'w') as f:
- f.write('; Files to uninstall\n')
- for dirname, files in reversed(filedir_list):
- f.write('\n')
- if not dirname:
- dirname = os.sep
- for filename in files:
- f.write('Delete "$INSTDIR%s"\n' % os.path.join(dirname, filename))
- f.write('RMDir "$INSTDIR%s"\n' % dirname)
diff --git a/pyproject.toml b/pyproject.toml
index 2674219a7..169561f13 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,13 +1,29 @@
[build-system]
-requires = [
- "setuptools",
- "wheel",
-]
+requires = ["setuptools", "wheel"]
[tool.black]
skip-string-normalization = true
[tool.isort]
profile = "black"
-# Python 2 stdlib
-extra_standard_library = ["urlparse", "HTMLParser", "urllib2"]
+
+[tool.pytest.ini_options]
+# Dump tracebacks if a test takes longer than X seconds
+faulthandler_timeout = 60
+# Hide logged warnings and errors in test output.
+log_cli_level = "CRITICAL"
+addopts = "--basetemp=_pytest_temp"
+markers = [
+ "todo: Tests that are yet to be written",
+ "gtkui: Tests for GTK code",
+ "security: Security related tests",
+ "slow: Tests that are particularly slow",
+ "internet: Tests that require internet connectivity",
+]
+filterwarnings = [
+ "ignore::DeprecationWarning:gi",
+ "ignore::DeprecationWarning:twisted.internet.gireactor",
+ "ignore:twisted.web.resource.*:DeprecationWarning",
+ "ignore:Using readBody.*:DeprecationWarning",
+ "ignore:resume_data is deprecated.*:DeprecationWarning:deluge.core.alertmanager",
+]
diff --git a/requirements-ci.txt b/requirements-ci.txt
new file mode 100644
index 000000000..0ebcd6077
--- /dev/null
+++ b/requirements-ci.txt
@@ -0,0 +1,4 @@
+-r requirements.txt
+-r requirements-tests.txt
+libtorrent==2.0.7
+pytest==7.4.2
diff --git a/requirements-dev.txt b/requirements-dev.txt
index c66dd799f..7e1194457 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -11,3 +11,4 @@ flake8-comprehensions
flake8-debugger
flake8-mock
flake8-mutable
+rjsmin
diff --git a/requirements-tests.txt b/requirements-tests.txt
index 705d96774..a570108ad 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -1,4 +1,5 @@
-pytest != 5.2.3, < 5.4
+libtorrent
+pytest
pytest-twisted
pytest-cov
mock
@@ -9,3 +10,4 @@ flake8-isort
pep8-naming
mccabe
pylint
+asyncmock; python_version <= '3.7'
diff --git a/requirements.txt b/requirements.txt
index c414829b9..a26ec758d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,14 +1,18 @@
+libtorrent
twisted[tls]>=17.1
rencode
pyopenssl
pyxdg
pillow
+pillow<=9; python_version=="3.7"
mako
+setuptools
chardet
-six
setproctitle
pywin32; sys_platform == 'win32'
certifi; sys_platform == 'win32'
windows-curses; sys_platform == 'win32'
zope.interface>=4.4.2
distro; 'linux' in sys_platform or 'bsd' in sys_platform
+pygeoip
+ifaddr>=0.2.0
diff --git a/setup.cfg b/setup.cfg
index b4071f53e..9403fb0cb 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -27,15 +27,19 @@ frameworks = CoreFoundation, Foundation, AppKit
[flake8]
max-line-length = 120
builtins = _,_n,__request__
-exclude = .git,.tox,.eggs,dist,build
-ignore =
-# A003 Class attribute is a python builtin.
+extend-exclude = dist,build
+extend-ignore =
+# flake8-builtins: A003 class attribute is shadowing a python builtin
A003,
-# C813, C815, C816: PY3 missing trailing commas.
- C813,C815,C816,
-# W503 line break before binary operator.
- W503,
- E203
-
+# E203 whitespace before ':'
+ E203,
+# N818 pep8-naming: error suffix in exception names
+ N818
+per-file-ignores =
+# import not top of file (gi checks required before import)
+ deluge/ui/gtk3/*.py : E402
+ deluge/**/gtkui.py: E402
+ deluge/**/gtkui/*.py: E402
+ deluge/plugins/Stats/deluge_stats/graph.py: E402
[pycodestyle]
max-line-length = 88
diff --git a/setup.py b/setup.py
index 4727417b3..ef70f20b0 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
@@ -9,20 +8,16 @@
# See LICENSE for more details.
#
-from __future__ import print_function
-
import glob
import os
import platform
import sys
-from distutils import cmd
from distutils.command.build import build as _build
from distutils.command.clean import clean as _clean
from distutils.command.install_data import install_data as _install_data
-from distutils.spawn import find_executable
-from shutil import rmtree
+from shutil import rmtree, which
-from setuptools import find_packages, setup
+from setuptools import Command, find_packages, setup
from setuptools.command.test import test as _test
import msgfmt
@@ -32,7 +27,7 @@ try:
from sphinx.setup_command import BuildDoc
except ImportError:
- class BuildDoc(object):
+ class BuildDoc:
pass
@@ -45,7 +40,7 @@ def osx_check():
desktop_data = 'deluge/ui/data/share/applications/deluge.desktop'
-appdata_data = 'deluge/ui/data/share/appdata/deluge.appdata.xml'
+metainfo_data = 'deluge/ui/data/share/metainfo/deluge.metainfo.xml'
# Variables for setuptools.setup
_package_data = {}
@@ -72,7 +67,7 @@ class PyTest(_test):
sys.exit(errcode)
-class CleanDocs(cmd.Command):
+class CleanDocs(Command):
description = 'Clean the documentation build and module rst files'
user_options = []
@@ -84,7 +79,7 @@ class CleanDocs(cmd.Command):
def run(self):
docs_build = 'docs/build'
- print('Deleting {}'.format(docs_build))
+ print(f'Deleting {docs_build}')
try:
rmtree(docs_build)
except OSError:
@@ -94,7 +89,7 @@ class CleanDocs(cmd.Command):
os.remove(module)
-class BuildWebUI(cmd.Command):
+class BuildWebUI(Command):
description = 'Minify WebUI files'
user_options = []
@@ -145,7 +140,7 @@ class BuildWebUI(cmd.Command):
create_gettext_js(deluge_all_path)
-class CleanWebUI(cmd.Command):
+class CleanWebUI(Command):
description = 'Clean the documentation build and rst files'
user_options = []
@@ -162,7 +157,7 @@ class CleanWebUI(cmd.Command):
for js_src_dir in BuildWebUI.JS_SRC_DIRS:
for file_type in ('.js', '-debug.js'):
js_file = os.path.join(js_basedir, js_src_dir + file_type)
- print('Deleting {}'.format(js_file))
+ print(f'Deleting {js_file}')
try:
os.remove(js_file)
except OSError:
@@ -170,14 +165,14 @@ class CleanWebUI(cmd.Command):
# Remove generated gettext.js
js_file = os.path.join(js_basedir, 'gettext.js')
- print('Deleting {}'.format(js_file))
+ print(f'Deleting {js_file}')
try:
os.remove(js_file)
except OSError:
pass
-class BuildTranslations(cmd.Command):
+class BuildTranslations(Command):
description = 'Compile .po files into .mo files & create .desktop file'
user_options = [
@@ -202,9 +197,9 @@ class BuildTranslations(cmd.Command):
basedir = os.path.join(self.build_lib, 'deluge', 'i18n')
intltool_merge = 'intltool-merge'
- if not windows_check() and find_executable(intltool_merge):
+ if not windows_check() and which(intltool_merge):
intltool_merge_opts = '--utf8 --quiet'
- for data_file in (desktop_data, appdata_data):
+ for data_file in (desktop_data, metainfo_data):
# creates the translated file from .in file.
in_file = data_file + '.in'
if 'xml' in data_file:
@@ -246,11 +241,11 @@ class BuildTranslations(cmd.Command):
upto_date = True
if upto_date:
- sys.stdout.write(' po files already upto date. ')
+ sys.stdout.write(' po files already up to date. ')
sys.stdout.write('\b\b \nFinished compiling translation files. \n')
-class CleanTranslations(cmd.Command):
+class CleanTranslations(Command):
description = 'Cleans translations files.'
user_options = [
('all', 'a', 'Remove all build output, not just temporary by-products')
@@ -264,13 +259,13 @@ class CleanTranslations(cmd.Command):
self.set_undefined_options('clean', ('all', 'all'))
def run(self):
- for path in (desktop_data, appdata_data):
+ for path in (desktop_data, metainfo_data):
if os.path.isfile(path):
print('Deleting %s' % path)
os.remove(path)
-class BuildPlugins(cmd.Command):
+class BuildPlugins(Command):
description = 'Build plugins into .eggs'
user_options = [
@@ -314,7 +309,7 @@ class BuildPlugins(cmd.Command):
)
-class CleanPlugins(cmd.Command):
+class CleanPlugins(Command):
description = 'Cleans the plugin folders'
user_options = [
('all', 'a', 'Remove all build output, not just temporary by-products')
@@ -361,7 +356,7 @@ class CleanPlugins(cmd.Command):
os.removedirs(path)
-class EggInfoPlugins(cmd.Command):
+class EggInfoPlugins(Command):
description = 'Create .egg-info directories for plugins'
user_options = []
@@ -394,7 +389,7 @@ class Build(_build):
try:
from deluge._libtorrent import LT_VERSION
- print('Info: Found libtorrent ({}) installed.'.format(LT_VERSION))
+ print(f'Info: Found libtorrent ({LT_VERSION}) installed.')
except ImportError as ex:
print('Warning: libtorrent (libtorrent-rasterbar) not found: %s' % ex)
@@ -459,7 +454,7 @@ if not windows_check() and not osx_check():
for icon_path in glob.glob('deluge/ui/data/icons/hicolor/*x*'):
size = os.path.basename(icon_path)
icons = glob.glob(os.path.join(icon_path, 'apps', 'deluge*.png'))
- _data_files.append(('share/icons/hicolor/{}/apps'.format(size), icons))
+ _data_files.append((f'share/icons/hicolor/{size}/apps', icons))
_data_files.extend(
[
(
@@ -481,14 +476,24 @@ if not windows_check() and not osx_check():
)
if os.path.isfile(desktop_data):
_data_files.append(('share/applications', [desktop_data]))
- if os.path.isfile(appdata_data):
- _data_files.append(('share/appdata', [appdata_data]))
+ if os.path.isfile(metainfo_data):
+ _data_files.append(('share/metainfo', [metainfo_data]))
+
+# Entry Points
_entry_points['console_scripts'] = [
'deluge-console = deluge.ui.console:start',
+]
+
+# On Windows use gui_scripts to hide cmd popup (no effect on Linux/MacOS)
+_entry_points['gui_scripts'] = [
+ 'deluge = deluge.ui.ui_entry:start_ui',
+ 'deluge-gtk = deluge.ui.gtk3:start',
'deluge-web = deluge.ui.web:start',
'deluged = deluge.core.daemon_entry:start_daemon',
]
+
+# Provide Windows 'debug' exes for stdin/stdout e.g. logging/errors
if windows_check():
_entry_points['console_scripts'].extend(
[
@@ -497,10 +502,7 @@ if windows_check():
'deluged-debug = deluge.core.daemon_entry:start_daemon',
]
)
-_entry_points['gui_scripts'] = [
- 'deluge = deluge.ui.ui_entry:start_ui',
- 'deluge-gtk = deluge.ui.gtk3:start',
-]
+
_entry_points['deluge.ui'] = [
'console = deluge.ui.console:Console',
'web = deluge.ui.web:Web',
@@ -544,7 +546,6 @@ install_requires = [
'pyopenssl',
'pyxdg',
'mako',
- 'six',
'setuptools',
"pywin32; sys_platform == 'win32'",
"certifi; sys_platform == 'win32'",
@@ -555,6 +556,7 @@ extras_require = {
'setproctitle',
'pillow',
'chardet',
+ 'ifaddr',
]
}
@@ -595,7 +597,7 @@ setup(
'Operating System :: POSIX',
'Topic :: Internet',
],
- python_requires='>=2.7',
+ python_requires='>=3.6',
license='GPLv3+',
cmdclass=cmdclass,
setup_requires=setup_requires,
diff --git a/tox.ini b/tox.ini
index 5b9152978..3756a5406 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,20 +4,9 @@
# Usage: `pip install tox` and then run `tox` from this directory.
[tox]
-envlist = py27, py3, lint, docs
+envlist = py3, lint, docs
minversion=3.0
-[pytest]
-# Hide logged warnings and errors in test output.
-log_cli_level = CRITICAL
-addopts = -p no:warnings --basetemp=_pytest_temp
-markers =
- todo: Tests that are yet to be written
- gtkui: Tests for GTK code
- security: Security related tests
- slow: Tests that are particularly slow
- internet: Tests that require internet connectivity
-
# =================
# Base dependencies
# =================
@@ -56,7 +45,7 @@ deps =
[testenv]
install_command = python -m pip install --ignore-installed {opts} {packages}
-passenv = DISPLAY PYTHONPATH APPDATA
+passenv = DISPLAY,PYTHONPATH,APPDATA
setenv =
PYTHONPATH = {toxinidir}
PYTEST_ADDOPTS = --verbose --capture=no
@@ -81,12 +70,6 @@ commands = pytest -m "gtkui" deluge/tests
[testenv:todo]
commands = pytest -m "todo" deluge/tests
-[testenv:trial]
-setenv = {[testenv]setenv}{:}{toxinidir}/deluge/tests
-commands =
- python -c "import libtorrent as lt; print(lt.__version__)"
- python -m twisted.trial --reporter=deluge-reporter deluge.tests
-
[testenv:plugins]
setenv = PYTHONPATH = {toxinidir}{:}{toxinidir}/deluge/plugins
commands =
@@ -107,7 +90,7 @@ commands =
[testenv:lint]
sitepackages = False
-passenv = HOMEPATH SSH_AUTH_SOCK
+passenv = HOMEPATH,SSH_AUTH_SOCK
deps = {[baselint]deps}
commands = pre-commit run --all-files
diff --git a/version.py b/version.py
index ff8f684d5..26098605d 100755
--- a/version.py
+++ b/version.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
# Authors: Douglas Creager <dcreager@dcreager.net>
# Calum Lind <calumlind@gmail.com>
#
@@ -31,8 +30,6 @@
# include RELEASE-VERSION
#
-from __future__ import print_function, unicode_literals
-
import os
import subprocess
@@ -58,9 +55,9 @@ def call_git_describe(prefix='', suffix=''):
def get_version(prefix='deluge-', suffix='.dev0'):
try:
- with open(VERSION_FILE, 'r') as f:
+ with open(VERSION_FILE) as f:
release_version = f.readline().strip()
- except IOError:
+ except OSError:
release_version = None
version = call_git_describe(prefix, suffix)