diff options
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] @@ -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 @@ -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 Binary files differindex 9441214cb..f1a02e756 100644 --- a/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.png +++ b/deluge/plugins/Blocklist/deluge_blocklist/data/blocklist_import24.png 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 Binary files differdeleted file mode 100644 index 841e52871..000000000 --- a/deluge/tests/data/seo.ico +++ /dev/null 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 Binary files differnew file mode 100644 index 000000000..e58057cc2 --- /dev/null +++ b/deluge/tests/data/v2_hybrid.torrent diff --git a/deluge/tests/data/v2_test.torrent b/deluge/tests/data/v2_test.torrent Binary files differnew file mode 100644 index 000000000..fe6cbd044 --- /dev/null +++ b/deluge/tests/data/v2_test.torrent 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 Binary files differindex 48fcc473e..32aa26ae2 100644 --- a/deluge/ui/data/icons/hicolor/128x128/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/128x128/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.png b/deluge/ui/data/icons/hicolor/16x16/apps/deluge-panel.png Binary files differindex 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 diff --git a/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png b/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png Binary files differindex 2f4ae4c70..1291dfaeb 100644 --- a/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/16x16/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png b/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png Binary files differindex 5d54ea41e..93c6feb44 100644 --- a/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/192x192/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.png b/deluge/ui/data/icons/hicolor/22x22/apps/deluge-panel.png Binary files differindex 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 diff --git a/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png b/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png Binary files differindex 13fe852ad..95b13e359 100644 --- a/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/22x22/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.png b/deluge/ui/data/icons/hicolor/24x24/apps/deluge-panel.png Binary files differindex 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 diff --git a/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png b/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png Binary files differindex 3a345eb44..96ab75325 100644 --- a/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/24x24/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png b/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png Binary files differindex ee5d2909b..4ba3bb997 100644 --- a/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/256x256/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png b/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png Binary files differindex 6787fa39a..916f13632 100644 --- a/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/32x32/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png b/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png Binary files differindex 4050041f5..92d35fa0f 100644 --- a/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/36x36/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png b/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png Binary files differindex 7b067ac94..95592bb49 100644 --- a/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/48x48/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png b/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png Binary files differindex 70cd91af5..464dd69f5 100644 --- a/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/512x512/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png b/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png Binary files differindex 427556373..fb87321a5 100644 --- a/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/64x64/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png b/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png Binary files differindex 7ba0efb1c..f313ed32c 100644 --- a/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/72x72/apps/deluge.png diff --git a/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png b/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png Binary files differindex 2c64ec8b9..9b8d3abfe 100644 --- a/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png +++ b/deluge/ui/data/icons/hicolor/96x96/apps/deluge.png diff --git a/deluge/ui/data/pixmaps/active16.png b/deluge/ui/data/pixmaps/active16.png Binary files differindex c9af82a5f..e5bab66cd 100644 --- a/deluge/ui/data/pixmaps/active16.png +++ b/deluge/ui/data/pixmaps/active16.png diff --git a/deluge/ui/data/pixmaps/alert16.png b/deluge/ui/data/pixmaps/alert16.png Binary files differindex 703663813..49028db9c 100644 --- a/deluge/ui/data/pixmaps/alert16.png +++ b/deluge/ui/data/pixmaps/alert16.png diff --git a/deluge/ui/data/pixmaps/all16.png b/deluge/ui/data/pixmaps/all16.png Binary files differindex c63f8df1a..1a9ba9092 100644 --- a/deluge/ui/data/pixmaps/all16.png +++ b/deluge/ui/data/pixmaps/all16.png diff --git a/deluge/ui/data/pixmaps/checking16.png b/deluge/ui/data/pixmaps/checking16.png Binary files differindex 6758e36e8..9961675c4 100644 --- a/deluge/ui/data/pixmaps/checking16.png +++ b/deluge/ui/data/pixmaps/checking16.png diff --git a/deluge/ui/data/pixmaps/deluge-about.png b/deluge/ui/data/pixmaps/deluge-about.png Binary files differindex 39322eb71..0b4ff4e6b 100644 --- a/deluge/ui/data/pixmaps/deluge-about.png +++ b/deluge/ui/data/pixmaps/deluge-about.png diff --git a/deluge/ui/data/pixmaps/deluge.ico b/deluge/ui/data/pixmaps/deluge.ico Binary files differindex d946d1114..854f867a5 100644 --- a/deluge/ui/data/pixmaps/deluge.ico +++ b/deluge/ui/data/pixmaps/deluge.ico diff --git a/deluge/ui/data/pixmaps/deluge.png b/deluge/ui/data/pixmaps/deluge.png Binary files differindex 7b067ac94..95592bb49 100644 --- a/deluge/ui/data/pixmaps/deluge.png +++ b/deluge/ui/data/pixmaps/deluge.png diff --git a/deluge/ui/data/pixmaps/deluge16.png b/deluge/ui/data/pixmaps/deluge16.png Binary files differindex 5afdbe4c6..1291dfaeb 100644 --- a/deluge/ui/data/pixmaps/deluge16.png +++ b/deluge/ui/data/pixmaps/deluge16.png diff --git a/deluge/ui/data/pixmaps/dht16.png b/deluge/ui/data/pixmaps/dht16.png Binary files differindex 363ee0c0f..2396bb132 100644 --- a/deluge/ui/data/pixmaps/dht16.png +++ b/deluge/ui/data/pixmaps/dht16.png diff --git a/deluge/ui/data/pixmaps/downloading16.png b/deluge/ui/data/pixmaps/downloading16.png Binary files differindex 24d6ffa38..e64aa5adc 100644 --- a/deluge/ui/data/pixmaps/downloading16.png +++ b/deluge/ui/data/pixmaps/downloading16.png diff --git a/deluge/ui/data/pixmaps/flags/ad.png b/deluge/ui/data/pixmaps/flags/ad.png Binary files differindex 93656aa35..cc5ab1e13 100644 --- a/deluge/ui/data/pixmaps/flags/ad.png +++ b/deluge/ui/data/pixmaps/flags/ad.png diff --git a/deluge/ui/data/pixmaps/flags/ae.png b/deluge/ui/data/pixmaps/flags/ae.png Binary files differindex e3abee163..590256f8a 100644 --- a/deluge/ui/data/pixmaps/flags/ae.png +++ b/deluge/ui/data/pixmaps/flags/ae.png diff --git a/deluge/ui/data/pixmaps/flags/af.png b/deluge/ui/data/pixmaps/flags/af.png Binary files differindex 8506736ad..d8faed3c9 100644 --- a/deluge/ui/data/pixmaps/flags/af.png +++ b/deluge/ui/data/pixmaps/flags/af.png diff --git a/deluge/ui/data/pixmaps/flags/ag.png b/deluge/ui/data/pixmaps/flags/ag.png Binary files differindex ba1aff986..7c6d7dbab 100644 --- a/deluge/ui/data/pixmaps/flags/ag.png +++ b/deluge/ui/data/pixmaps/flags/ag.png diff --git a/deluge/ui/data/pixmaps/flags/al.png b/deluge/ui/data/pixmaps/flags/al.png Binary files differindex 9b56fcba0..71f9d0aae 100644 --- a/deluge/ui/data/pixmaps/flags/al.png +++ b/deluge/ui/data/pixmaps/flags/al.png diff --git a/deluge/ui/data/pixmaps/flags/am.png b/deluge/ui/data/pixmaps/flags/am.png Binary files differindex 83ac72eb3..8bc88a26e 100644 --- a/deluge/ui/data/pixmaps/flags/am.png +++ b/deluge/ui/data/pixmaps/flags/am.png diff --git a/deluge/ui/data/pixmaps/flags/an.png b/deluge/ui/data/pixmaps/flags/an.png Binary files differindex 09cfdb128..49220a4c9 100644 --- a/deluge/ui/data/pixmaps/flags/an.png +++ b/deluge/ui/data/pixmaps/flags/an.png diff --git a/deluge/ui/data/pixmaps/flags/ao.png b/deluge/ui/data/pixmaps/flags/ao.png Binary files differindex c2004d618..9f1dcb46a 100644 --- a/deluge/ui/data/pixmaps/flags/ao.png +++ b/deluge/ui/data/pixmaps/flags/ao.png diff --git a/deluge/ui/data/pixmaps/flags/aq.png b/deluge/ui/data/pixmaps/flags/aq.png Binary files differindex 76fe736cb..de9bc8f2c 100644 --- a/deluge/ui/data/pixmaps/flags/aq.png +++ b/deluge/ui/data/pixmaps/flags/aq.png diff --git a/deluge/ui/data/pixmaps/flags/ar.png b/deluge/ui/data/pixmaps/flags/ar.png Binary files differindex f16a290b7..e3a87c893 100644 --- a/deluge/ui/data/pixmaps/flags/ar.png +++ b/deluge/ui/data/pixmaps/flags/ar.png diff --git a/deluge/ui/data/pixmaps/flags/as.png b/deluge/ui/data/pixmaps/flags/as.png Binary files differindex e38ce4323..1d00ab05a 100644 --- a/deluge/ui/data/pixmaps/flags/as.png +++ b/deluge/ui/data/pixmaps/flags/as.png diff --git a/deluge/ui/data/pixmaps/flags/at.png b/deluge/ui/data/pixmaps/flags/at.png Binary files differindex 25c7e421f..475bd128e 100644 --- a/deluge/ui/data/pixmaps/flags/at.png +++ b/deluge/ui/data/pixmaps/flags/at.png diff --git a/deluge/ui/data/pixmaps/flags/au.png b/deluge/ui/data/pixmaps/flags/au.png Binary files differindex 0de18f0b2..0335dbfc8 100644 --- a/deluge/ui/data/pixmaps/flags/au.png +++ b/deluge/ui/data/pixmaps/flags/au.png diff --git a/deluge/ui/data/pixmaps/flags/aw.png b/deluge/ui/data/pixmaps/flags/aw.png Binary files differindex 788d7385c..fc58befc3 100644 --- a/deluge/ui/data/pixmaps/flags/aw.png +++ b/deluge/ui/data/pixmaps/flags/aw.png diff --git a/deluge/ui/data/pixmaps/flags/ax.png b/deluge/ui/data/pixmaps/flags/ax.png Binary files differindex 0060c3566..134b2ac51 100644 --- a/deluge/ui/data/pixmaps/flags/ax.png +++ b/deluge/ui/data/pixmaps/flags/ax.png diff --git a/deluge/ui/data/pixmaps/flags/az.png b/deluge/ui/data/pixmaps/flags/az.png Binary files differindex cb9c9a171..e225bd227 100644 --- a/deluge/ui/data/pixmaps/flags/az.png +++ b/deluge/ui/data/pixmaps/flags/az.png diff --git a/deluge/ui/data/pixmaps/flags/ba.png b/deluge/ui/data/pixmaps/flags/ba.png Binary files differindex 160c5e2ab..bc8732550 100644 --- a/deluge/ui/data/pixmaps/flags/ba.png +++ b/deluge/ui/data/pixmaps/flags/ba.png diff --git a/deluge/ui/data/pixmaps/flags/bd.png b/deluge/ui/data/pixmaps/flags/bd.png Binary files differindex 10266cd50..23fea0e64 100644 --- a/deluge/ui/data/pixmaps/flags/bd.png +++ b/deluge/ui/data/pixmaps/flags/bd.png diff --git a/deluge/ui/data/pixmaps/flags/bf.png b/deluge/ui/data/pixmaps/flags/bf.png Binary files differindex 452329a01..aa7238bce 100644 --- a/deluge/ui/data/pixmaps/flags/bf.png +++ b/deluge/ui/data/pixmaps/flags/bf.png diff --git a/deluge/ui/data/pixmaps/flags/bh.png b/deluge/ui/data/pixmaps/flags/bh.png Binary files differindex 1b876e211..812126b66 100644 --- a/deluge/ui/data/pixmaps/flags/bh.png +++ b/deluge/ui/data/pixmaps/flags/bh.png diff --git a/deluge/ui/data/pixmaps/flags/bi.png b/deluge/ui/data/pixmaps/flags/bi.png Binary files differindex f4d9adf92..21ead6b8d 100644 --- a/deluge/ui/data/pixmaps/flags/bi.png +++ b/deluge/ui/data/pixmaps/flags/bi.png diff --git a/deluge/ui/data/pixmaps/flags/bj.png b/deluge/ui/data/pixmaps/flags/bj.png Binary files differindex 5740ccc5d..6b9a53899 100644 --- a/deluge/ui/data/pixmaps/flags/bj.png +++ b/deluge/ui/data/pixmaps/flags/bj.png diff --git a/deluge/ui/data/pixmaps/flags/bm.png b/deluge/ui/data/pixmaps/flags/bm.png Binary files differindex 85411bff3..c1859d1d7 100644 --- a/deluge/ui/data/pixmaps/flags/bm.png +++ b/deluge/ui/data/pixmaps/flags/bm.png diff --git a/deluge/ui/data/pixmaps/flags/bn.png b/deluge/ui/data/pixmaps/flags/bn.png Binary files differindex a0c223f48..a18d496f0 100644 --- a/deluge/ui/data/pixmaps/flags/bn.png +++ b/deluge/ui/data/pixmaps/flags/bn.png diff --git a/deluge/ui/data/pixmaps/flags/bo.png b/deluge/ui/data/pixmaps/flags/bo.png Binary files differindex c2ef0f42f..1b25cd93f 100644 --- a/deluge/ui/data/pixmaps/flags/bo.png +++ b/deluge/ui/data/pixmaps/flags/bo.png diff --git a/deluge/ui/data/pixmaps/flags/bs.png b/deluge/ui/data/pixmaps/flags/bs.png Binary files differindex 959252d65..dec86f3ac 100644 --- a/deluge/ui/data/pixmaps/flags/bs.png +++ b/deluge/ui/data/pixmaps/flags/bs.png diff --git a/deluge/ui/data/pixmaps/flags/bt.png b/deluge/ui/data/pixmaps/flags/bt.png Binary files differindex 2e1969652..3942fc987 100644 --- a/deluge/ui/data/pixmaps/flags/bt.png +++ b/deluge/ui/data/pixmaps/flags/bt.png diff --git a/deluge/ui/data/pixmaps/flags/bv.png b/deluge/ui/data/pixmaps/flags/bv.png Binary files differindex ae9060caf..faaa529d1 100644 --- a/deluge/ui/data/pixmaps/flags/bv.png +++ b/deluge/ui/data/pixmaps/flags/bv.png diff --git a/deluge/ui/data/pixmaps/flags/bw.png b/deluge/ui/data/pixmaps/flags/bw.png Binary files differindex 2dee7fb3c..d5e0284e4 100644 --- a/deluge/ui/data/pixmaps/flags/bw.png +++ b/deluge/ui/data/pixmaps/flags/bw.png diff --git a/deluge/ui/data/pixmaps/flags/by.png b/deluge/ui/data/pixmaps/flags/by.png Binary files differindex 29361d693..a6c5f6c16 100644 --- a/deluge/ui/data/pixmaps/flags/by.png +++ b/deluge/ui/data/pixmaps/flags/by.png diff --git a/deluge/ui/data/pixmaps/flags/bz.png b/deluge/ui/data/pixmaps/flags/bz.png Binary files differindex 88e4ea1b3..63f74285a 100644 --- a/deluge/ui/data/pixmaps/flags/bz.png +++ b/deluge/ui/data/pixmaps/flags/bz.png diff --git a/deluge/ui/data/pixmaps/flags/ca.png b/deluge/ui/data/pixmaps/flags/ca.png Binary files differindex 155eea8ce..1e53d20a9 100644 --- a/deluge/ui/data/pixmaps/flags/ca.png +++ b/deluge/ui/data/pixmaps/flags/ca.png diff --git a/deluge/ui/data/pixmaps/flags/cc.png b/deluge/ui/data/pixmaps/flags/cc.png Binary files differindex 93509ae3a..a518e487d 100644 --- a/deluge/ui/data/pixmaps/flags/cc.png +++ b/deluge/ui/data/pixmaps/flags/cc.png diff --git a/deluge/ui/data/pixmaps/flags/cf.png b/deluge/ui/data/pixmaps/flags/cf.png Binary files differindex 3951b789f..1b9ea85bd 100644 --- a/deluge/ui/data/pixmaps/flags/cf.png +++ b/deluge/ui/data/pixmaps/flags/cf.png diff --git a/deluge/ui/data/pixmaps/flags/cg.png b/deluge/ui/data/pixmaps/flags/cg.png Binary files differindex 6f32484ff..45422bba1 100644 --- a/deluge/ui/data/pixmaps/flags/cg.png +++ b/deluge/ui/data/pixmaps/flags/cg.png diff --git a/deluge/ui/data/pixmaps/flags/ci.png b/deluge/ui/data/pixmaps/flags/ci.png Binary files differindex ee1740685..5f670c7ec 100644 --- a/deluge/ui/data/pixmaps/flags/ci.png +++ b/deluge/ui/data/pixmaps/flags/ci.png diff --git a/deluge/ui/data/pixmaps/flags/ck.png b/deluge/ui/data/pixmaps/flags/ck.png Binary files differindex 043a12f7a..904192b4d 100644 --- a/deluge/ui/data/pixmaps/flags/ck.png +++ b/deluge/ui/data/pixmaps/flags/ck.png diff --git a/deluge/ui/data/pixmaps/flags/cl.png b/deluge/ui/data/pixmaps/flags/cl.png Binary files differindex 6c05438c1..afb270e7a 100644 --- a/deluge/ui/data/pixmaps/flags/cl.png +++ b/deluge/ui/data/pixmaps/flags/cl.png diff --git a/deluge/ui/data/pixmaps/flags/cm.png b/deluge/ui/data/pixmaps/flags/cm.png Binary files differindex e2a7c0a14..395dd9fac 100644 --- a/deluge/ui/data/pixmaps/flags/cm.png +++ b/deluge/ui/data/pixmaps/flags/cm.png diff --git a/deluge/ui/data/pixmaps/flags/cn.png b/deluge/ui/data/pixmaps/flags/cn.png Binary files differindex 5a893ee6b..28ab66c84 100644 --- a/deluge/ui/data/pixmaps/flags/cn.png +++ b/deluge/ui/data/pixmaps/flags/cn.png diff --git a/deluge/ui/data/pixmaps/flags/co.png b/deluge/ui/data/pixmaps/flags/co.png Binary files differindex 24eb981b0..d31a69d36 100644 --- a/deluge/ui/data/pixmaps/flags/co.png +++ b/deluge/ui/data/pixmaps/flags/co.png diff --git a/deluge/ui/data/pixmaps/flags/cr.png b/deluge/ui/data/pixmaps/flags/cr.png Binary files differindex 4efd96730..e892b41c0 100644 --- a/deluge/ui/data/pixmaps/flags/cr.png +++ b/deluge/ui/data/pixmaps/flags/cr.png diff --git a/deluge/ui/data/pixmaps/flags/cu.png b/deluge/ui/data/pixmaps/flags/cu.png Binary files differindex addfb8e73..64205edc3 100644 --- a/deluge/ui/data/pixmaps/flags/cu.png +++ b/deluge/ui/data/pixmaps/flags/cu.png diff --git a/deluge/ui/data/pixmaps/flags/cv.png b/deluge/ui/data/pixmaps/flags/cv.png Binary files differindex 6411a6279..54f1abdca 100644 --- a/deluge/ui/data/pixmaps/flags/cv.png +++ b/deluge/ui/data/pixmaps/flags/cv.png diff --git a/deluge/ui/data/pixmaps/flags/cx.png b/deluge/ui/data/pixmaps/flags/cx.png Binary files differindex 75c8bfaed..198e299e1 100644 --- a/deluge/ui/data/pixmaps/flags/cx.png +++ b/deluge/ui/data/pixmaps/flags/cx.png diff --git a/deluge/ui/data/pixmaps/flags/cy.png b/deluge/ui/data/pixmaps/flags/cy.png Binary files differindex bbacfbe0c..6993e6b6b 100644 --- a/deluge/ui/data/pixmaps/flags/cy.png +++ b/deluge/ui/data/pixmaps/flags/cy.png diff --git a/deluge/ui/data/pixmaps/flags/de.png b/deluge/ui/data/pixmaps/flags/de.png Binary files differindex 933014a30..3d078604c 100644 --- a/deluge/ui/data/pixmaps/flags/de.png +++ b/deluge/ui/data/pixmaps/flags/de.png diff --git a/deluge/ui/data/pixmaps/flags/dj.png b/deluge/ui/data/pixmaps/flags/dj.png Binary files differindex 883343a4a..ca361d0bd 100644 --- a/deluge/ui/data/pixmaps/flags/dj.png +++ b/deluge/ui/data/pixmaps/flags/dj.png diff --git a/deluge/ui/data/pixmaps/flags/dk.png b/deluge/ui/data/pixmaps/flags/dk.png Binary files differindex 408eea295..3b4d12d94 100644 --- a/deluge/ui/data/pixmaps/flags/dk.png +++ b/deluge/ui/data/pixmaps/flags/dk.png diff --git a/deluge/ui/data/pixmaps/flags/dm.png b/deluge/ui/data/pixmaps/flags/dm.png Binary files differindex e7671bc4b..2767c49bc 100644 --- a/deluge/ui/data/pixmaps/flags/dm.png +++ b/deluge/ui/data/pixmaps/flags/dm.png diff --git a/deluge/ui/data/pixmaps/flags/do.png b/deluge/ui/data/pixmaps/flags/do.png Binary files differindex deefc7724..df9b90d33 100644 --- a/deluge/ui/data/pixmaps/flags/do.png +++ b/deluge/ui/data/pixmaps/flags/do.png diff --git a/deluge/ui/data/pixmaps/flags/dz.png b/deluge/ui/data/pixmaps/flags/dz.png Binary files differindex 65028f9b5..a450eeffe 100644 --- a/deluge/ui/data/pixmaps/flags/dz.png +++ b/deluge/ui/data/pixmaps/flags/dz.png diff --git a/deluge/ui/data/pixmaps/flags/ec.png b/deluge/ui/data/pixmaps/flags/ec.png Binary files differindex 7f93b49f5..26b50cdb4 100644 --- a/deluge/ui/data/pixmaps/flags/ec.png +++ b/deluge/ui/data/pixmaps/flags/ec.png diff --git a/deluge/ui/data/pixmaps/flags/eg.png b/deluge/ui/data/pixmaps/flags/eg.png Binary files differindex 7219431c1..26e80b9f5 100644 --- a/deluge/ui/data/pixmaps/flags/eg.png +++ b/deluge/ui/data/pixmaps/flags/eg.png diff --git a/deluge/ui/data/pixmaps/flags/eh.png b/deluge/ui/data/pixmaps/flags/eh.png Binary files differindex ae7daca43..39982d297 100644 --- a/deluge/ui/data/pixmaps/flags/eh.png +++ b/deluge/ui/data/pixmaps/flags/eh.png diff --git a/deluge/ui/data/pixmaps/flags/er.png b/deluge/ui/data/pixmaps/flags/er.png Binary files differindex b3644d4a2..d30b89f4a 100644 --- a/deluge/ui/data/pixmaps/flags/er.png +++ b/deluge/ui/data/pixmaps/flags/er.png diff --git a/deluge/ui/data/pixmaps/flags/es.png b/deluge/ui/data/pixmaps/flags/es.png Binary files differindex 9cc55dd04..afcffef78 100644 --- a/deluge/ui/data/pixmaps/flags/es.png +++ b/deluge/ui/data/pixmaps/flags/es.png diff --git a/deluge/ui/data/pixmaps/flags/et.png b/deluge/ui/data/pixmaps/flags/et.png Binary files differindex f4356fa51..a2c5d3ba7 100644 --- a/deluge/ui/data/pixmaps/flags/et.png +++ b/deluge/ui/data/pixmaps/flags/et.png diff --git a/deluge/ui/data/pixmaps/flags/fi.png b/deluge/ui/data/pixmaps/flags/fi.png Binary files differindex 73f8c91de..c89702679 100644 --- a/deluge/ui/data/pixmaps/flags/fi.png +++ b/deluge/ui/data/pixmaps/flags/fi.png diff --git a/deluge/ui/data/pixmaps/flags/fj.png b/deluge/ui/data/pixmaps/flags/fj.png Binary files differindex 0544e7a95..805d3b2af 100644 --- a/deluge/ui/data/pixmaps/flags/fj.png +++ b/deluge/ui/data/pixmaps/flags/fj.png diff --git a/deluge/ui/data/pixmaps/flags/fo.png b/deluge/ui/data/pixmaps/flags/fo.png Binary files differindex b922a4f30..63ef1bdd4 100644 --- a/deluge/ui/data/pixmaps/flags/fo.png +++ b/deluge/ui/data/pixmaps/flags/fo.png diff --git a/deluge/ui/data/pixmaps/flags/fr.png b/deluge/ui/data/pixmaps/flags/fr.png Binary files differindex 59346a911..84dd91764 100644 --- a/deluge/ui/data/pixmaps/flags/fr.png +++ b/deluge/ui/data/pixmaps/flags/fr.png diff --git a/deluge/ui/data/pixmaps/flags/ga.png b/deluge/ui/data/pixmaps/flags/ga.png Binary files differindex 63cb01397..e584067d2 100644 --- a/deluge/ui/data/pixmaps/flags/ga.png +++ b/deluge/ui/data/pixmaps/flags/ga.png diff --git a/deluge/ui/data/pixmaps/flags/gb.png b/deluge/ui/data/pixmaps/flags/gb.png Binary files differindex 95007c7be..4a351b7ab 100644 --- a/deluge/ui/data/pixmaps/flags/gb.png +++ b/deluge/ui/data/pixmaps/flags/gb.png diff --git a/deluge/ui/data/pixmaps/flags/gd.png b/deluge/ui/data/pixmaps/flags/gd.png Binary files differindex 1c7de15f6..56f2d2b23 100644 --- a/deluge/ui/data/pixmaps/flags/gd.png +++ b/deluge/ui/data/pixmaps/flags/gd.png diff --git a/deluge/ui/data/pixmaps/flags/ge.png b/deluge/ui/data/pixmaps/flags/ge.png Binary files differindex 44685b656..e2389a15a 100644 --- a/deluge/ui/data/pixmaps/flags/ge.png +++ b/deluge/ui/data/pixmaps/flags/ge.png diff --git a/deluge/ui/data/pixmaps/flags/gf.png b/deluge/ui/data/pixmaps/flags/gf.png Binary files differindex 59346a911..84dd91764 100644 --- a/deluge/ui/data/pixmaps/flags/gf.png +++ b/deluge/ui/data/pixmaps/flags/gf.png diff --git a/deluge/ui/data/pixmaps/flags/gg.png b/deluge/ui/data/pixmaps/flags/gg.png Binary files differindex f65fbe1d2..3057a73d6 100644 --- a/deluge/ui/data/pixmaps/flags/gg.png +++ b/deluge/ui/data/pixmaps/flags/gg.png diff --git a/deluge/ui/data/pixmaps/flags/gh.png b/deluge/ui/data/pixmaps/flags/gh.png Binary files differindex bc5741c3f..39f91b1fd 100644 --- a/deluge/ui/data/pixmaps/flags/gh.png +++ b/deluge/ui/data/pixmaps/flags/gh.png diff --git a/deluge/ui/data/pixmaps/flags/gi.png b/deluge/ui/data/pixmaps/flags/gi.png Binary files differindex 8edbe3748..3e631b3b1 100644 --- a/deluge/ui/data/pixmaps/flags/gi.png +++ b/deluge/ui/data/pixmaps/flags/gi.png diff --git a/deluge/ui/data/pixmaps/flags/gl.png b/deluge/ui/data/pixmaps/flags/gl.png Binary files differindex d8c022fde..40b443434 100644 --- a/deluge/ui/data/pixmaps/flags/gl.png +++ b/deluge/ui/data/pixmaps/flags/gl.png diff --git a/deluge/ui/data/pixmaps/flags/gn.png b/deluge/ui/data/pixmaps/flags/gn.png Binary files differindex b73833359..879495159 100644 --- a/deluge/ui/data/pixmaps/flags/gn.png +++ b/deluge/ui/data/pixmaps/flags/gn.png diff --git a/deluge/ui/data/pixmaps/flags/gp.png b/deluge/ui/data/pixmaps/flags/gp.png Binary files differindex 81a21d3d5..a8340a26f 100644 --- a/deluge/ui/data/pixmaps/flags/gp.png +++ b/deluge/ui/data/pixmaps/flags/gp.png diff --git a/deluge/ui/data/pixmaps/flags/gq.png b/deluge/ui/data/pixmaps/flags/gq.png Binary files differindex 5d22f18ad..f773395ca 100644 --- a/deluge/ui/data/pixmaps/flags/gq.png +++ b/deluge/ui/data/pixmaps/flags/gq.png diff --git a/deluge/ui/data/pixmaps/flags/gr.png b/deluge/ui/data/pixmaps/flags/gr.png Binary files differindex 9fcb34548..840b53462 100644 --- a/deluge/ui/data/pixmaps/flags/gr.png +++ b/deluge/ui/data/pixmaps/flags/gr.png diff --git a/deluge/ui/data/pixmaps/flags/gt.png b/deluge/ui/data/pixmaps/flags/gt.png Binary files differindex a05b89c3e..d9b836e26 100644 --- a/deluge/ui/data/pixmaps/flags/gt.png +++ b/deluge/ui/data/pixmaps/flags/gt.png diff --git a/deluge/ui/data/pixmaps/flags/gu.png b/deluge/ui/data/pixmaps/flags/gu.png Binary files differindex 83beb89ac..4886da3d4 100644 --- a/deluge/ui/data/pixmaps/flags/gu.png +++ b/deluge/ui/data/pixmaps/flags/gu.png diff --git a/deluge/ui/data/pixmaps/flags/gw.png b/deluge/ui/data/pixmaps/flags/gw.png Binary files differindex fe406b28a..db172bdf4 100644 --- a/deluge/ui/data/pixmaps/flags/gw.png +++ b/deluge/ui/data/pixmaps/flags/gw.png diff --git a/deluge/ui/data/pixmaps/flags/gy.png b/deluge/ui/data/pixmaps/flags/gy.png Binary files differindex fff34e303..6424d8e9c 100644 --- a/deluge/ui/data/pixmaps/flags/gy.png +++ b/deluge/ui/data/pixmaps/flags/gy.png diff --git a/deluge/ui/data/pixmaps/flags/hk.png b/deluge/ui/data/pixmaps/flags/hk.png Binary files differindex 6a5625a00..e969f862b 100644 --- a/deluge/ui/data/pixmaps/flags/hk.png +++ b/deluge/ui/data/pixmaps/flags/hk.png diff --git a/deluge/ui/data/pixmaps/flags/hm.png b/deluge/ui/data/pixmaps/flags/hm.png Binary files differindex 0de18f0b2..0335dbfc8 100644 --- a/deluge/ui/data/pixmaps/flags/hm.png +++ b/deluge/ui/data/pixmaps/flags/hm.png diff --git a/deluge/ui/data/pixmaps/flags/hn.png b/deluge/ui/data/pixmaps/flags/hn.png Binary files differindex 77a009ace..e3faca9f3 100644 --- a/deluge/ui/data/pixmaps/flags/hn.png +++ b/deluge/ui/data/pixmaps/flags/hn.png diff --git a/deluge/ui/data/pixmaps/flags/hr.png b/deluge/ui/data/pixmaps/flags/hr.png Binary files differindex f56bc2926..aa64bf889 100644 --- a/deluge/ui/data/pixmaps/flags/hr.png +++ b/deluge/ui/data/pixmaps/flags/hr.png diff --git a/deluge/ui/data/pixmaps/flags/ht.png b/deluge/ui/data/pixmaps/flags/ht.png Binary files differindex 16d00e8c1..bbcfd7ed6 100644 --- a/deluge/ui/data/pixmaps/flags/ht.png +++ b/deluge/ui/data/pixmaps/flags/ht.png diff --git a/deluge/ui/data/pixmaps/flags/id.png b/deluge/ui/data/pixmaps/flags/id.png Binary files differindex 201ad0fcb..b1cdf79b9 100644 --- a/deluge/ui/data/pixmaps/flags/id.png +++ b/deluge/ui/data/pixmaps/flags/id.png diff --git a/deluge/ui/data/pixmaps/flags/in.png b/deluge/ui/data/pixmaps/flags/in.png Binary files differindex 0b515d76a..0f5801e7f 100644 --- a/deluge/ui/data/pixmaps/flags/in.png +++ b/deluge/ui/data/pixmaps/flags/in.png diff --git a/deluge/ui/data/pixmaps/flags/iq.png b/deluge/ui/data/pixmaps/flags/iq.png Binary files differindex cb1ca164e..221fbc6df 100644 --- a/deluge/ui/data/pixmaps/flags/iq.png +++ b/deluge/ui/data/pixmaps/flags/iq.png diff --git a/deluge/ui/data/pixmaps/flags/ir.png b/deluge/ui/data/pixmaps/flags/ir.png Binary files differindex f6d1027bd..8992fa456 100644 --- a/deluge/ui/data/pixmaps/flags/ir.png +++ b/deluge/ui/data/pixmaps/flags/ir.png diff --git a/deluge/ui/data/pixmaps/flags/is.png b/deluge/ui/data/pixmaps/flags/is.png Binary files differindex f7383cbce..3f5d5c42f 100644 --- a/deluge/ui/data/pixmaps/flags/is.png +++ b/deluge/ui/data/pixmaps/flags/is.png diff --git a/deluge/ui/data/pixmaps/flags/it.png b/deluge/ui/data/pixmaps/flags/it.png Binary files differindex c3c3143c7..ad34d9f2c 100644 --- a/deluge/ui/data/pixmaps/flags/it.png +++ b/deluge/ui/data/pixmaps/flags/it.png diff --git a/deluge/ui/data/pixmaps/flags/jp.png b/deluge/ui/data/pixmaps/flags/jp.png Binary files differindex e470d756d..def1d3c7d 100644 --- a/deluge/ui/data/pixmaps/flags/jp.png +++ b/deluge/ui/data/pixmaps/flags/jp.png diff --git a/deluge/ui/data/pixmaps/flags/ke.png b/deluge/ui/data/pixmaps/flags/ke.png Binary files differindex 212cc2e0e..2adad2d51 100644 --- a/deluge/ui/data/pixmaps/flags/ke.png +++ b/deluge/ui/data/pixmaps/flags/ke.png diff --git a/deluge/ui/data/pixmaps/flags/kg.png b/deluge/ui/data/pixmaps/flags/kg.png Binary files differindex 848da86b8..8be5235df 100644 --- a/deluge/ui/data/pixmaps/flags/kg.png +++ b/deluge/ui/data/pixmaps/flags/kg.png diff --git a/deluge/ui/data/pixmaps/flags/kh.png b/deluge/ui/data/pixmaps/flags/kh.png Binary files differindex bf961c5d0..24da97236 100644 --- a/deluge/ui/data/pixmaps/flags/kh.png +++ b/deluge/ui/data/pixmaps/flags/kh.png diff --git a/deluge/ui/data/pixmaps/flags/km.png b/deluge/ui/data/pixmaps/flags/km.png Binary files differindex 7ead97e0e..625437d25 100644 --- a/deluge/ui/data/pixmaps/flags/km.png +++ b/deluge/ui/data/pixmaps/flags/km.png diff --git a/deluge/ui/data/pixmaps/flags/kp.png b/deluge/ui/data/pixmaps/flags/kp.png Binary files differindex b517d1c20..fb473ec22 100644 --- a/deluge/ui/data/pixmaps/flags/kp.png +++ b/deluge/ui/data/pixmaps/flags/kp.png diff --git a/deluge/ui/data/pixmaps/flags/kr.png b/deluge/ui/data/pixmaps/flags/kr.png Binary files differindex ea3122e47..55bcf3872 100644 --- a/deluge/ui/data/pixmaps/flags/kr.png +++ b/deluge/ui/data/pixmaps/flags/kr.png diff --git a/deluge/ui/data/pixmaps/flags/kw.png b/deluge/ui/data/pixmaps/flags/kw.png Binary files differindex a3a7fcbcf..565cc0369 100644 --- a/deluge/ui/data/pixmaps/flags/kw.png +++ b/deluge/ui/data/pixmaps/flags/kw.png diff --git a/deluge/ui/data/pixmaps/flags/ky.png b/deluge/ui/data/pixmaps/flags/ky.png Binary files differindex 63b55463f..b08b3bccb 100644 --- a/deluge/ui/data/pixmaps/flags/ky.png +++ b/deluge/ui/data/pixmaps/flags/ky.png diff --git a/deluge/ui/data/pixmaps/flags/kz.png b/deluge/ui/data/pixmaps/flags/kz.png Binary files differindex 193cf3872..49fcc2611 100644 --- a/deluge/ui/data/pixmaps/flags/kz.png +++ b/deluge/ui/data/pixmaps/flags/kz.png diff --git a/deluge/ui/data/pixmaps/flags/la.png b/deluge/ui/data/pixmaps/flags/la.png Binary files differindex 85b6097a1..0098f1b80 100644 --- a/deluge/ui/data/pixmaps/flags/la.png +++ b/deluge/ui/data/pixmaps/flags/la.png diff --git a/deluge/ui/data/pixmaps/flags/lb.png b/deluge/ui/data/pixmaps/flags/lb.png Binary files differindex b8e9c2f15..c519388ab 100644 --- a/deluge/ui/data/pixmaps/flags/lb.png +++ b/deluge/ui/data/pixmaps/flags/lb.png diff --git a/deluge/ui/data/pixmaps/flags/lc.png b/deluge/ui/data/pixmaps/flags/lc.png Binary files differindex d8a8656b5..f42f007ee 100644 --- a/deluge/ui/data/pixmaps/flags/lc.png +++ b/deluge/ui/data/pixmaps/flags/lc.png diff --git a/deluge/ui/data/pixmaps/flags/li.png b/deluge/ui/data/pixmaps/flags/li.png Binary files differindex 6bb7b2b7b..02f75488b 100644 --- a/deluge/ui/data/pixmaps/flags/li.png +++ b/deluge/ui/data/pixmaps/flags/li.png diff --git a/deluge/ui/data/pixmaps/flags/lk.png b/deluge/ui/data/pixmaps/flags/lk.png Binary files differindex 4f16cd77a..22abcdb68 100644 --- a/deluge/ui/data/pixmaps/flags/lk.png +++ b/deluge/ui/data/pixmaps/flags/lk.png diff --git a/deluge/ui/data/pixmaps/flags/lr.png b/deluge/ui/data/pixmaps/flags/lr.png Binary files differindex d76e171e9..676fda789 100644 --- a/deluge/ui/data/pixmaps/flags/lr.png +++ b/deluge/ui/data/pixmaps/flags/lr.png diff --git a/deluge/ui/data/pixmaps/flags/ls.png b/deluge/ui/data/pixmaps/flags/ls.png Binary files differindex 02cdd660f..3a2fcae5a 100644 --- a/deluge/ui/data/pixmaps/flags/ls.png +++ b/deluge/ui/data/pixmaps/flags/ls.png diff --git a/deluge/ui/data/pixmaps/flags/lt.png b/deluge/ui/data/pixmaps/flags/lt.png Binary files differindex e0714c541..05a445c00 100644 --- a/deluge/ui/data/pixmaps/flags/lt.png +++ b/deluge/ui/data/pixmaps/flags/lt.png diff --git a/deluge/ui/data/pixmaps/flags/lu.png b/deluge/ui/data/pixmaps/flags/lu.png Binary files differindex f750b0c24..c9d4d7253 100644 --- a/deluge/ui/data/pixmaps/flags/lu.png +++ b/deluge/ui/data/pixmaps/flags/lu.png diff --git a/deluge/ui/data/pixmaps/flags/lv.png b/deluge/ui/data/pixmaps/flags/lv.png Binary files differindex f35709550..72504d11d 100644 --- a/deluge/ui/data/pixmaps/flags/lv.png +++ b/deluge/ui/data/pixmaps/flags/lv.png diff --git a/deluge/ui/data/pixmaps/flags/ly.png b/deluge/ui/data/pixmaps/flags/ly.png Binary files differindex 9e35e389d..ebd3cf62c 100644 --- a/deluge/ui/data/pixmaps/flags/ly.png +++ b/deluge/ui/data/pixmaps/flags/ly.png diff --git a/deluge/ui/data/pixmaps/flags/ma.png b/deluge/ui/data/pixmaps/flags/ma.png Binary files differindex b8f8decfa..62d706570 100644 --- a/deluge/ui/data/pixmaps/flags/ma.png +++ b/deluge/ui/data/pixmaps/flags/ma.png diff --git a/deluge/ui/data/pixmaps/flags/mc.png b/deluge/ui/data/pixmaps/flags/mc.png Binary files differindex 67099ea18..3f2d6be4a 100644 --- a/deluge/ui/data/pixmaps/flags/mc.png +++ b/deluge/ui/data/pixmaps/flags/mc.png diff --git a/deluge/ui/data/pixmaps/flags/md.png b/deluge/ui/data/pixmaps/flags/md.png Binary files differindex 6ff6cf59e..c72632e1d 100644 --- a/deluge/ui/data/pixmaps/flags/md.png +++ b/deluge/ui/data/pixmaps/flags/md.png diff --git a/deluge/ui/data/pixmaps/flags/me.png b/deluge/ui/data/pixmaps/flags/me.png Binary files differindex 36cbdd582..0760fb8e8 100644 --- a/deluge/ui/data/pixmaps/flags/me.png +++ b/deluge/ui/data/pixmaps/flags/me.png diff --git a/deluge/ui/data/pixmaps/flags/mg.png b/deluge/ui/data/pixmaps/flags/mg.png Binary files differindex d9313e295..7000ff015 100644 --- a/deluge/ui/data/pixmaps/flags/mg.png +++ b/deluge/ui/data/pixmaps/flags/mg.png diff --git a/deluge/ui/data/pixmaps/flags/mh.png b/deluge/ui/data/pixmaps/flags/mh.png Binary files differindex 7618cc6fa..fe2caeff1 100644 --- a/deluge/ui/data/pixmaps/flags/mh.png +++ b/deluge/ui/data/pixmaps/flags/mh.png diff --git a/deluge/ui/data/pixmaps/flags/mk.png b/deluge/ui/data/pixmaps/flags/mk.png Binary files differindex 1c98d5154..8739fee7b 100644 --- a/deluge/ui/data/pixmaps/flags/mk.png +++ b/deluge/ui/data/pixmaps/flags/mk.png diff --git a/deluge/ui/data/pixmaps/flags/ml.png b/deluge/ui/data/pixmaps/flags/ml.png Binary files differindex d59cea847..f35c3a302 100644 --- a/deluge/ui/data/pixmaps/flags/ml.png +++ b/deluge/ui/data/pixmaps/flags/ml.png diff --git a/deluge/ui/data/pixmaps/flags/mm.png b/deluge/ui/data/pixmaps/flags/mm.png Binary files differindex 175fc5787..94bcb9362 100644 --- a/deluge/ui/data/pixmaps/flags/mm.png +++ b/deluge/ui/data/pixmaps/flags/mm.png diff --git a/deluge/ui/data/pixmaps/flags/mn.png b/deluge/ui/data/pixmaps/flags/mn.png Binary files differindex 0ad97b350..8e20c1d57 100644 --- a/deluge/ui/data/pixmaps/flags/mn.png +++ b/deluge/ui/data/pixmaps/flags/mn.png diff --git a/deluge/ui/data/pixmaps/flags/mo.png b/deluge/ui/data/pixmaps/flags/mo.png Binary files differindex f49e677e8..bbc14deef 100644 --- a/deluge/ui/data/pixmaps/flags/mo.png +++ b/deluge/ui/data/pixmaps/flags/mo.png diff --git a/deluge/ui/data/pixmaps/flags/mp.png b/deluge/ui/data/pixmaps/flags/mp.png Binary files differindex e91b75b63..ab6d5b8b7 100644 --- a/deluge/ui/data/pixmaps/flags/mp.png +++ b/deluge/ui/data/pixmaps/flags/mp.png diff --git a/deluge/ui/data/pixmaps/flags/mq.png b/deluge/ui/data/pixmaps/flags/mq.png Binary files differindex 5c5ae824b..9f52d1556 100644 --- a/deluge/ui/data/pixmaps/flags/mq.png +++ b/deluge/ui/data/pixmaps/flags/mq.png diff --git a/deluge/ui/data/pixmaps/flags/mr.png b/deluge/ui/data/pixmaps/flags/mr.png Binary files differindex 91d9a5a9d..e335ec22c 100644 --- a/deluge/ui/data/pixmaps/flags/mr.png +++ b/deluge/ui/data/pixmaps/flags/mr.png diff --git a/deluge/ui/data/pixmaps/flags/ms.png b/deluge/ui/data/pixmaps/flags/ms.png Binary files differindex d45c16c39..addd14417 100644 --- a/deluge/ui/data/pixmaps/flags/ms.png +++ b/deluge/ui/data/pixmaps/flags/ms.png diff --git a/deluge/ui/data/pixmaps/flags/mu.png b/deluge/ui/data/pixmaps/flags/mu.png Binary files differindex ecdc1869a..38b6687a1 100644 --- a/deluge/ui/data/pixmaps/flags/mu.png +++ b/deluge/ui/data/pixmaps/flags/mu.png diff --git a/deluge/ui/data/pixmaps/flags/mv.png b/deluge/ui/data/pixmaps/flags/mv.png Binary files differindex b17294451..1185f135c 100644 --- a/deluge/ui/data/pixmaps/flags/mv.png +++ b/deluge/ui/data/pixmaps/flags/mv.png diff --git a/deluge/ui/data/pixmaps/flags/mw.png b/deluge/ui/data/pixmaps/flags/mw.png Binary files differindex cb9b61f49..d68ffeefb 100644 --- a/deluge/ui/data/pixmaps/flags/mw.png +++ b/deluge/ui/data/pixmaps/flags/mw.png diff --git a/deluge/ui/data/pixmaps/flags/mx.png b/deluge/ui/data/pixmaps/flags/mx.png Binary files differindex b8e70b868..996bcaef3 100644 --- a/deluge/ui/data/pixmaps/flags/mx.png +++ b/deluge/ui/data/pixmaps/flags/mx.png diff --git a/deluge/ui/data/pixmaps/flags/my.png b/deluge/ui/data/pixmaps/flags/my.png Binary files differindex 879cf519c..2b13696be 100644 --- a/deluge/ui/data/pixmaps/flags/my.png +++ b/deluge/ui/data/pixmaps/flags/my.png diff --git a/deluge/ui/data/pixmaps/flags/mz.png b/deluge/ui/data/pixmaps/flags/mz.png Binary files differindex 35545922b..484e99892 100644 --- a/deluge/ui/data/pixmaps/flags/mz.png +++ b/deluge/ui/data/pixmaps/flags/mz.png diff --git a/deluge/ui/data/pixmaps/flags/na.png b/deluge/ui/data/pixmaps/flags/na.png Binary files differindex 426cde9a5..806e2d624 100644 --- a/deluge/ui/data/pixmaps/flags/na.png +++ b/deluge/ui/data/pixmaps/flags/na.png diff --git a/deluge/ui/data/pixmaps/flags/nc.png b/deluge/ui/data/pixmaps/flags/nc.png Binary files differindex 88c35ac13..de2314a82 100644 --- a/deluge/ui/data/pixmaps/flags/nc.png +++ b/deluge/ui/data/pixmaps/flags/nc.png diff --git a/deluge/ui/data/pixmaps/flags/ne.png b/deluge/ui/data/pixmaps/flags/ne.png Binary files differindex eff1c3530..f76deb1cc 100644 --- a/deluge/ui/data/pixmaps/flags/ne.png +++ b/deluge/ui/data/pixmaps/flags/ne.png diff --git a/deluge/ui/data/pixmaps/flags/ng.png b/deluge/ui/data/pixmaps/flags/ng.png Binary files differindex e2f2b61d1..4e66ac3c0 100644 --- a/deluge/ui/data/pixmaps/flags/ng.png +++ b/deluge/ui/data/pixmaps/flags/ng.png diff --git a/deluge/ui/data/pixmaps/flags/ni.png b/deluge/ui/data/pixmaps/flags/ni.png Binary files differindex 9332b95c2..6d0cd865d 100644 --- a/deluge/ui/data/pixmaps/flags/ni.png +++ b/deluge/ui/data/pixmaps/flags/ni.png diff --git a/deluge/ui/data/pixmaps/flags/nl.png b/deluge/ui/data/pixmaps/flags/nl.png Binary files differindex 267d1dceb..b0653cc8a 100644 --- a/deluge/ui/data/pixmaps/flags/nl.png +++ b/deluge/ui/data/pixmaps/flags/nl.png diff --git a/deluge/ui/data/pixmaps/flags/no.png b/deluge/ui/data/pixmaps/flags/no.png Binary files differindex ae9060caf..faaa529d1 100644 --- a/deluge/ui/data/pixmaps/flags/no.png +++ b/deluge/ui/data/pixmaps/flags/no.png diff --git a/deluge/ui/data/pixmaps/flags/np.png b/deluge/ui/data/pixmaps/flags/np.png Binary files differindex 6637ea544..9e161085b 100644 --- a/deluge/ui/data/pixmaps/flags/np.png +++ b/deluge/ui/data/pixmaps/flags/np.png diff --git a/deluge/ui/data/pixmaps/flags/nr.png b/deluge/ui/data/pixmaps/flags/nr.png Binary files differindex 60f73461e..c28d3eec7 100644 --- a/deluge/ui/data/pixmaps/flags/nr.png +++ b/deluge/ui/data/pixmaps/flags/nr.png diff --git a/deluge/ui/data/pixmaps/flags/nu.png b/deluge/ui/data/pixmaps/flags/nu.png Binary files differindex 21e52a76e..9e82b5e12 100644 --- a/deluge/ui/data/pixmaps/flags/nu.png +++ b/deluge/ui/data/pixmaps/flags/nu.png diff --git a/deluge/ui/data/pixmaps/flags/nz.png b/deluge/ui/data/pixmaps/flags/nz.png Binary files differindex d5a7ebc7b..db738747b 100644 --- a/deluge/ui/data/pixmaps/flags/nz.png +++ b/deluge/ui/data/pixmaps/flags/nz.png diff --git a/deluge/ui/data/pixmaps/flags/om.png b/deluge/ui/data/pixmaps/flags/om.png Binary files differindex 6dc30d4fc..aa02242c9 100644 --- a/deluge/ui/data/pixmaps/flags/om.png +++ b/deluge/ui/data/pixmaps/flags/om.png diff --git a/deluge/ui/data/pixmaps/flags/pa.png b/deluge/ui/data/pixmaps/flags/pa.png Binary files differindex 895443640..810268af3 100644 --- a/deluge/ui/data/pixmaps/flags/pa.png +++ b/deluge/ui/data/pixmaps/flags/pa.png diff --git a/deluge/ui/data/pixmaps/flags/pf.png b/deluge/ui/data/pixmaps/flags/pf.png Binary files differindex ea4d04611..9a05d6674 100644 --- a/deluge/ui/data/pixmaps/flags/pf.png +++ b/deluge/ui/data/pixmaps/flags/pf.png diff --git a/deluge/ui/data/pixmaps/flags/pg.png b/deluge/ui/data/pixmaps/flags/pg.png Binary files differindex c97ce6a76..e4ec2d175 100644 --- a/deluge/ui/data/pixmaps/flags/pg.png +++ b/deluge/ui/data/pixmaps/flags/pg.png diff --git a/deluge/ui/data/pixmaps/flags/ph.png b/deluge/ui/data/pixmaps/flags/ph.png Binary files differindex 67d289173..423209405 100644 --- a/deluge/ui/data/pixmaps/flags/ph.png +++ b/deluge/ui/data/pixmaps/flags/ph.png diff --git a/deluge/ui/data/pixmaps/flags/pk.png b/deluge/ui/data/pixmaps/flags/pk.png Binary files differindex a92d71c08..bb4822ffc 100644 --- a/deluge/ui/data/pixmaps/flags/pk.png +++ b/deluge/ui/data/pixmaps/flags/pk.png diff --git a/deluge/ui/data/pixmaps/flags/pl.png b/deluge/ui/data/pixmaps/flags/pl.png Binary files differindex 2c4f2ed1f..843568a6a 100644 --- a/deluge/ui/data/pixmaps/flags/pl.png +++ b/deluge/ui/data/pixmaps/flags/pl.png diff --git a/deluge/ui/data/pixmaps/flags/pm.png b/deluge/ui/data/pixmaps/flags/pm.png Binary files differindex ce429f0d2..f167375f0 100644 --- a/deluge/ui/data/pixmaps/flags/pm.png +++ b/deluge/ui/data/pixmaps/flags/pm.png diff --git a/deluge/ui/data/pixmaps/flags/pr.png b/deluge/ui/data/pixmaps/flags/pr.png Binary files differindex 60d359917..0dc6d8d37 100644 --- a/deluge/ui/data/pixmaps/flags/pr.png +++ b/deluge/ui/data/pixmaps/flags/pr.png diff --git a/deluge/ui/data/pixmaps/flags/ps.png b/deluge/ui/data/pixmaps/flags/ps.png Binary files differindex ceda7d7aa..3955b5cd7 100644 --- a/deluge/ui/data/pixmaps/flags/ps.png +++ b/deluge/ui/data/pixmaps/flags/ps.png diff --git a/deluge/ui/data/pixmaps/flags/pt.png b/deluge/ui/data/pixmaps/flags/pt.png Binary files differindex c489acda3..ee95fc82f 100644 --- a/deluge/ui/data/pixmaps/flags/pt.png +++ b/deluge/ui/data/pixmaps/flags/pt.png diff --git a/deluge/ui/data/pixmaps/flags/pw.png b/deluge/ui/data/pixmaps/flags/pw.png Binary files differindex 668c93a03..43d9dcb85 100644 --- a/deluge/ui/data/pixmaps/flags/pw.png +++ b/deluge/ui/data/pixmaps/flags/pw.png diff --git a/deluge/ui/data/pixmaps/flags/py.png b/deluge/ui/data/pixmaps/flags/py.png Binary files differindex d5e943b0c..bc5d77db3 100644 --- a/deluge/ui/data/pixmaps/flags/py.png +++ b/deluge/ui/data/pixmaps/flags/py.png diff --git a/deluge/ui/data/pixmaps/flags/qa.png b/deluge/ui/data/pixmaps/flags/qa.png Binary files differindex 08a3793e7..09709cc63 100644 --- a/deluge/ui/data/pixmaps/flags/qa.png +++ b/deluge/ui/data/pixmaps/flags/qa.png diff --git a/deluge/ui/data/pixmaps/flags/re.png b/deluge/ui/data/pixmaps/flags/re.png Binary files differindex 59346a911..84dd91764 100644 --- a/deluge/ui/data/pixmaps/flags/re.png +++ b/deluge/ui/data/pixmaps/flags/re.png diff --git a/deluge/ui/data/pixmaps/flags/ro.png b/deluge/ui/data/pixmaps/flags/ro.png Binary files differindex f80a44a7f..594f245f5 100644 --- a/deluge/ui/data/pixmaps/flags/ro.png +++ b/deluge/ui/data/pixmaps/flags/ro.png diff --git a/deluge/ui/data/pixmaps/flags/rs.png b/deluge/ui/data/pixmaps/flags/rs.png Binary files differindex 9a35bcc55..03176f73d 100644 --- a/deluge/ui/data/pixmaps/flags/rs.png +++ b/deluge/ui/data/pixmaps/flags/rs.png diff --git a/deluge/ui/data/pixmaps/flags/rw.png b/deluge/ui/data/pixmaps/flags/rw.png Binary files differindex 0d3143791..c7e422a7b 100644 --- a/deluge/ui/data/pixmaps/flags/rw.png +++ b/deluge/ui/data/pixmaps/flags/rw.png diff --git a/deluge/ui/data/pixmaps/flags/sa.png b/deluge/ui/data/pixmaps/flags/sa.png Binary files differindex 2fc170454..bd19de219 100644 --- a/deluge/ui/data/pixmaps/flags/sa.png +++ b/deluge/ui/data/pixmaps/flags/sa.png diff --git a/deluge/ui/data/pixmaps/flags/sc.png b/deluge/ui/data/pixmaps/flags/sc.png Binary files differindex c25e11369..e0bbfa72d 100644 --- a/deluge/ui/data/pixmaps/flags/sc.png +++ b/deluge/ui/data/pixmaps/flags/sc.png diff --git a/deluge/ui/data/pixmaps/flags/sd.png b/deluge/ui/data/pixmaps/flags/sd.png Binary files differindex d22408c30..cbca3d2b5 100644 --- a/deluge/ui/data/pixmaps/flags/sd.png +++ b/deluge/ui/data/pixmaps/flags/sd.png diff --git a/deluge/ui/data/pixmaps/flags/se.png b/deluge/ui/data/pixmaps/flags/se.png Binary files differindex 482101a66..e3d273a6f 100644 --- a/deluge/ui/data/pixmaps/flags/se.png +++ b/deluge/ui/data/pixmaps/flags/se.png diff --git a/deluge/ui/data/pixmaps/flags/sg.png b/deluge/ui/data/pixmaps/flags/sg.png Binary files differindex 055e04a1e..898748b76 100644 --- a/deluge/ui/data/pixmaps/flags/sg.png +++ b/deluge/ui/data/pixmaps/flags/sg.png diff --git a/deluge/ui/data/pixmaps/flags/sh.png b/deluge/ui/data/pixmaps/flags/sh.png Binary files differindex 838dad78e..cb5282e2d 100644 --- a/deluge/ui/data/pixmaps/flags/sh.png +++ b/deluge/ui/data/pixmaps/flags/sh.png diff --git a/deluge/ui/data/pixmaps/flags/si.png b/deluge/ui/data/pixmaps/flags/si.png Binary files differindex 206721e5f..d2f976b3a 100644 --- a/deluge/ui/data/pixmaps/flags/si.png +++ b/deluge/ui/data/pixmaps/flags/si.png diff --git a/deluge/ui/data/pixmaps/flags/sj.png b/deluge/ui/data/pixmaps/flags/sj.png Binary files differindex ae9060caf..faaa529d1 100644 --- a/deluge/ui/data/pixmaps/flags/sj.png +++ b/deluge/ui/data/pixmaps/flags/sj.png diff --git a/deluge/ui/data/pixmaps/flags/sk.png b/deluge/ui/data/pixmaps/flags/sk.png Binary files differindex acdd1893d..6f36d1429 100644 --- a/deluge/ui/data/pixmaps/flags/sk.png +++ b/deluge/ui/data/pixmaps/flags/sk.png diff --git a/deluge/ui/data/pixmaps/flags/sl.png b/deluge/ui/data/pixmaps/flags/sl.png Binary files differindex d50e5d4ef..3d70d224c 100644 --- a/deluge/ui/data/pixmaps/flags/sl.png +++ b/deluge/ui/data/pixmaps/flags/sl.png diff --git a/deluge/ui/data/pixmaps/flags/sn.png b/deluge/ui/data/pixmaps/flags/sn.png Binary files differindex 4029d3a7d..3be2d4c56 100644 --- a/deluge/ui/data/pixmaps/flags/sn.png +++ b/deluge/ui/data/pixmaps/flags/sn.png diff --git a/deluge/ui/data/pixmaps/flags/so.png b/deluge/ui/data/pixmaps/flags/so.png Binary files differindex b17fcf27f..c3169fa64 100644 --- a/deluge/ui/data/pixmaps/flags/so.png +++ b/deluge/ui/data/pixmaps/flags/so.png diff --git a/deluge/ui/data/pixmaps/flags/sr.png b/deluge/ui/data/pixmaps/flags/sr.png Binary files differindex e79029176..006f86f05 100644 --- a/deluge/ui/data/pixmaps/flags/sr.png +++ b/deluge/ui/data/pixmaps/flags/sr.png diff --git a/deluge/ui/data/pixmaps/flags/st.png b/deluge/ui/data/pixmaps/flags/st.png Binary files differindex 63c3e12ff..086db357a 100644 --- a/deluge/ui/data/pixmaps/flags/st.png +++ b/deluge/ui/data/pixmaps/flags/st.png diff --git a/deluge/ui/data/pixmaps/flags/sv.png b/deluge/ui/data/pixmaps/flags/sv.png Binary files differindex 309eb39d3..691fc669b 100644 --- a/deluge/ui/data/pixmaps/flags/sv.png +++ b/deluge/ui/data/pixmaps/flags/sv.png diff --git a/deluge/ui/data/pixmaps/flags/sy.png b/deluge/ui/data/pixmaps/flags/sy.png Binary files differindex 8a369aabf..5a28ce67b 100644 --- a/deluge/ui/data/pixmaps/flags/sy.png +++ b/deluge/ui/data/pixmaps/flags/sy.png diff --git a/deluge/ui/data/pixmaps/flags/sz.png b/deluge/ui/data/pixmaps/flags/sz.png Binary files differindex 2da75092b..fba6beffc 100644 --- a/deluge/ui/data/pixmaps/flags/sz.png +++ b/deluge/ui/data/pixmaps/flags/sz.png diff --git a/deluge/ui/data/pixmaps/flags/tc.png b/deluge/ui/data/pixmaps/flags/tc.png Binary files differindex c7b735443..dfecacd03 100644 --- a/deluge/ui/data/pixmaps/flags/tc.png +++ b/deluge/ui/data/pixmaps/flags/tc.png diff --git a/deluge/ui/data/pixmaps/flags/td.png b/deluge/ui/data/pixmaps/flags/td.png Binary files differindex c82f450f0..27b6cb805 100644 --- a/deluge/ui/data/pixmaps/flags/td.png +++ b/deluge/ui/data/pixmaps/flags/td.png diff --git a/deluge/ui/data/pixmaps/flags/tf.png b/deluge/ui/data/pixmaps/flags/tf.png Binary files differindex bf8077348..0f56fa04b 100644 --- a/deluge/ui/data/pixmaps/flags/tf.png +++ b/deluge/ui/data/pixmaps/flags/tf.png diff --git a/deluge/ui/data/pixmaps/flags/th.png b/deluge/ui/data/pixmaps/flags/th.png Binary files differindex bf739941e..744865f57 100644 --- a/deluge/ui/data/pixmaps/flags/th.png +++ b/deluge/ui/data/pixmaps/flags/th.png diff --git a/deluge/ui/data/pixmaps/flags/tj.png b/deluge/ui/data/pixmaps/flags/tj.png Binary files differindex 3efd165cc..837dd2caa 100644 --- a/deluge/ui/data/pixmaps/flags/tj.png +++ b/deluge/ui/data/pixmaps/flags/tj.png diff --git a/deluge/ui/data/pixmaps/flags/tl.png b/deluge/ui/data/pixmaps/flags/tl.png Binary files differindex d7c41a9c7..843ec671d 100644 --- a/deluge/ui/data/pixmaps/flags/tl.png +++ b/deluge/ui/data/pixmaps/flags/tl.png diff --git a/deluge/ui/data/pixmaps/flags/tm.png b/deluge/ui/data/pixmaps/flags/tm.png Binary files differindex a2aed22dd..6356f3c5d 100644 --- a/deluge/ui/data/pixmaps/flags/tm.png +++ b/deluge/ui/data/pixmaps/flags/tm.png diff --git a/deluge/ui/data/pixmaps/flags/tn.png b/deluge/ui/data/pixmaps/flags/tn.png Binary files differindex 61086fa77..fdf73c09d 100644 --- a/deluge/ui/data/pixmaps/flags/tn.png +++ b/deluge/ui/data/pixmaps/flags/tn.png diff --git a/deluge/ui/data/pixmaps/flags/to.png b/deluge/ui/data/pixmaps/flags/to.png Binary files differindex 8e49d30e3..7bd4389a2 100644 --- a/deluge/ui/data/pixmaps/flags/to.png +++ b/deluge/ui/data/pixmaps/flags/to.png diff --git a/deluge/ui/data/pixmaps/flags/tp.png b/deluge/ui/data/pixmaps/flags/tp.png Binary files differindex aa298c0f8..a8bb82fe4 100644 --- a/deluge/ui/data/pixmaps/flags/tp.png +++ b/deluge/ui/data/pixmaps/flags/tp.png diff --git a/deluge/ui/data/pixmaps/flags/tr.png b/deluge/ui/data/pixmaps/flags/tr.png Binary files differindex 09f82d09e..34b5a1c4b 100644 --- a/deluge/ui/data/pixmaps/flags/tr.png +++ b/deluge/ui/data/pixmaps/flags/tr.png diff --git a/deluge/ui/data/pixmaps/flags/tt.png b/deluge/ui/data/pixmaps/flags/tt.png Binary files differindex 18b09e1d3..7ff729b30 100644 --- a/deluge/ui/data/pixmaps/flags/tt.png +++ b/deluge/ui/data/pixmaps/flags/tt.png diff --git a/deluge/ui/data/pixmaps/flags/tz.png b/deluge/ui/data/pixmaps/flags/tz.png Binary files differindex 3543543ce..38fbdf18f 100644 --- a/deluge/ui/data/pixmaps/flags/tz.png +++ b/deluge/ui/data/pixmaps/flags/tz.png diff --git a/deluge/ui/data/pixmaps/flags/ua.png b/deluge/ui/data/pixmaps/flags/ua.png Binary files differindex e09f110dd..08e38f95e 100644 --- a/deluge/ui/data/pixmaps/flags/ua.png +++ b/deluge/ui/data/pixmaps/flags/ua.png diff --git a/deluge/ui/data/pixmaps/flags/ug.png b/deluge/ui/data/pixmaps/flags/ug.png Binary files differindex d1d9bc0f8..2fa3e44ce 100644 --- a/deluge/ui/data/pixmaps/flags/ug.png +++ b/deluge/ui/data/pixmaps/flags/ug.png diff --git a/deluge/ui/data/pixmaps/flags/um.png b/deluge/ui/data/pixmaps/flags/um.png Binary files differindex 2694ab98e..a0617bd81 100644 --- a/deluge/ui/data/pixmaps/flags/um.png +++ b/deluge/ui/data/pixmaps/flags/um.png diff --git a/deluge/ui/data/pixmaps/flags/us.png b/deluge/ui/data/pixmaps/flags/us.png Binary files differindex 68707f40c..5e0b8cf5c 100644 --- a/deluge/ui/data/pixmaps/flags/us.png +++ b/deluge/ui/data/pixmaps/flags/us.png diff --git a/deluge/ui/data/pixmaps/flags/uy.png b/deluge/ui/data/pixmaps/flags/uy.png Binary files differindex 4c515670f..e69713a6e 100644 --- a/deluge/ui/data/pixmaps/flags/uy.png +++ b/deluge/ui/data/pixmaps/flags/uy.png diff --git a/deluge/ui/data/pixmaps/flags/uz.png b/deluge/ui/data/pixmaps/flags/uz.png Binary files differindex e85d67d4b..5d3d7c16d 100644 --- a/deluge/ui/data/pixmaps/flags/uz.png +++ b/deluge/ui/data/pixmaps/flags/uz.png diff --git a/deluge/ui/data/pixmaps/flags/va.png b/deluge/ui/data/pixmaps/flags/va.png Binary files differindex 582e2ba16..af8f6b23e 100644 --- a/deluge/ui/data/pixmaps/flags/va.png +++ b/deluge/ui/data/pixmaps/flags/va.png diff --git a/deluge/ui/data/pixmaps/flags/vc.png b/deluge/ui/data/pixmaps/flags/vc.png Binary files differindex e5726112b..cf1d6e204 100644 --- a/deluge/ui/data/pixmaps/flags/vc.png +++ b/deluge/ui/data/pixmaps/flags/vc.png diff --git a/deluge/ui/data/pixmaps/flags/ve.png b/deluge/ui/data/pixmaps/flags/ve.png Binary files differindex 0899d54ae..e26b28a08 100644 --- a/deluge/ui/data/pixmaps/flags/ve.png +++ b/deluge/ui/data/pixmaps/flags/ve.png diff --git a/deluge/ui/data/pixmaps/flags/vg.png b/deluge/ui/data/pixmaps/flags/vg.png Binary files differindex d9f04d6f7..3ece564ae 100644 --- a/deluge/ui/data/pixmaps/flags/vg.png +++ b/deluge/ui/data/pixmaps/flags/vg.png diff --git a/deluge/ui/data/pixmaps/flags/vi.png b/deluge/ui/data/pixmaps/flags/vi.png Binary files differindex 2b887b30f..54d5c0e90 100644 --- a/deluge/ui/data/pixmaps/flags/vi.png +++ b/deluge/ui/data/pixmaps/flags/vi.png diff --git a/deluge/ui/data/pixmaps/flags/vn.png b/deluge/ui/data/pixmaps/flags/vn.png Binary files differindex 335e9fc8e..8a6970349 100644 --- a/deluge/ui/data/pixmaps/flags/vn.png +++ b/deluge/ui/data/pixmaps/flags/vn.png diff --git a/deluge/ui/data/pixmaps/flags/vu.png b/deluge/ui/data/pixmaps/flags/vu.png Binary files differindex f3fa6e62b..2794923a6 100644 --- a/deluge/ui/data/pixmaps/flags/vu.png +++ b/deluge/ui/data/pixmaps/flags/vu.png diff --git a/deluge/ui/data/pixmaps/flags/wf.png b/deluge/ui/data/pixmaps/flags/wf.png Binary files differindex 1d6460edb..574d08ac5 100644 --- a/deluge/ui/data/pixmaps/flags/wf.png +++ b/deluge/ui/data/pixmaps/flags/wf.png diff --git a/deluge/ui/data/pixmaps/flags/ws.png b/deluge/ui/data/pixmaps/flags/ws.png Binary files differindex 628f38793..6339a173e 100644 --- a/deluge/ui/data/pixmaps/flags/ws.png +++ b/deluge/ui/data/pixmaps/flags/ws.png diff --git a/deluge/ui/data/pixmaps/flags/ye.png b/deluge/ui/data/pixmaps/flags/ye.png Binary files differindex b3f760b7b..c9187dd99 100644 --- a/deluge/ui/data/pixmaps/flags/ye.png +++ b/deluge/ui/data/pixmaps/flags/ye.png diff --git a/deluge/ui/data/pixmaps/flags/yt.png b/deluge/ui/data/pixmaps/flags/yt.png Binary files differindex d0c5a2c88..41ef4a7f3 100644 --- a/deluge/ui/data/pixmaps/flags/yt.png +++ b/deluge/ui/data/pixmaps/flags/yt.png diff --git a/deluge/ui/data/pixmaps/flags/yu.png b/deluge/ui/data/pixmaps/flags/yu.png Binary files differindex 4827aec2d..18fbde63f 100644 --- a/deluge/ui/data/pixmaps/flags/yu.png +++ b/deluge/ui/data/pixmaps/flags/yu.png diff --git a/deluge/ui/data/pixmaps/flags/za.png b/deluge/ui/data/pixmaps/flags/za.png Binary files differindex 53ba7e1dd..04146b599 100644 --- a/deluge/ui/data/pixmaps/flags/za.png +++ b/deluge/ui/data/pixmaps/flags/za.png diff --git a/deluge/ui/data/pixmaps/flags/zm.png b/deluge/ui/data/pixmaps/flags/zm.png Binary files differindex 2911ca7ce..363b4dfbc 100644 --- a/deluge/ui/data/pixmaps/flags/zm.png +++ b/deluge/ui/data/pixmaps/flags/zm.png diff --git a/deluge/ui/data/pixmaps/flags/zw.png b/deluge/ui/data/pixmaps/flags/zw.png Binary files differindex 9c065013a..030b1474b 100644 --- a/deluge/ui/data/pixmaps/flags/zw.png +++ b/deluge/ui/data/pixmaps/flags/zw.png diff --git a/deluge/ui/data/pixmaps/inactive16.png b/deluge/ui/data/pixmaps/inactive16.png Binary files differindex cae8b2c0a..e434dad9f 100644 --- a/deluge/ui/data/pixmaps/inactive16.png +++ b/deluge/ui/data/pixmaps/inactive16.png diff --git a/deluge/ui/data/pixmaps/magnet16.png b/deluge/ui/data/pixmaps/magnet16.png Binary files differindex 61d6dabb7..6fc25ed69 100644 --- a/deluge/ui/data/pixmaps/magnet16.png +++ b/deluge/ui/data/pixmaps/magnet16.png diff --git a/deluge/ui/data/pixmaps/magnet_add16.png b/deluge/ui/data/pixmaps/magnet_add16.png Binary files differindex 37c1c36b0..f6010f54a 100644 --- a/deluge/ui/data/pixmaps/magnet_add16.png +++ b/deluge/ui/data/pixmaps/magnet_add16.png diff --git a/deluge/ui/data/pixmaps/magnet_copy16.png b/deluge/ui/data/pixmaps/magnet_copy16.png Binary files differindex a4be9d218..9b4ec6bbf 100644 --- a/deluge/ui/data/pixmaps/magnet_copy16.png +++ b/deluge/ui/data/pixmaps/magnet_copy16.png diff --git a/deluge/ui/data/pixmaps/queued16.png b/deluge/ui/data/pixmaps/queued16.png Binary files differindex f9f74540f..74db4c5eb 100644 --- a/deluge/ui/data/pixmaps/queued16.png +++ b/deluge/ui/data/pixmaps/queued16.png diff --git a/deluge/ui/data/pixmaps/seeding16.png b/deluge/ui/data/pixmaps/seeding16.png Binary files differindex fce70a8d9..2d9dc58f7 100644 --- a/deluge/ui/data/pixmaps/seeding16.png +++ b/deluge/ui/data/pixmaps/seeding16.png diff --git a/deluge/ui/data/pixmaps/tracker_all16.png b/deluge/ui/data/pixmaps/tracker_all16.png Binary files differindex 36756bbcc..10a8f9832 100644 --- a/deluge/ui/data/pixmaps/tracker_all16.png +++ b/deluge/ui/data/pixmaps/tracker_all16.png diff --git a/deluge/ui/data/pixmaps/tracker_warning16.png b/deluge/ui/data/pixmaps/tracker_warning16.png Binary files differindex 219432c44..7c29972a3 100644 --- a/deluge/ui/data/pixmaps/tracker_warning16.png +++ b/deluge/ui/data/pixmaps/tracker_warning16.png diff --git a/deluge/ui/data/pixmaps/traffic16.png b/deluge/ui/data/pixmaps/traffic16.png Binary files differindex ecd87204d..b4ce5ea14 100644 --- a/deluge/ui/data/pixmaps/traffic16.png +++ b/deluge/ui/data/pixmaps/traffic16.png 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"><Deluge-MainWindow>/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"><Deluge-MainWindow>/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"><MainWindow>/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"><Deluge-MainWindow>/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"><Deluge-MainWindow>/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 Binary files differindex e39cd0c7e..761f0601c 100644 --- a/deluge/ui/web/docs/template/resources/deluge.png +++ b/deluge/ui/web/docs/template/resources/deluge.png diff --git a/deluge/ui/web/docs/template/resources/form.png b/deluge/ui/web/docs/template/resources/form.png Binary files differindex f0945cf7f..ef32f38e7 100644 --- a/deluge/ui/web/docs/template/resources/form.png +++ b/deluge/ui/web/docs/template/resources/form.png 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 diff --git a/deluge/ui/web/docs/template/resources/images/default/shadow.png b/deluge/ui/web/docs/template/resources/images/default/shadow.png Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 diff --git a/deluge/ui/web/icons/active.png b/deluge/ui/web/icons/active.png Binary files differindex c9af82a5f..e5bab66cd 100644 --- a/deluge/ui/web/icons/active.png +++ b/deluge/ui/web/icons/active.png diff --git a/deluge/ui/web/icons/add.png b/deluge/ui/web/icons/add.png Binary files differindex 3bc06ca36..4b2c04743 100644 --- a/deluge/ui/web/icons/add.png +++ b/deluge/ui/web/icons/add.png diff --git a/deluge/ui/web/icons/add_file.png b/deluge/ui/web/icons/add_file.png Binary files differindex 6a057450f..80b41c813 100644 --- a/deluge/ui/web/icons/add_file.png +++ b/deluge/ui/web/icons/add_file.png diff --git a/deluge/ui/web/icons/add_url.png b/deluge/ui/web/icons/add_url.png Binary files differindex 74dc99f4d..3453f501a 100644 --- a/deluge/ui/web/icons/add_url.png +++ b/deluge/ui/web/icons/add_url.png diff --git a/deluge/ui/web/icons/alert.png b/deluge/ui/web/icons/alert.png Binary files differindex 703663813..49028db9c 100644 --- a/deluge/ui/web/icons/alert.png +++ b/deluge/ui/web/icons/alert.png diff --git a/deluge/ui/web/icons/all.png b/deluge/ui/web/icons/all.png Binary files differindex c63f8df1a..1a9ba9092 100644 --- a/deluge/ui/web/icons/all.png +++ b/deluge/ui/web/icons/all.png diff --git a/deluge/ui/web/icons/back.png b/deluge/ui/web/icons/back.png Binary files differindex fdcf9f060..cd955f590 100644 --- a/deluge/ui/web/icons/back.png +++ b/deluge/ui/web/icons/back.png diff --git a/deluge/ui/web/icons/bottom.png b/deluge/ui/web/icons/bottom.png Binary files differindex ca32c1f10..a57c743ba 100644 --- a/deluge/ui/web/icons/bottom.png +++ b/deluge/ui/web/icons/bottom.png diff --git a/deluge/ui/web/icons/checking.png b/deluge/ui/web/icons/checking.png Binary files differindex 6758e36e8..9961675c4 100644 --- a/deluge/ui/web/icons/checking.png +++ b/deluge/ui/web/icons/checking.png diff --git a/deluge/ui/web/icons/connection_manager.png b/deluge/ui/web/icons/connection_manager.png Binary files differindex a0333068f..f2f72c99a 100644 --- a/deluge/ui/web/icons/connection_manager.png +++ b/deluge/ui/web/icons/connection_manager.png diff --git a/deluge/ui/web/icons/connections.png b/deluge/ui/web/icons/connections.png Binary files differindex f0b660bd5..859df38d5 100644 --- a/deluge/ui/web/icons/connections.png +++ b/deluge/ui/web/icons/connections.png diff --git a/deluge/ui/web/icons/create.png b/deluge/ui/web/icons/create.png Binary files differindex 9008c98ac..375078c2a 100644 --- a/deluge/ui/web/icons/create.png +++ b/deluge/ui/web/icons/create.png diff --git a/deluge/ui/web/icons/deluge-192.png b/deluge/ui/web/icons/deluge-192.png Binary files differindex b49a31863..93c6feb44 100644 --- a/deluge/ui/web/icons/deluge-192.png +++ b/deluge/ui/web/icons/deluge-192.png diff --git a/deluge/ui/web/icons/deluge-32.png b/deluge/ui/web/icons/deluge-32.png Binary files differindex 6787fa39a..916f13632 100644 --- a/deluge/ui/web/icons/deluge-32.png +++ b/deluge/ui/web/icons/deluge-32.png diff --git a/deluge/ui/web/icons/deluge-512.png b/deluge/ui/web/icons/deluge-512.png Binary files differindex 866f6b939..464dd69f5 100644 --- a/deluge/ui/web/icons/deluge-512.png +++ b/deluge/ui/web/icons/deluge-512.png diff --git a/deluge/ui/web/icons/deluge-apple-180.png b/deluge/ui/web/icons/deluge-apple-180.png Binary files differindex 82f7be23f..527501fac 100644 --- a/deluge/ui/web/icons/deluge-apple-180.png +++ b/deluge/ui/web/icons/deluge-apple-180.png diff --git a/deluge/ui/web/icons/deluge.png b/deluge/ui/web/icons/deluge.png Binary files differindex 5afdbe4c6..1291dfaeb 100644 --- a/deluge/ui/web/icons/deluge.png +++ b/deluge/ui/web/icons/deluge.png diff --git a/deluge/ui/web/icons/dht.png b/deluge/ui/web/icons/dht.png Binary files differindex 363ee0c0f..2396bb132 100644 --- a/deluge/ui/web/icons/dht.png +++ b/deluge/ui/web/icons/dht.png diff --git a/deluge/ui/web/icons/document.png b/deluge/ui/web/icons/document.png Binary files differindex 5ee00182f..12b8b3e3a 100644 --- a/deluge/ui/web/icons/document.png +++ b/deluge/ui/web/icons/document.png diff --git a/deluge/ui/web/icons/down.png b/deluge/ui/web/icons/down.png Binary files differindex 68be2b311..3e81fbee8 100644 --- a/deluge/ui/web/icons/down.png +++ b/deluge/ui/web/icons/down.png diff --git a/deluge/ui/web/icons/downloading.png b/deluge/ui/web/icons/downloading.png Binary files differindex 24d6ffa38..e64aa5adc 100644 --- a/deluge/ui/web/icons/downloading.png +++ b/deluge/ui/web/icons/downloading.png diff --git a/deluge/ui/web/icons/drive.png b/deluge/ui/web/icons/drive.png Binary files differindex 6fd688eee..a358fa34e 100644 --- a/deluge/ui/web/icons/drive.png +++ b/deluge/ui/web/icons/drive.png diff --git a/deluge/ui/web/icons/edit_trackers.png b/deluge/ui/web/icons/edit_trackers.png Binary files differindex 80455ecc0..17f0d944d 100644 --- a/deluge/ui/web/icons/edit_trackers.png +++ b/deluge/ui/web/icons/edit_trackers.png diff --git a/deluge/ui/web/icons/error.png b/deluge/ui/web/icons/error.png Binary files differindex 20ad66a25..2ffb89fcc 100644 --- a/deluge/ui/web/icons/error.png +++ b/deluge/ui/web/icons/error.png diff --git a/deluge/ui/web/icons/expand_all.png b/deluge/ui/web/icons/expand_all.png Binary files differindex 050419f1c..013d55ad0 100644 --- a/deluge/ui/web/icons/expand_all.png +++ b/deluge/ui/web/icons/expand_all.png diff --git a/deluge/ui/web/icons/favicon.ico b/deluge/ui/web/icons/favicon.ico Binary files differindex 4e6f12436..176ca6688 100644 --- a/deluge/ui/web/icons/favicon.ico +++ b/deluge/ui/web/icons/favicon.ico diff --git a/deluge/ui/web/icons/find_more.png b/deluge/ui/web/icons/find_more.png Binary files differindex 199d73eae..e53a4ef57 100644 --- a/deluge/ui/web/icons/find_more.png +++ b/deluge/ui/web/icons/find_more.png diff --git a/deluge/ui/web/icons/forward.png b/deluge/ui/web/icons/forward.png Binary files differindex 2e5548996..95ee82203 100644 --- a/deluge/ui/web/icons/forward.png +++ b/deluge/ui/web/icons/forward.png diff --git a/deluge/ui/web/icons/help.png b/deluge/ui/web/icons/help.png Binary files differindex 0566ae067..e68c4586a 100644 --- a/deluge/ui/web/icons/help.png +++ b/deluge/ui/web/icons/help.png diff --git a/deluge/ui/web/icons/high.png b/deluge/ui/web/icons/high.png Binary files differindex 4b2b1ffac..6755979d6 100644 --- a/deluge/ui/web/icons/high.png +++ b/deluge/ui/web/icons/high.png diff --git a/deluge/ui/web/icons/home.png b/deluge/ui/web/icons/home.png Binary files differindex a319df64c..dda4941e9 100644 --- a/deluge/ui/web/icons/home.png +++ b/deluge/ui/web/icons/home.png diff --git a/deluge/ui/web/icons/inactive.png b/deluge/ui/web/icons/inactive.png Binary files differindex cae8b2c0a..e434dad9f 100644 --- a/deluge/ui/web/icons/inactive.png +++ b/deluge/ui/web/icons/inactive.png diff --git a/deluge/ui/web/icons/install_plugin.png b/deluge/ui/web/icons/install_plugin.png Binary files differindex aff31e77e..e50270478 100644 --- a/deluge/ui/web/icons/install_plugin.png +++ b/deluge/ui/web/icons/install_plugin.png diff --git a/deluge/ui/web/icons/login.png b/deluge/ui/web/icons/login.png Binary files differindex c06d0d119..ccc7ff3ad 100644 --- a/deluge/ui/web/icons/login.png +++ b/deluge/ui/web/icons/login.png diff --git a/deluge/ui/web/icons/logout.png b/deluge/ui/web/icons/logout.png Binary files differindex 2f53a65b4..1e9d5b83d 100644 --- a/deluge/ui/web/icons/logout.png +++ b/deluge/ui/web/icons/logout.png diff --git a/deluge/ui/web/icons/low.png b/deluge/ui/web/icons/low.png Binary files differindex ff669db7d..b9f1d2b04 100644 --- a/deluge/ui/web/icons/low.png +++ b/deluge/ui/web/icons/low.png diff --git a/deluge/ui/web/icons/magnet.png b/deluge/ui/web/icons/magnet.png Binary files differindex 61d6dabb7..6fc25ed69 100644 --- a/deluge/ui/web/icons/magnet.png +++ b/deluge/ui/web/icons/magnet.png diff --git a/deluge/ui/web/icons/magnet_add.png b/deluge/ui/web/icons/magnet_add.png Binary files differindex 37c1c36b0..f6010f54a 100644 --- a/deluge/ui/web/icons/magnet_add.png +++ b/deluge/ui/web/icons/magnet_add.png diff --git a/deluge/ui/web/icons/magnet_copy.png b/deluge/ui/web/icons/magnet_copy.png Binary files differindex a4be9d218..9b4ec6bbf 100644 --- a/deluge/ui/web/icons/magnet_copy.png +++ b/deluge/ui/web/icons/magnet_copy.png diff --git a/deluge/ui/web/icons/move.png b/deluge/ui/web/icons/move.png Binary files differindex ea83832c0..319cc166a 100644 --- a/deluge/ui/web/icons/move.png +++ b/deluge/ui/web/icons/move.png diff --git a/deluge/ui/web/icons/no_download.png b/deluge/ui/web/icons/no_download.png Binary files differindex 206d43660..e569b6a4f 100644 --- a/deluge/ui/web/icons/no_download.png +++ b/deluge/ui/web/icons/no_download.png diff --git a/deluge/ui/web/icons/normal.png b/deluge/ui/web/icons/normal.png Binary files differindex 2e5548996..95ee82203 100644 --- a/deluge/ui/web/icons/normal.png +++ b/deluge/ui/web/icons/normal.png diff --git a/deluge/ui/web/icons/ok.png b/deluge/ui/web/icons/ok.png Binary files differindex 33eb7db27..79195804c 100644 --- a/deluge/ui/web/icons/ok.png +++ b/deluge/ui/web/icons/ok.png diff --git a/deluge/ui/web/icons/pause.png b/deluge/ui/web/icons/pause.png Binary files differindex 8fdd6bc73..6e7b8c351 100644 --- a/deluge/ui/web/icons/pause.png +++ b/deluge/ui/web/icons/pause.png diff --git a/deluge/ui/web/icons/preferences.png b/deluge/ui/web/icons/preferences.png Binary files differindex 7d6deb246..e8e6a0666 100644 --- a/deluge/ui/web/icons/preferences.png +++ b/deluge/ui/web/icons/preferences.png diff --git a/deluge/ui/web/icons/queue.png b/deluge/ui/web/icons/queue.png Binary files differindex 3e4b4bee0..150ef8a43 100644 --- a/deluge/ui/web/icons/queue.png +++ b/deluge/ui/web/icons/queue.png diff --git a/deluge/ui/web/icons/queued.png b/deluge/ui/web/icons/queued.png Binary files differindex f9f74540f..74db4c5eb 100644 --- a/deluge/ui/web/icons/queued.png +++ b/deluge/ui/web/icons/queued.png diff --git a/deluge/ui/web/icons/recheck.png b/deluge/ui/web/icons/recheck.png Binary files differindex 199d73eae..e53a4ef57 100644 --- a/deluge/ui/web/icons/recheck.png +++ b/deluge/ui/web/icons/recheck.png diff --git a/deluge/ui/web/icons/remove.png b/deluge/ui/web/icons/remove.png Binary files differindex 3f911914d..70db1ca4e 100644 --- a/deluge/ui/web/icons/remove.png +++ b/deluge/ui/web/icons/remove.png diff --git a/deluge/ui/web/icons/seeding.png b/deluge/ui/web/icons/seeding.png Binary files differindex fce70a8d9..2d9dc58f7 100644 --- a/deluge/ui/web/icons/seeding.png +++ b/deluge/ui/web/icons/seeding.png diff --git a/deluge/ui/web/icons/start.png b/deluge/ui/web/icons/start.png Binary files differindex ff669db7d..b9f1d2b04 100644 --- a/deluge/ui/web/icons/start.png +++ b/deluge/ui/web/icons/start.png diff --git a/deluge/ui/web/icons/top.png b/deluge/ui/web/icons/top.png Binary files differindex 850656f3d..db9969d2b 100644 --- a/deluge/ui/web/icons/top.png +++ b/deluge/ui/web/icons/top.png diff --git a/deluge/ui/web/icons/traffic.png b/deluge/ui/web/icons/traffic.png Binary files differindex ecd87204d..b4ce5ea14 100644 --- a/deluge/ui/web/icons/traffic.png +++ b/deluge/ui/web/icons/traffic.png diff --git a/deluge/ui/web/icons/up.png b/deluge/ui/web/icons/up.png Binary files differindex 223e73326..9aa7c6437 100644 --- a/deluge/ui/web/icons/up.png +++ b/deluge/ui/web/icons/up.png diff --git a/deluge/ui/web/icons/update.png b/deluge/ui/web/icons/update.png Binary files differindex 0ff6d45a7..ee61b01e9 100644 --- a/deluge/ui/web/icons/update.png +++ b/deluge/ui/web/icons/update.png diff --git a/deluge/ui/web/icons/upload_slots.png b/deluge/ui/web/icons/upload_slots.png Binary files differindex 0e7000d81..294523eb5 100644 --- a/deluge/ui/web/icons/upload_slots.png +++ b/deluge/ui/web/icons/upload_slots.png diff --git a/deluge/ui/web/icons/warning.png b/deluge/ui/web/icons/warning.png Binary files differindex f66feda89..c81dc592f 100644 --- a/deluge/ui/web/icons/warning.png +++ b/deluge/ui/web/icons/warning.png 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('<', '<').replace('>', '>'); - return text.replace('&', '&'); - }, - 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 = { + '&': '&', + '>': '>', + '<': '<', + '"': '"', + "'": ''', + }; + + 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 '∞'; + } + 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 '∞'; - } - 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 ? '∞' : ftime(time); + if (time === 0) return ''; + if (time <= -1) return '∞'; + 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 Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/access/grid/hmenu-unlock.png b/deluge/ui/web/themes/images/access/grid/hmenu-unlock.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/access/slider/slider-bg.png b/deluge/ui/web/themes/images/access/slider/slider-bg.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/access/slider/slider-thumb.png b/deluge/ui/web/themes/images/access/slider/slider-thumb.png Binary files differindex 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 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 Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/access/window/right-corners.png b/deluge/ui/web/themes/images/access/window/right-corners.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/access/window/top-bottom.png b/deluge/ui/web/themes/images/access/window/top-bottom.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/default/grid/hmenu-lock.png b/deluge/ui/web/themes/images/default/grid/hmenu-lock.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/default/grid/hmenu-unlock.png b/deluge/ui/web/themes/images/default/grid/hmenu-unlock.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/default/slider/slider-bg.png b/deluge/ui/web/themes/images/default/slider/slider-bg.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/default/slider/slider-thumb.png b/deluge/ui/web/themes/images/default/slider/slider-thumb.png Binary files differindex 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 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 Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/default/window/right-corners.png b/deluge/ui/web/themes/images/default/window/right-corners.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/default/window/top-bottom.png b/deluge/ui/web/themes/images/default/window/top-bottom.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/gray/slider/slider-thumb.png b/deluge/ui/web/themes/images/gray/slider/slider-thumb.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/gray/window/left-corners.png b/deluge/ui/web/themes/images/gray/window/left-corners.png Binary files differindex 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 diff --git a/deluge/ui/web/themes/images/gray/window/right-corners.png b/deluge/ui/web/themes/images/gray/window/right-corners.png Binary files differindex 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 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__': @@ -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 Binary files differindex 58c2bed5b..58c2bed5b 100644 --- a/packaging/win32/installer-side.bmp +++ b/packaging/win/installer-side.bmp diff --git a/packaging/win32/installer-top.bmp b/packaging/win/installer-top.bmp Binary files differindex 1e1a94cd7..1e1a94cd7 100644 --- a/packaging/win32/installer-top.bmp +++ b/packaging/win/installer-top.bmp 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 Binary files differdeleted file mode 100644 index 12134fc0a..000000000 --- a/packaging/win32/DelugeStart Theme/lib/gtk-2.0/2.10.0/engines/libmurrine.dll +++ /dev/null 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 Binary files differdeleted file mode 100644 index 8cdd1cb23..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check1.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index a484a7019..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check2.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index faafa4748..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check3.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index bbaa5af19..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check4.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index e6b81d80c..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/check5.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index b00483813..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/checklight.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index eba2cbf67..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option1.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 22779c3d1..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option2.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index ecbb7e4d2..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option3.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 42aed6679..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Check-Radio/option4.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index d9b0608f2..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Icons/close.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 4c5468fd9..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-horiz.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 4c5468fd9..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/scroll-thumb-vert.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 1e60f4bef..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-insens.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index db46ba089..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz-prelight.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index fe5babaaf..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-horiz.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index ea9bd9999..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-insens.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 35ed7cf75..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert-prelight.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 2ffe36930..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/slider-vert.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 25a99c204..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-horiz.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 094364fa5..000000000 --- a/packaging/win32/DelugeStart Theme/share/themes/DelugeStart/gtk-2.0/Scrollbars/trough-scrollbar-vert.png +++ /dev/null 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 @@ -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 @@ -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, @@ -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) |