Compare commits

...

78 Commits

Author SHA1 Message Date
sledgehammer999
01519b5e77 Bump to 4.3.9 2021-10-31 12:36:05 +02:00
sledgehammer999
95ad606db1 Update Changelog 2021-10-31 12:35:31 +02:00
sledgehammer999
7a31c4c1fd Sync translations from Transifex and run lupdate 2021-10-31 12:35:07 +02:00
sledgehammer999
74b4c24c8f Sync translations from Transifex and run lupdate 2021-10-31 03:01:46 +03:00
SiderealArt
fac91a29e8 NSIS: Update Traditional Chinese translation
PR #15595.
2021-10-25 00:17:20 +03:00
Faisal Al-Munawar Fathur Rahman
a58cfe3f00 Update Indonesian translation
PR #15436.
2021-10-25 00:17:12 +03:00
Chocobo1
1bae770b2c Fix broken behavior of "priority by shown file order"
Closes #15421.
PR #15423.
2021-10-12 10:42:12 +08:00
Chocobo1
298d63d47c Prevent self-assignment in assignment operator 2021-10-12 10:42:12 +08:00
Chocobo1
c598bdd290 Guard for null pointer 2021-10-12 10:42:12 +08:00
gxcreator
8ba8d69bef Initialize member fields 2021-10-12 10:42:12 +08:00
Jose M. Abuin
57ec71b4d3 Add missing double-click action
Closes #15422.
PR #15509.
2021-10-12 10:42:12 +08:00
Chocobo1
c561c28614 Revert "WebUI: group trackers by hostname"
This functionality wasn't ever correctly implemented and couldn't be
done without considerable effort, so revert it for now.
This reverts commit 4ac25a50ed.
PR #15542.
2021-10-12 10:42:12 +08:00
Vladimir Golovnev
ac5c264e66 Properly handle exceptions when create torrent
Closes #15518.
PR #15532.
2021-10-08 12:47:31 +03:00
Chocobo1
338d9a0848 Remove unnecessary UI properties in "Add new torrent" dialog
Closes #15383.

PR #15387
2021-09-10 09:00:00 +08:00
JungHee Lee
0efd667d7e Update korean.nsi
PR #15380
2021-09-10 09:00:00 +08:00
Chocobo1
5fd6e18390 Fix WebUI crash when tracker URL is invalid
Closes #15391.
PR #15395.
2021-09-10 09:00:00 +08:00
xavier2k6
b5f4afabe0 Remove Windows Vista support from manifest
PR #15394.
2021-09-10 09:00:00 +08:00
sledgehammer999
9392ce436d Bump to 4.3.8 2021-08-29 01:57:25 +03:00
sledgehammer999
8ceb2d8832 Update Changelog 2021-08-29 01:56:30 +03:00
sledgehammer999
40ec873199 Sync translations from Transifex and run lupdate 2021-08-29 01:55:57 +03:00
Chocobo1
f3a5a43a20 Specify Unicode for resource block (#15370)
The StringFileInfo block was using "1252 Multilingual", change it to
"1200 Unicode" for consistency.
https://docs.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block

Closes #15364.
2021-08-29 00:13:09 +03:00
a1346054
72ba22afd1 Fix typo 2021-08-23 22:21:47 +03:00
a1346054
423232b4b9 Use license file verbatim
The GPL part of COPYING file was distributed incomplete, it must be
distributed verbatim.

The file was obtained from:
https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
2021-08-23 22:21:35 +03:00
sledgehammer999
86a6b53f82 Change IRC channel to Libera.Chat
Unfortunately Freenode, after a takeover, decided to purge all their databases, thus
deleting all channel and user registrations, without a warning.
So if we're forced to re-register our stuff why not go where the cool kids are at?
2021-08-23 22:21:34 +03:00
a-raccoon
618afb3609 Update README.md
Removed these two lines because they haven't been true in a very long time, and is no longer even possible.
```
You can also meet me (sledgehammer_999) on IRC:
`#qbittorrent on irc.freenode.net`
```
2021-08-23 22:21:17 +03:00
Chocobo1
fefce03379 GHA CI: Backport latest changes (#15361)
By having unified github workflows, the building cache would be utilized
more efficiently as the total cache size will be smaller (no more vcpkg
caching on macOS CI) and will stop thrashing the build cache (large
vcpkg cache evicts other smaller cache).

Relevant PRs:
#15321
#15340
#15342
#15355
2021-08-23 10:56:03 +08:00
Chocobo1
250cac73b6 Improve "last activity" calculation in WebAPI (#15339)
Co-authored-by: 秦风 <mayli.he@gmail.com>

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2021-08-21 12:59:40 +08:00
Chocobo1
84ec5a6acf Use default values for inconsequential fields
Or when the default value is actually what we want.
2021-08-21 12:59:40 +08:00
Chocobo1
0e2aca7583 Use default upper limits for ddns entries
The default is 32767 which is larger than the previous artificial limit.
2021-08-21 12:59:40 +08:00
Chocobo1
3bded7e71b Use the same icon for selecting folders/files
As stated in Qt doc, the `QStyle::SP_DialogOpenButton` is only for a
button within QDialogButtonBox which means it isn't suitable elsewhere.
2021-08-21 12:59:40 +08:00
Mengyang Li
51ca65eea1 WebUI: group trackers by hostname (#15264)
Closes #13608.
2021-08-21 12:59:40 +08:00
Chocobo1
f21f7b8edc Ignore file heath for 3rd party libraries 2021-08-21 12:59:40 +08:00
Chocobo1
331304159d Work around missing function error
The fix comes from older version of MochaUI.
2021-08-21 12:59:40 +08:00
Chocobo1
2aab675dc4 Update MochaUI to v0.9.7
Upstream: https://github.com/cdotyone/mochaui/blob/develop-0.9.8/Build/mocha.js
2021-08-21 12:59:40 +08:00
Chocobo1
9c8016dc65 Update clipboard.js to v2.0.8 2021-08-21 12:59:40 +08:00
Chocobo1
b99262bc36 Update mootools to v1.6.0 2021-08-21 12:59:40 +08:00
Sylvain Finot
078cfe656a Expose SSRF mitigation (#15247) 2021-08-21 12:59:40 +08:00
Matthaiks
89ac9bf2c9 NSIS: Add Polish translation (#15262) 2021-08-21 12:59:40 +08:00
Vladimir Golovnev
81932b6313 Delay processing of watched folders (#15282)
Fixes regression of #14882.
Closes #15272.
2021-08-21 12:59:40 +08:00
sledgehammer999
4ce5d9d5e2 Update grunt dependency
This fixes CVE-2020-7729
2021-08-21 12:59:40 +08:00
sledgehammer999
0415c0c6f8 Bump to 4.3.7 2021-08-03 23:24:47 +03:00
sledgehammer999
def5765160 Update Changelog 2021-08-03 23:20:32 +03:00
sledgehammer999
9977395c03 Sync translations from Transifex and run lupdate 2021-08-03 23:18:15 +03:00
Daniel Aleksandersen
2b05b2b471 Disconnect comment links fom the WebUI (#15251) 2021-08-03 23:00:47 +03:00
scootergrisen
d1bd426618 Update Danish translation (#15192) 2021-07-26 02:04:23 +03:00
Andrei Stepanov
b84c5edf51 Remove excess space 2021-07-26 02:04:16 +03:00
Vladimir Golovnev (glassez)
3ac8c97e6f Properly create "clean path" for watched folder 2021-07-12 11:45:57 +03:00
Vladimir Golovnev
ede42910da Merge pull request #15150 from glassez/backports
Backport changes to v4.3.x branch
2021-07-05 11:20:38 +03:00
xavier2k6
500d0e717b Update AppVeyor CI image to Visual Studio 2019 (#14983)
Update AppVeyor CI image to Visual Studio 2019
2021-07-03 11:44:43 +03:00
An0n
a3f039ffcb Bump file pool size (#14966) 2021-07-03 11:43:10 +03:00
Kacper Michajłow
81ad324209 Suppress C4267 conversion warnings
- warning C4267: 'initializing': conversion from 'size_t' to 'int',
  possible loss of data

Caused by mismatch between size_type of std and Qt containers. It is
safe to cast to int as all of those containers hold low number of
objects.
2021-07-03 11:16:04 +03:00
Vladimir Golovnev (Glassez)
81de07789a Provide tracker peers count via TrackerEntry
Don't expose additional accessor in Torrent interface.
2021-07-01 14:03:14 +03:00
Vladimir Golovnev (Glassez)
6a8a1f602f Don't overwrite tracker message
Use one of the tracker endpoint messages.
2021-07-01 13:44:48 +03:00
FranciscoPombal
9dfaaa2dd1 Remove TravisCI config 2021-07-01 11:09:35 +03:00
tgregerson
c06d6eaa77 Don't close tags menu when toggling items (#15098)
The issue was resolved by using QAction::toggled signal instead of
QAction::triggered. In QT 5.15+ the latter signal causes a QMenu
to close, whereas the former does not. Closes #13492.
2021-07-01 10:09:17 +03:00
Vladimir Golovnev (Glassez)
d198ee97a5 Don't forget to start "watch timer" 2021-07-01 09:44:46 +03:00
sledgehammer999
ac8105c304 Bump to 4.3.6 2021-06-26 21:24:22 +03:00
sledgehammer999
6cb16cfbb7 Update Changelog 2021-06-26 21:20:58 +03:00
sledgehammer999
2b475c4296 Sync translations from Transifex and run lupdate 2021-06-26 21:13:38 +03:00
sledgehammer999
3a9e4397fd Add new languages
* Mongolian
* Persian
* Thai
2021-06-26 21:12:41 +03:00
sledgehammer999
1ce0bb7cee Add Turkish Qt translation 2021-06-26 21:12:39 +03:00
sledgehammer999
dd4b09f3a9 Update Qt translations
Based on Qt 5.15.2
2021-06-26 21:12:38 +03:00
nonew-star
8c2df049f1 NSIS: Update Swedish translation (#14950)
Remove redundant spaces.
2021-06-26 21:12:38 +03:00
nonew-star
8b66c444ee NSIS: Update Swedish translation (#14933)
Added a word.
2021-06-26 21:12:37 +03:00
xkrstudio
0ccbdbccf4 NSIS: Update Hungarian translation (#14906)
Add hungarian translation.
2021-06-26 21:12:36 +03:00
Burak Yavuz
d08b6c81ba NSIS: Update Turkish translation
Uninstall string added
2021-06-26 21:12:36 +03:00
maboroshin
5f897709cf NSIS: Update Japanese translation 2021-06-26 21:12:30 +03:00
Vladimir Golovnev (Glassez)
e25948e737 Properly add torrent with new tags
First, an attempt is made to add new tags to the Session.
Closes #15105.
2021-06-19 10:52:08 +03:00
Vladimir Golovnev (Glassez)
144956a209 Remove lockfile only when last app instance is destroyed 2021-06-19 10:52:08 +03:00
Dmitry Khlestkov
667d4e4211 Keep sub-sorting order (#15074)
Fixes #15073
2021-06-15 14:19:53 +03:00
Vladimir Golovnev (Glassez)
d957eef331 Improve "Watched folders" feature
Make "file system watcher" an application core component
and separate it from its presentation model.
2021-06-15 14:19:53 +03:00
brvphoenix
baa32a20e0 Remove the lockfile on exit (#14997) 2021-06-15 14:19:53 +03:00
Chocobo1
eff465126e Fix main window turns blank after restoring from tray (#15031)
When restoring from tray icon, although the window manager shows qbt
window but qbt itself didn't handle the event correctly, i.e. the
`show()` was missing and thus qbt did nothing and the window is blank.
Note that at this point the `visible` property is `false`.
After invoking `show()` qbt will start showing the contents and also
fire another showEvent where `visible` property is `true` and here is where
qbt should handle preparations for the window.

Fix #9510.
2021-06-15 14:19:53 +03:00
thalieht
09089b2d33 Make some strings in WebUI translatable
Closes #14920
2021-06-15 14:19:53 +03:00
Vladimir Golovnev (Glassez)
3aa36ad40c Declare AbstractFileStorage destructor virtual 2021-06-15 14:19:53 +03:00
Vladimir Golovnev (Glassez)
24bc5a9875 Unify custom exceptions 2021-06-15 14:19:53 +03:00
Vladimir Golovnev (Glassez)
744a2cb5a3 Allow add torrents with relative save path
The relative save path will be resoloved against the default one.
2021-06-15 14:19:53 +03:00
Vladimir Golovnev (Glassez)
33e090cfcb Provide correct error description in "upload mode" 2021-06-08 08:06:11 +03:00
283 changed files with 141955 additions and 85592 deletions

View File

@@ -3,7 +3,7 @@ version: '{branch}-{build}'
# Do not build on tags (GitHub only)
skip_tags: true
image: Visual Studio 2017
image: Visual Studio 2019
branches:
except: # blacklist
@@ -42,7 +42,7 @@ install:
before_build:
# setup env
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
# setup project
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"

View File

@@ -1,240 +0,0 @@
name: GitHub Actions CI
# Cache is used for all Windows and macOS dependencies (size approx. 1230 * 2 + 1850 = 4310 MiB)
# Cache is not used for Ubuntu builds, because it already has all dependencies except
# the appropriate libtorrent version, which only takes 3-5 minutes to build from source anyway
on: [pull_request, push]
env:
VCPKG_COMMIT: e4ce66eecfd3e5cca5eac06c971921bf8e37cf88
VCPKG_DEST_MACOS: /Users/runner/qbt_tools/vcpkg
VCPKG_DEST_WIN: C:\qbt_tools\vcpkg
LIBTORRENT_VERSION_TAG: v1.2.12
jobs:
ci_ubuntu:
name: Ubuntu
strategy:
matrix:
os: [ubuntu-20.04]
qbt_gui: ["GUI=ON", "GUI=OFF"]
fail-fast: false
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: checkout repository
uses: actions/checkout@v2
- name: install all build dependencies except libtorrent from Ubuntu repos
run: |
sudo apt update
sudo apt install \
build-essential cmake git ninja-build pkg-config \
libssl-dev libgeoip-dev zlib1g-dev \
libboost-dev libboost-chrono-dev libboost-random-dev libboost-system-dev \
libqt5svg5-dev qtbase5-dev qttools5-dev
- name: install libtorrent from source
run: |
git clone https://github.com/arvidn/libtorrent && cd libtorrent
git checkout ${{ env.LIBTORRENT_VERSION_TAG }}
cmake -B cmake-build-dir -G "Ninja" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-Ddeprecated-functions=OFF \
--graphviz=cmake-build-dir/target_graph.dot
cmake --build cmake-build-dir
sudo cmake --install cmake-build-dir --prefix /usr/local
- name: build qBittorrent
run: |
cmake -B build -G "Ninja" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
- name: upload artifact as zip
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_${{ matrix.os }}-x64_${{ matrix.qbt_gui }}
path: |
build/compile_commands.json
build/target_graph.dot
build/qbittorrent
build/qbittorrent-nox
libtorrent/cmake-build-dir/compile_commands.json
libtorrent/cmake-build-dir/target_graph.dot
ci_windows:
name: Windows + vcpkg
runs-on: windows-2019
defaults:
run:
shell: pwsh
steps:
- name: checkout repository
uses: actions/checkout@v2
# - ninja is needed for building qBittorrent (because it's preferrable, not a hard requirement)
- name: install additional required packages with chocolatey
run: |
choco install ninja
- name: setup vcpkg (cached, if possible)
uses: lukka/run-vcpkg@v4
with:
vcpkgDirectory: ${{ env.VCPKG_DEST_WIN }}
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
setupOnly: true
# Tell vcpkg to only build Release variants of the dependencies
- name: configure vcpkg triplet overlay for release builds only
run: |
New-Item -Path ${{ github.workspace }} -Name "triplets_overlay" -ItemType Directory
Copy-Item ${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-windows-static.cmake `
${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake
Add-Content ${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake `
-Value "set(VCPKG_BUILD_TYPE release)"
# clear buildtrees after each package installation to reduce disk space requirements
- name: install dependencies via vcpkg
run: |
$packages = `
"boost-circular-buffer:x64-windows-static-release",
"libtorrent:x64-windows-static-release",
"qt5-base:x64-windows-static-release",
"qt5-svg:x64-windows-static-release",
"qt5-tools:x64-windows-static-release",
"qt5-winextras:x64-windows-static-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--no-dry-run
foreach($package in $packages)
{
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install $package `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--clean-after-build
}
# NOTE: this is necessary to correctly find and use cl.exe with the Ninja generator for now
- name: setup devcmd
uses: ilammy/msvc-dev-cmd@v1
- name: build qBittorrent
shell: cmd
run: |
cmake -B build -G "Ninja" ^
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo ^
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DEST_WIN }}\scripts\buildsystems\vcpkg.cmake ^
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release ^
-DVERBOSE_CONFIGURE=ON ^
-DMSVC_RUNTIME_DYNAMIC=OFF ^
--graphviz=build\target_graph.dot
cmake --build build
- name: upload artifact as zip
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_Windows-x64
path: |
build/compile_commands.json
build/target_graph.dot
build/qbittorrent.exe
build/qbittorrent.pdb
dist/windows/qt.conf
ci_macos:
name: macOS + vcpkg
strategy:
matrix:
qbt_gui: ["GUI=ON", "GUI=OFF"]
fail-fast: false
runs-on: macos-10.15
defaults:
run:
shell: pwsh
steps:
- name: checkout repository
uses: actions/checkout@v2
# - ninja is needed for building qBittorrent (because it's preferrable, not a hard requirement)
# - automake is needed for the installation the vcpkg installation of fontconfig, a dependency of qt5-base
- name: install additional required packages with homebrew
shell: bash
run: |
brew install automake ninja
- name: setup vcpkg (cached, if possible)
uses: lukka/run-vcpkg@v4
with:
vcpkgDirectory: ${{ env.VCPKG_DEST_MACOS }}
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
setupOnly: true
- name: configure vcpkg triplet overlay for release builds only
run: |
New-Item -Path ${{ github.workspace }} -Name "triplets_overlay" -ItemType Directory
Copy-Item ${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-osx.cmake `
${{ github.workspace }}/triplets_overlay/x64-osx-release.cmake
Add-Content ${{ github.workspace }}/triplets_overlay/x64-osx-release.cmake `
-Value "set(VCPKG_BUILD_TYPE release)","set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)"
- name: install dependencies via vcpkg
run: |
$packages = `
"boost-circular-buffer:x64-osx-release",
"libtorrent:x64-osx-release",
"qt5-base:x64-osx-release",
"qt5-svg:x64-osx-release",
"qt5-tools:x64-osx-release",
"qt5-macextras:x64-osx-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg upgrade `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--no-dry-run
foreach($package in $packages)
{
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg install $package `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--clean-after-build
}
- name: build qBittorrent
shell: bash
run: |
cmake -B build -G "Ninja" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DEST_MACOS }}/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=x64-osx-release \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
- name: upload artifact as zip
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}
path: |
build/compile_commands.json
build/target_graph.dot
build/qbittorrent.app
build/qbittorrent-nox.app

20
.github/workflows/ci_file_health.yaml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: CI - File health
on: [pull_request, push]
jobs:
ci:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install tools
run: |
sudo apt update
sudo apt install zsh
- name: Check files
run: |
./.github/workflows/file_health.sh

73
.github/workflows/ci_macos.yaml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: CI - macOS
on: [pull_request, push]
jobs:
ci:
name: Build
runs-on: macos-10.15
strategy:
matrix:
libt_version: ["v1.2.12"]
qbt_gui: ["GUI=ON", "GUI=OFF"]
fail-fast: false
env:
openssl_root: /usr/local/opt/openssl@1.1
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}
- name: Install dependencies
run: |
brew update > /dev/null
brew install \
cmake ninja \
boost openssl@1.1 qt@5 zlib
brew link --force \
qt@5
# workaround for cmake + Qt
sudo ln -s /usr/local/opt/qt@5/mkspecs /usr/local/mkspecs
sudo ln -s /usr/local/opt/qt@5/plugins /usr/local/plugins
- name: Install libtorrent
run: |
git clone --branch ${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
cd libtorrent
git submodule update --init --recursive
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_STANDARD=17 \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}"
cmake --build build
sudo cmake --install build
- name: Build qBittorrent
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}"
cmake --build build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}
path: |
build/qbittorrent.app
build/qbittorrent-nox.app

84
.github/workflows/ci_ubuntu.yaml vendored Normal file
View File

@@ -0,0 +1,84 @@
name: CI - Ubuntu
on: [pull_request, push]
jobs:
ci:
name: Build
runs-on: ubuntu-20.04
strategy:
matrix:
libt_version: ["v1.2.12"]
qbt_gui: ["GUI=ON", "GUI=OFF"]
fail-fast: false
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}
- name: Install dependencies
run: |
sudo apt update
sudo apt install \
build-essential cmake git ninja-build pkg-config \
libssl-dev libgeoip-dev zlib1g-dev \
libboost-dev libboost-chrono-dev libboost-random-dev libboost-system-dev
# sudo apt install libqt5svg5-dev qtbase5-dev qttools5-dev # the Qt version in the standard repositories is too old...
# this will be installed under /opt/qt515. CMake will still find it automatically without additional hints
# to speed up the process, only the required components are installed rather than the full qt515-meta-full metapackage
- name: Install Qt
run: |
sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-focal
sudo apt install \
qt515base qt515svg qt515tools
- name: Install libtorrent
run: |
git clone --branch ${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
cd libtorrent
git submodule update --init --recursive
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-Ddeprecated-functions=OFF \
--graphviz=cmake-build-dir/target_graph.dot
cmake --build build
sudo cmake --install build
- name: Build qBittorrent
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
- name: Install qBittorrent
run: sudo cmake --install build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_ubuntu-20.04-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}
path: |
build/compile_commands.json
build/install_manifest.txt
build/target_graph.dot
build/qbittorrent
build/qbittorrent-nox
libtorrent/cmake-build-dir/compile_commands.json
libtorrent/cmake-build-dir/target_graph.dot

32
.github/workflows/ci_webui.yaml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: CI - WebUI
on: [pull_request, push]
jobs:
ci:
name: Check
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/webui/www
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup nodejs
uses: actions/setup-node@v2
with:
node-version: 'lts/*'
- name: Install tools
run: npm install
- name: Lint code
run: npm run lint
- name: Format code
run: |
npm run format
git diff --exit-code

85
.github/workflows/ci_windows.yaml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: CI - Windows
on: [pull_request, push]
jobs:
ci:
name: Build
runs-on: windows-2019
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install build tools
run: |
choco install ninja
# use the preinstalled vcpkg from image
# https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#package-management
- name: Setup vcpkg
uses: lukka/run-vcpkg@v7
with:
vcpkgDirectory: C:/vcpkg
doNotUpdateVcpkg: true # the preinstalled vcpkg is updated regularly
setupOnly: true
# tell vcpkg to only build Release variants of the dependencies
- name: Configure vcpkg triplet overlay
run: |
New-Item `
-Path "${{ github.workspace }}" `
-Name "triplets_overlay" `
-ItemType Directory
Copy-Item `
"${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-windows-static.cmake" `
"${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake"
Add-Content `
"${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake" `
-Value "set(VCPKG_BUILD_TYPE release)"
# clear buildtrees after each package installation to reduce disk space requirements
- name: Install dependencies
run: |
$packages = `
"boost-circular-buffer:x64-windows-static-release",
"libtorrent:x64-windows-static-release",
"qt5-base:x64-windows-static-release",
"qt5-svg:x64-windows-static-release",
"qt5-tools:x64-windows-static-release",
"qt5-winextras:x64-windows-static-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
--overlay-triplets="${{ github.workspace }}/triplets_overlay" `
--no-dry-run
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install `
--overlay-triplets="${{ github.workspace }}/triplets_overlay" `
--clean-after-build `
$packages
# this is necessary to correctly find and use cl.exe with the Ninja generator for now
- name: Setup devcmd
uses: ilammy/msvc-dev-cmd@v1
- name: Build qBittorrent
run: |
cmake `
-B build `
-G "Ninja" `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
-DCMAKE_TOOLCHAIN_FILE="${{ env.RUNVCPKG_VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" `
-DMSVC_RUNTIME_DYNAMIC=OFF `
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release `
-DVERBOSE_CONFIGURE=ON `
--graphviz=build/target_graph.dot
cmake --build build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_Windows-x64
path: |
build/compile_commands.json
build/qbittorrent.exe
build/qbittorrent.pdb
build/target_graph.dot
dist/windows/qt.conf

View File

@@ -11,9 +11,9 @@ regressions=0
# exclusions (these are just grep extended regular expressions to match against paths relative to the root of the repository)
exclusions_nonutf8='(.*(7z|gif|ic(ns|o)|png|qm|zip))'
exclusions_bom='src/base/unicodestrings.h'
exclusions_tw='(*.ts)|src/webui/www/private/scripts/lib/mootools-1.2-more.js'
exclusions_tw='(*.ts)|src/webui/www/private/scripts/lib/*'
exclusions_trailing_newline='configure'
exclusions_no_lf='(*.ts)|(.*svg)|compile_commands.json|src/webui/www/private/scripts/lib/mootools-1.2-(core-yc.js|more.js)'
exclusions_no_lf='(*.ts)|(.*svg)|compile_commands.json|src/webui/www/private/scripts/lib/*'
echo -e "\n*** Detect files not encoded in UTF-8 ***\n"

View File

@@ -1,22 +0,0 @@
name: GitHub Actions file health check
on: [pull_request, push]
jobs:
check_file_health:
name: Check file health
runs-on: ubuntu-20.04
steps:
- name: checkout repository
uses: actions/checkout@v2
- name: install zsh
run: |
sudo apt update
sudo apt install zsh
- name: run check file health script
run: |
./.github/workflows/file_health.sh

View File

@@ -1,31 +0,0 @@
name: WebUI CI
on: [pull_request, push]
jobs:
check_webui:
name: Check WebUI
runs-on: ubuntu-20.04
defaults:
run:
working-directory: src/webui/www
steps:
- name: checkout repository
uses: actions/checkout@v2
- name: setup nodejs
uses: actions/setup-node@v2
with:
node-version: '14'
- name: install tools
run: npm install
- name: lint code
run: npm run lint
- name: format code
run: |
npm run format
git diff --exit-code

View File

@@ -1,188 +0,0 @@
language: cpp
os:
- linux
- osx
dist: focal
osx_image: xcode12.2
env:
matrix:
- libt_branch=RC_1_2 gui=true build_system=qmake
- libt_branch=RC_1_2 gui=false build_system=qmake
- libt_branch=RC_1_2 gui=true build_system=cmake
- libt_branch=RC_1_2 gui=false build_system=cmake
global:
- secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
- coverity_branch: coverity_scan
jobs:
include:
- env: libt_branch=RC_2_0 gui=true build_system=qmake
os: linux
notifications:
email:
on_success: change
on_failure: change
cache:
ccache: true
directories:
- $HOME/travis/deb
- $HOME/travis/brew
addons:
coverity_scan:
project:
name: "qbittorrent/qBittorrent"
description: "Build submitted via Travis CI"
build_command_prepend: "./bootstrap.sh && ./configure $qmake_conf"
build_command: "make -j2"
branch_pattern: $coverity_branch
notification_email: sledgehammer999@qbittorrent.org
apt:
sources:
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
- sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main'
key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc'
packages:
# packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty
- [autoconf, automake, cmake, colormake]
- [libboost-dev, libboost-system-dev]
- libssl-dev
- [qtbase5-dev, libqt5svg5-dev, qttools5-dev]
- zlib1g-dev
before_install:
# only allow specific build for coverity scan, others will stop
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$libt_branch" = "RC_1_2" -a "$gui" = "true" -a "$build_system" = "qmake" ]; then exit ; fi
- shopt -s expand_aliases
- alias make="colormake -j2" # Using nprocs/2 sometimes may fail (gcc is killed by system)
- qbt_path="$HOME/qbt_install"
- qmake_conf="$qmake_conf --prefix=$qbt_path"
- cmake_conf="$cmake_conf -DCMAKE_INSTALL_PREFIX=$qbt_path"
# options for specific branches
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# setup virtual display for after_success target
if [ "$gui" = "true" ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ;
# CMake from Kitware is installed in /usr/bin
# TravisCI installs its own cmake to another location which ovverides other installations
# if they don't call the new binary directly
alias cmake="/usr/bin/cmake"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedefs"
openssl_root_path="/usr/local/opt/openssl"
qmake_conf="$qmake_conf PKG_CONFIG_PATH=$openssl_root_path/lib/pkgconfig:$PKG_CONFIG_PATH"
cmake_conf="$cmake_conf -DOPENSSL_ROOT_DIR=$openssl_root_path"
fi
- |
if [ "$gui" = "false" ]; then
qmake_conf="$qmake_conf --disable-gui"
cmake_conf="$cmake_conf -DGUI=OFF"
fi
# print settings
- echo $libt_branch
- echo $gui
- echo $build_system
- echo $qmake_conf
- echo $cmake_conf
install:
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# dependencies
PATH="/usr/local/opt/ccache/libexec:$PATH"
brew update > /dev/null
brew upgrade cmake
brew install ccache colormake boost openssl qt@5 zlib
brew link --force qt@5 zlib
if [ "$build_system" = "cmake" ]; then
sudo ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
sudo ln -s /usr/local/opt/qt/plugins /usr/local/plugins
fi
fi
- |
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export use_ccache=true
ccache -M 512M
ccache -V && ccache --show-stats && ccache --zero-stats
fi
- |
if [ "$libt_branch" = "RC_1_2" ]; then
pushd "$HOME"
git clone --single-branch --branch RC_1_2 https://github.com/arvidn/libtorrent.git
cd libtorrent
git checkout tags/v1.2.12
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_STANDARD=17 \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="$openssl_root_path" \
./
make
sudo make install
popd
elif [ "$libt_branch" = "RC_2_0" ]; then
pushd "$HOME"
git clone --single-branch --branch RC_2_0 https://github.com/arvidn/libtorrent.git
cd libtorrent
git checkout tags/v2.0.2
git submodule update --init --recursive
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_STANDARD=17 \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="$openssl_root_path" \
./
make
sudo make install
popd
fi
script:
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # skip usual build when running coverity scan
- |
cd "$TRAVIS_BUILD_DIR"
if [ "$build_system" = "qmake" ]; then
# scan only as lupdate is prone to hang
lupdate -extensions c,cpp,h,hpp,ui ./
./bootstrap.sh
./configure $qmake_conf CXXFLAGS="$CXXFLAGS"
else
mkdir build && cd build
cmake $cmake_conf ../
fi
- make
- make install
after_success:
- if [ "$gui" = "true" ]; then qbt_exe="qbittorrent" ; else qbt_exe="qbittorrent-nox" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
if [ "$build_system" = "qmake" ]; then
macdeployqt "$TRAVIS_BUILD_DIR/src/$qbt_exe.app"
cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS"
else
cd "$qbt_path/$qbt_exe.app/Contents/MacOS"
fi
fi
- ./$qbt_exe --version
after_script:
- if [ "$use_ccache" = true ]; then ccache --show-stats ; fi

View File

@@ -10,7 +10,7 @@ project(qBittorrent
# use CONFIG mode first in find_package
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
# version requirements - older vesions may work, but you are on your own
# version requirements - older versions may work, but you are on your own
set(minBoostVersion 1.65)
set(minQtVersion 5.11)
set(minOpenSSLVersion 1.1.1)

65
COPYING
View File

@@ -18,8 +18,8 @@ See also the AUTHORS file
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -32,7 +32,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -295,3 +295,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 2 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -1,3 +1,41 @@
Sun Oct 31 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.9
- BUGFIX: Fix "no action" option on torrent double click (Jose M. Abuin)
- BUGFIX: Fix broken behavior of "priority by shown file order" (Chocobo1)
- WEBUI: Fix WebUI crash when tracker URL is invalid (Chocobo1)
- WEBUI: Revert "WebUI: group trackers by hostname" (Chocobo1)
- WINDOWS: Remove Windows Vista support from manifest (xavier2k6)
- WINDOWS: NSIS: Update Korean, Indonesian and Traditional Chinese translation (JungHee Lee, Faisal Al-Munawar Fathur Rahman, SiderealArt)
Sun Aug 29 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.8
- BUGFIX: Delay processing of watched folders (#15282) (glassez)
- BUGFIX: Use the same icon for selecting folders/files (Chocobo1)
- BUGFIX: Use default upper limits for ddns entries (Chocobo1)
- WEBUI: Expose SSRF mitigation (#15247) (Sylvain Finot)
- WEBUI: Update webui libraries (Chocobo1)
- WEBUI: Group trackers by hostname (#15264) (Mengyang Li)
- WEBUI: Improve "last activity" calculation in WebAPI (#15339) (Chocobo1)
- WINDOWS: NSIS: Add Polish translation (#15262) (Matthaiks)
Tue Aug 03 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.7
- BUGFIX: Don't forget to start Watched folders timer (glassez)
- BUGFIX: Don't close tags menu when toggling items (tgregerson)
- BUGFIX: Don't overwrite tracker message (glassez)
- BUGFIX: Bump file pool size (#14966) (An0n)
- BUGFIX: Properly create "clean path" for watched folder (glassez)
- WEBUI: Disconnect comment links (Daniel Aleksandersen)
- WINDOWS: NSIS: Update Danish translation (scootergrisen)
Sat Jun 26 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.6
- FEATURE: New languages: Mongolian, Persian, Thai
- BUGFIX: Provide correct error description in "upload mode" (glassez)
- BUGFIX: Allow adding torrents with relative save path (glassez)
- BUGFIX: Fix main window turns blank after restoring from tray (#15031) (Chocobo1)
- BUGFIX: Remove the lockfile on exit (#14997) (brvphoenix)
- BUGFIX: Improve "Watched folders" feature (glassez)
- BUGFIX: Keep sub-sorting order (#15074) (Dmitry Khlestkov)
- BUGFIX: Properly add torrent with new tags (glassez)
- WINDOWS: NSIS: Update Japanese, Turkish, Hungarian, Swedish translation (maboroshin, Burak Yavuz, xkrstudio, nonew-star)
Sun May 02 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.5
- BUGFIX: Move cursor to the end when autofilling URL/hash in "Download from URLs" dialog (Chocobo1)
- BUGFIX: Sort invalid QDateTime values after valid values (Chocobo1)

View File

@@ -1,7 +1,6 @@
qBittorrent - A BitTorrent client in Qt
------------------------------------------
[![TravisCI Status](https://travis-ci.org/qbittorrent/qBittorrent.svg?branch=master)](https://travis-ci.org/qbittorrent/qBittorrent)
[![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/qbittorrent/qBittorrent?branch=master&svg=true)](https://ci.appveyor.com/project/qbittorrent/qBittorrent)
[![GitHub Actions CI Status](https://github.com/qbittorrent/qBittorrent/workflows/GitHub%20Actions%20CI/badge.svg)](https://github.com/qbittorrent/qBittorrent/actions)
[![Coverity Status](https://scan.coverity.com/projects/5494/badge.svg)](https://scan.coverity.com/projects/5494)
@@ -46,8 +45,8 @@ http://forum.qbittorrent.org
Please report any bug (or feature request) to:
http://bugs.qbittorrent.org
You can also meet me (sledgehammer_999) on IRC:
`#qbittorrent on irc.freenode.net`
Official IRC channel:
`#qbittorrent on irc.libera.chat`
------------------------------------------
sledgehammer999 <sledgehammer999@qbittorrent.org>

20
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.70 for qbittorrent v4.3.5.
# Generated by GNU Autoconf 2.70 for qbittorrent v4.3.9.
#
# Report bugs to <bugs.qbittorrent.org>.
#
@@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v4.3.5'
PACKAGE_STRING='qbittorrent v4.3.5'
PACKAGE_VERSION='v4.3.9'
PACKAGE_STRING='qbittorrent v4.3.9'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/'
@@ -1330,7 +1330,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v4.3.5 to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.3.9 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1401,7 +1401,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.3.5:";;
short | recursive ) echo "Configuration of qbittorrent v4.3.9:";;
esac
cat <<\_ACEOF
@@ -1538,7 +1538,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v4.3.5
qbittorrent configure v4.3.9
generated by GNU Autoconf 2.70
Copyright (C) 2020 Free Software Foundation, Inc.
@@ -1700,7 +1700,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v4.3.5, which was
It was created by qbittorrent $as_me v4.3.9, which was
generated by GNU Autoconf 2.70. Invocation command line was
$ $0$ac_configure_args_raw
@@ -4848,7 +4848,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v4.3.5'
VERSION='v4.3.9'
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -7401,7 +7401,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.3.5, which was
This file was extended by qbittorrent $as_me v4.3.9, which was
generated by GNU Autoconf 2.70. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7461,7 +7461,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
qbittorrent config.status v4.3.5
qbittorrent config.status v4.3.9
configured by $0, generated by GNU Autoconf 2.70,
with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v4.3.5], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.3.9], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
: ${CFLAGS=""}

2
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.3.5</string>
<string>4.3.9</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/qt-translations/qtbase_tr.qm vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -74,6 +74,6 @@
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
<content_rating type="oars-1.1"/>
<releases>
<release version="4.3.5" date="2021-05-02"/>
<release version="4.3.9" date="2021-10-31"/>
</releases>
</component>

View File

@@ -49,8 +49,8 @@ Name[da]=qBittorrent
Comment[de]=Über BitTorrent Dateien herunterladen und teilen
GenericName[de]=BitTorrent Client
Name[de]=qBittorrent
Comment[el]=Κάντε λήψη και ανταλλάξτε αρχεία μέσω BitTorrent
GenericName[el]=Πελάτης BitTorrent
Comment[el]=Κάντε λήψη και μοιραστείτε αρχεία μέσω BitTorrent
GenericName[el]=BitTorrent client
Name[el]=qBittorrent
Comment[en_GB]=Download and share files over BitTorrent
GenericName[en_GB]=BitTorrent client
@@ -70,7 +70,7 @@ Name[fa]=کیو بیت تورنت
Comment[fi]=Lataa ja jaa tiedostoja BitTorrentia käyttäen
GenericName[fi]=BitTorrent-asiakasohjelma
Name[fi]=qBittorrent
Comment[fr]=Télécharger et partager des fichiers avec BitTorrent
Comment[fr]=Letölteni és megosztani a dokumentumokat Bittorrenttel
GenericName[fr]=Client BitTorrent
Name[fr]=qBittorrent
Comment[gl]=Descargar e compartir ficheiros co protocolo BitTorrent
@@ -106,8 +106,8 @@ Name[ja]=qBittorrent
Comment[ka]=ჩამოტვირთე და გააზიარე ფაილები Bittorrent-ის საშუალებით
GenericName[ka]=BitTorrent კლიენტი
Name[ka]=qBittorrent
Comment[ko]=비트토렌트를 통해 파일을 받고 공유합니다
GenericName[ko]=비트토렌트 클라이언트
Comment[ko]=BitTorrent를 통해 파일 다운로드 및 공유
GenericName[ko]=BitTorrent 클라이언트
Name[ko]=qBittorrent
Comment[zh]=通过 BitTorrent 下载和分享文件
GenericName[zh]=BitTorrent 客户端
@@ -142,8 +142,8 @@ Name[pt_BR]=qBittorrent
Comment[ro]=Descărcați și partajați fișiere prin BitTorrent
GenericName[ro]=Client BitTorrent
Name[ro]=qBittorrent
Comment[ru]=Обмен файлами по сети BitTorrent
GenericName[ru]=Клиент сети BitTorrent
Comment[ru]=Обмен файлами по сети БитТоррент
GenericName[ru]=Клиент сети БитТоррент
Name[ru]=qBittorrent
Comment[sk]=Sťahovanie a zdieľanie súborov prostredníctvom siete BitTorrent
GenericName[sk]=Klient siete BitTorrent
@@ -153,6 +153,7 @@ GenericName[sl]=BitTorrent odjemalec
Name[sl]=qBittorrent
Comment[sr]=Преузимајте и делите фајлове преко BitTorrent протокола
GenericName[sr]=BitTorrent-клијент
Name[sr]=qBittorrent
Comment[sr@latin]=Preuzimanje i deljenje fajlova preko BitTorrent-a
GenericName[sr@latin]=BitTorrent klijent
Name[sr@latin]=qBittorrent
@@ -177,8 +178,8 @@ Name[ur]=قیو بٹ ٹورنٹ
Comment[uk]=Завантажуйте та поширюйте файли через BitTorrent
GenericName[uk]=BitTorrent-клієнт
Name[uk]=qBittorrent
Comment[vi]=Tải về và chia sẻ các tập tin thông qua BitTorrent
GenericName[vi]=Máy trạm dạng BitTorrent
Comment[vi]=Tải về và chia sẻ tệp qua BitTorrent
GenericName[vi]=Máy khách BitTorrent
Name[vi]=qBittorrent
Comment[az@latin]=Faylları BitTorrent vasitəsilə göndərin və paylaşın
GenericName[az@latin]=BitTorrent client
@@ -192,6 +193,9 @@ Name[zh_TW]=qBittorrent
Comment[lv_LV]=Lejupielādēt un koplietot failus ar BitTorrent
GenericName[lv_LV]=BitTorrent klients
Name[lv_LV]=qBittorrent
Comment[kk]=BitTorrent арқылы файл жүктеу және бөлісу
GenericName[kk]=BitTorrent клиенті
Name[kk]=qBittorrent
Comment[ms_MY]=Muat turun dan kongsi fail melalui BitTorrent
GenericName[ms_MY]=Klien BitTorrent
Name[ms_MY]=qBittorrent
@@ -213,6 +217,6 @@ Name[te]=క్యు బిట్ టొరెంట్
Comment[pt_PT]=Transferir e partilhar ficheiros por BitTorrent
GenericName[pt_PT]=Cliente BitTorrent
Name[pt_PT]=qBittorrent
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ด้วยบิททอเร้น
GenericName[th]=โปรแกรมบิททอเร้น
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่าน BitTorrent
GenericName[th]=ไคลเอนต์ BitTorrent
Name[th]=qBittorrent

View File

@@ -7,7 +7,7 @@ LangString inst_dekstop ${LANG_DANISH} "Opret skrivebordsgenvej"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_DANISH} "Opret genvej i menuen Start"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_DANISH} "Start qBittorrent i Windows opstart"
LangString inst_startup ${LANG_DANISH} "Start qBittorrent når Windows starter"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_DANISH} "Åbn .torrent-filer med qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
@@ -15,7 +15,7 @@ LangString inst_magnet ${LANG_DANISH} "Åbn magnet-links med qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_DANISH} "Tilføj Windows Firewall-regel"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_DANISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_DANISH} "Deaktivér grænse for for lang Windows-stien kan være (MAX_PATH-begrænsning på 260 tegn, kræver Windows 10 1607 eller senere)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_DANISH} "Tilføjer Windows Firewall-regel"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
@@ -27,11 +27,11 @@ LangString inst_unist ${LANG_DANISH} "Afinstallerer tidligere version."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_DANISH} "Start qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_DANISH} "Dette installationsprogram virker kun i 64-bit Windows versioner."
LangString inst_requires_64bit ${LANG_DANISH} "Installationsprogrammet virker kun i Windows-versioner som er 64-bit."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_DANISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_DANISH} "qBittorrent-versionen kræver mindst Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_DANISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_DANISH} "Afinstaller qBittorrent"
;------------------------------------
;Uninstaller strings

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_HUNGARIAN} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_HUNGARIAN} "qBittorrent (kötelező)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_HUNGARIAN} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_HUNGARIAN} "Asztali parancsikon létrehozása"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_HUNGARIAN} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_HUNGARIAN} "Start menüben parancsikon létrehozása"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_HUNGARIAN} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_HUNGARIAN} "qBittorrent indítása Windows indításakor"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_HUNGARIAN} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_HUNGARIAN} ".torrent fájlok megnyitása qBittorrenttel"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_HUNGARIAN} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_HUNGARIAN} "Magnet linkek megnyitása qBittorrenttel"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_HUNGARIAN} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_HUNGARIAN} "Windows Tűzfal szabály hozzáadása"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_HUNGARIAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_HUNGARIAN} "A Windows elérési útvonalak hosszának feloldása (A MAX_PATH korlátozás 260 karakteres, Windows 10 1607 vagy újabb verzió szükséges)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_HUNGARIAN} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_HUNGARIAN} "Windows Tűzfal szabály hozzáadása folyamatban"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_HUNGARIAN} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_HUNGARIAN} "A qBittorrent fut. Kérem zárjba be az alkalmazást a telepítés előtt."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_HUNGARIAN} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_HUNGARIAN} "A jelenlegi verzió el lesz távolítva. A felhasználói beállítások és a torrentek megmaradnak."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_HUNGARIAN} "Uninstalling previous version."
LangString inst_unist ${LANG_HUNGARIAN} "Előző verzió eltávolítása."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_HUNGARIAN} "Launch qBittorrent."
LangString launch_qbt ${LANG_HUNGARIAN} "qBittorrent indítása."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_HUNGARIAN} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_HUNGARIAN} "A telepítő csak 64-bites Windows verziókon működik."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_HUNGARIAN} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_HUNGARIAN} "A qBittorrent ezen verziójához minimum Windows 7 szükséges."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_HUNGARIAN} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_HUNGARIAN} "qBittorrent eltávolítása"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_HUNGARIAN} "Remove files"
LangString remove_files ${LANG_HUNGARIAN} "Fájlok eltávolítása"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_HUNGARIAN} "Remove shortcuts"
LangString remove_shortcuts ${LANG_HUNGARIAN} "Parancsikonok eltávolítása"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_HUNGARIAN} "Remove file associations"
LangString remove_associations ${LANG_HUNGARIAN} "Fájltársítások eltávolítása"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_HUNGARIAN} "Remove registry keys"
LangString remove_registry ${LANG_HUNGARIAN} "Regisztrációs kulcsok eltávolítása"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_HUNGARIAN} "Remove configuration files"
LangString remove_conf ${LANG_HUNGARIAN} "Konfigurációs fájlok eltávolítása"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_HUNGARIAN} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_HUNGARIAN} "Windows Tűzfal szabály eltávolítása"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_HUNGARIAN} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_HUNGARIAN} "Windows Tűzfal szabály eltávolítása folyamatban"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_HUNGARIAN} "Remove torrents and cached data"
LangString remove_cache ${LANG_HUNGARIAN} "Torrentek és gyorsítótárazott adatok eltávolítása"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_HUNGARIAN} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_HUNGARIAN} "A qBittorrent jelenleg is fut. Kérem zárja be az alkalmazást mielőtt eltávolítaná."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_HUNGARIAN} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_HUNGARIAN} "A .torrent fájlok társítása nem lett eltávolítva. Társítva van:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_HUNGARIAN} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_HUNGARIAN} "A magnet linkek társítása nem lett eltávolítva. Társítva van:"

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_INDONESIAN} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_INDONESIAN} "qBittorrent (diperlukan)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_INDONESIAN} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_INDONESIAN} "Buat Pintasan Desktop"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_INDONESIAN} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_INDONESIAN} "Buat Pintasan Menu"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_INDONESIAN} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_INDONESIAN} "Jalankan qBittorrent pada start up Windows"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_INDONESIAN} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_INDONESIAN} "Buka file .torrent dengan qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_INDONESIAN} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_INDONESIAN} "Buka link magnet dengan qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_INDONESIAN} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_INDONESIAN} "Tambahkan aturan Windows Firewall"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_INDONESIAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_INDONESIAN} "Matikan batasan panjang path Windows (limitasi MAX_PATH 260 karaker, membutuhkan Windows 10 1607 atau lebih baru)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_INDONESIAN} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_INDONESIAN} "Menambahkan aturan Windows Firewall"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_INDONESIAN} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_INDONESIAN} "qBittorrent berjalan. Mohon tutup aplikasi sebelum memasang."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_INDONESIAN} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_INDONESIAN} "Versi saat ini akan dicopot. Pengaturan pengguna dan torrent akan tetap utuh."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_INDONESIAN} "Uninstalling previous version."
LangString inst_unist ${LANG_INDONESIAN} "Mencopot versi sebelumnya."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_INDONESIAN} "Launch qBittorrent."
LangString launch_qbt ${LANG_INDONESIAN} "Buka qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_INDONESIAN} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_INDONESIAN} "Aplikasi ini hanya berjalan pada versi Windows 64-bit."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_INDONESIAN} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_INDONESIAN} "Versi qBittorrent ini membutuhkan setidaknya Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_INDONESIAN} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_INDONESIAN} "Copot qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_INDONESIAN} "Remove files"
LangString remove_files ${LANG_INDONESIAN} "Hapus file"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_INDONESIAN} "Remove shortcuts"
LangString remove_shortcuts ${LANG_INDONESIAN} "Hapus pintasan"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_INDONESIAN} "Remove file associations"
LangString remove_associations ${LANG_INDONESIAN} "Hapus asosiasi file"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_INDONESIAN} "Remove registry keys"
LangString remove_registry ${LANG_INDONESIAN} "Hapus key registri"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_INDONESIAN} "Remove configuration files"
LangString remove_conf ${LANG_INDONESIAN} "Hapus file konfigurasi"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_INDONESIAN} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_INDONESIAN} "Hapus aturan Windows Firewall"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_INDONESIAN} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_INDONESIAN} "Menghapus aturan Windows Firewall"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_INDONESIAN} "Remove torrents and cached data"
LangString remove_cache ${LANG_INDONESIAN} "Hapus torrent dan data ter-cache"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_INDONESIAN} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_INDONESIAN} "qBittorrent berjalan. Mohon tutup aplikasi sebelum mencopot."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_INDONESIAN} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_INDONESIAN} "Tidak menghapus asosiasi .torrent. Terasosiasi dengan:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_INDONESIAN} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_INDONESIAN} "Tidak menghapus asosiasi magnet. Terasosiasi dengan:"

View File

@@ -19,9 +19,9 @@ LangString inst_pathlimit ${LANG_JAPANESE} "Windows のパスの文字長制限
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_JAPANESE} "Windows ファイアウォールのルールを追加"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_JAPANESE} "qBittorrent 実行中です。インストールの前にアプリケーションを終了してください。"
LangString inst_warning ${LANG_JAPANESE} "qBittorrent 実行中です。インストールの前にアプリケーションを終了してください。"
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_JAPANESE} "以前のインストールが検出されました。ユーザー設定を削除せずにアンインストールします。"
LangString inst_uninstall_question ${LANG_JAPANESE} "利用中のバージョンをアンインストールします。ユーザー設定やトレントは残されます。"
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_JAPANESE} "以前のバージョンをアンインストールしています。"
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
@@ -31,7 +31,7 @@ LangString inst_requires_64bit ${LANG_JAPANESE} "このインストーラーは
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_JAPANESE} "このバージョンの qBittorrent には Windows 7 以降が必要です。"
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_JAPANESE} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_JAPANESE} "qBittorrent をアンインストール"
;------------------------------------
;Uninstaller strings

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_KOREAN} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_KOREAN} "qBittorrent (필요함)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_KOREAN} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_KOREAN} "바탕화면 바로가기 만들기"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_KOREAN} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_KOREAN} "시작 메뉴 바로가기 만들기"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_KOREAN} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_KOREAN} "Windows 시작 시 qBittorrent 시작"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_KOREAN} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_KOREAN} "qBittorrent로 .torrent 파일 열기"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_KOREAN} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_KOREAN} "qBittorrent로 자석 링크 열기"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_KOREAN} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_KOREAN} "Windows 방화벽 규칙 추가"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_KOREAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_KOREAN} "Windows 경로 길이 제한 비활성화(260자 MAX_PATH 제한, Windows 10 1607 이상 필요)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_KOREAN} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_KOREAN} "Windows 방화벽 규칙 추가하는 중"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_KOREAN} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_KOREAN} "qBittorrent가 실행 중입니다. 설치하기 전에 응용 프로그램을 닫으십시오."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_KOREAN} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_KOREAN} "현재 버전이 삭제됩니다. 사용자 설정과 토렌트는 그대로 유지됩니다."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_KOREAN} "Uninstalling previous version."
LangString inst_unist ${LANG_KOREAN} "이전 버전을 삭제하는 중입니다."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_KOREAN} "Launch qBittorrent."
LangString launch_qbt ${LANG_KOREAN} "qBittorrent를 실행합니다."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_KOREAN} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_KOREAN} "이 설치 프로그램은 64비트 윈도우즈 버전에서만 작동합니다."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_KOREAN} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_KOREAN} " qBittorrent 버전에는 Windows 7 이상이 필요합니다."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_KOREAN} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_KOREAN} "qBittorrent 삭제"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_KOREAN} "Remove files"
LangString remove_files ${LANG_KOREAN} "파일 제거"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_KOREAN} "Remove shortcuts"
LangString remove_shortcuts ${LANG_KOREAN} "바로가기 제거"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_KOREAN} "Remove file associations"
LangString remove_associations ${LANG_KOREAN} "파일 연결 제거"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_KOREAN} "Remove registry keys"
LangString remove_registry ${LANG_KOREAN} "레지스트리 키 제거"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_KOREAN} "Remove configuration files"
LangString remove_conf ${LANG_KOREAN} "구성 파일 제거"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_KOREAN} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_KOREAN} "Windows 방화벽 규칙 제거"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_KOREAN} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_KOREAN} "Windows 방화벽 규칙 제거하는 중"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_KOREAN} "Remove torrents and cached data"
LangString remove_cache ${LANG_KOREAN} "토렌트 및 캐시된 데이터 제거"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_KOREAN} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_KOREAN} "qBittorrent가 실행 중입니다. 삭제하기 전에 응용 프로그램을 닫으십시오."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_KOREAN} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_KOREAN} ".torrent 연결을 제거하지 않습니다. 관련 항목:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_KOREAN} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_KOREAN} "자석 연결을 제거하지 않습니다. 관련 항목:"

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_POLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_POLISH} "qBittorrent (wymagany)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_POLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_POLISH} "Utwórz skrót na pulpicie"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_POLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_POLISH} "Utwórz skrót w menu Start"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_POLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_POLISH} "Uruchamiaj qBittorrent wraz z systemem Windows"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_POLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_POLISH} "Otwieraj pliki .torrent za pomocą qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_POLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_POLISH} "Otwieraj odnośniki magnet za pomocą qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_POLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_POLISH} "Dodaj regułę Zapory systemu Windows"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_POLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_POLISH} "Wyłącz ograniczenie długości ścieżki systemu Windows (ograniczenie MAX_PATH do 260 znaków, wymaga systemu Windows 10 1607 lub nowszego)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_POLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_POLISH} "Dodawanie reguły Zapory systemu Windows"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_POLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_POLISH} "qBittorrent jest uruchomiony. Proszę zamknąć aplikację przed instalacją."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_POLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_POLISH} "Obecna wersja zostanie odinstalowana. Ustawienia użytkownika i torrenty pozostaną nienaruszone."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_POLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_POLISH} "Odinstalowywanie poprzedniej wersji."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_POLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_POLISH} "Uruchom qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_POLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_POLISH} "Ten instalator działa tylko w 64-bitowych wersjach systemu Windows."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_POLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_POLISH} "Ta wersja qBittorrent wymaga co najmniej systemu Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_POLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_POLISH} "Odinstaluj qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_POLISH} "Remove files"
LangString remove_files ${LANG_POLISH} "Usuń pliki"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_POLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_POLISH} "Usuń skróty"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_POLISH} "Remove file associations"
LangString remove_associations ${LANG_POLISH} "Usuń skojarzenia plików"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_POLISH} "Remove registry keys"
LangString remove_registry ${LANG_POLISH} "Usuń klucze rejestru"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_POLISH} "Remove configuration files"
LangString remove_conf ${LANG_POLISH} "Usuń pliki konfiguracyjne"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_POLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_POLISH} "Usuń regułę Zapory systemu Windows"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_POLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_POLISH} "Usuwanie reguły Zapory systemu Windows"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_POLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_POLISH} "Usuń torrenty i dane z pamięci podręcznej"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_POLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_POLISH} "qBittorrent jest uruchomiony. Zamknij aplikację przed odinstalowaniem."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_POLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_POLISH} "Bez usuwania skojarzenia .torrent. Skojarzono z:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_POLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_POLISH} "Bez usuwania skojarzenia magnet. Skojarzono z:"

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_SWEDISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_SWEDISH} "qBittorrent (krävs)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_SWEDISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_SWEDISH} "Skapa skrivbordsgenväg"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_SWEDISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_SWEDISH} "Skapa startmenygenväg"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_SWEDISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows start"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_SWEDISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_SWEDISH} "Öppna .torrent-filer med qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_SWEDISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_SWEDISH} "Öppna magnetlänkar med qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_SWEDISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggregel"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_SWEDISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_SWEDISH} "Inaktivera gränsen för Windows-sökvägslängd (260 tecken MAX_PATH-begränsning, kräver Windows 10 1607 eller senare)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_SWEDISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggregel"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_SWEDISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du installerar."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_SWEDISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_SWEDISH} "Nuvarande version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_SWEDISH} "Uninstalling previous version."
LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SWEDISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SWEDISH} "Kör qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SWEDISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SWEDISH} "Installationsprogrammet fungerar endast i 64-bitars Windows-versioner."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_SWEDISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_SWEDISH} "Den här qBittorrent-versionen kräver minst Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_SWEDISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_SWEDISH} "Avinstallera qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_SWEDISH} "Remove files"
LangString remove_files ${LANG_SWEDISH} "Ta bort filer"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_SWEDISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_SWEDISH} "Ta bort genvägar"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_SWEDISH} "Remove file associations"
LangString remove_associations ${LANG_SWEDISH} "Ta bort filassociationer"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_SWEDISH} "Remove registry keys"
LangString remove_registry ${LANG_SWEDISH} "Ta bort registernycklar"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_SWEDISH} "Remove configuration files"
LangString remove_conf ${LANG_SWEDISH} "Ta bort konfigurationsfiler"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_SWEDISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_SWEDISH} "Ta bort Windows-brandväggsregel"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_SWEDISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_SWEDISH} "Tar bort Windows-brandväggsregel"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_SWEDISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_SWEDISH} "Ta bort torrenter och cachade data"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du avinstallerar."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_SWEDISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_SWEDISH} "Tar inte bort .torrent-association. Den är associerad med:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_SWEDISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_SWEDISH} "Tar inte bort magnet-association. Den är associerad med:"

View File

@@ -15,21 +15,21 @@ LangString inst_magnet ${LANG_TRADCHINESE} "使用 qBittorrent 開啟 magnet 連
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_TRADCHINESE} "建立 Windows 防火牆規則"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_TRADCHINESE} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_TRADCHINESE} "停用 Windows 路徑長度限制 (MAX_PATH 260 字元限制,需 Windows 10 1607 以上版本)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_TRADCHINESE} "正在建立 Windows 防火牆規則"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_TRADCHINESE} "qBittorrent 正在執行中,請先關閉後再進行安裝。"
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_TRADCHINESE} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_TRADCHINESE} "目前版本將被解除安裝。使用者設定及 torrent 將保留。"
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_TRADCHINESE} "正在移除先前版本"
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_TRADCHINESE} "啟動 qBittorrent"
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_TRADCHINESE} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_TRADCHINESE} "此安裝程式僅支援 64 位元版本的 Windows。"
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_TRADCHINESE} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_TRADCHINESE} " qBittorrent 版本僅支援 Windows 7 以上的系統。"
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_TRADCHINESE} "移除 qBittorrent"

View File

@@ -31,7 +31,7 @@ LangString inst_requires_64bit ${LANG_TURKISH} "Bu yükleyici sadece 64-bit Wind
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_TURKISH} "Bu qBittorrent sürümü en az Windows 7 gerektirir."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_TURKISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_TURKISH} "qBittorrent'i kaldır"
;------------------------------------
;Uninstaller strings

View File

@@ -28,7 +28,7 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific
!define PROG_VERSION "4.3.5"
!define PROG_VERSION "4.3.9"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun

View File

@@ -75,9 +75,9 @@
#include "base/profile.h"
#include "base/rss/rss_autodownloader.h"
#include "base/rss/rss_session.h"
#include "base/scanfoldersmodel.h"
#include "base/search/searchpluginmanager.h"
#include "base/settingsstorage.h"
#include "base/torrentfileswatcher.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
@@ -625,7 +625,7 @@ int Application::exec(const QStringList &params)
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
Net::GeoIPManager::initInstance();
ScanFoldersModel::initInstance();
TorrentFilesWatcher::initInstance();
#ifndef DISABLE_WEBUI
m_webui = new WebUI;
@@ -642,7 +642,7 @@ int Application::exec(const QStringList &params)
catch (const RuntimeError &err)
{
#ifdef DISABLE_GUI
fprintf(stderr, "%s", err.what());
fprintf(stderr, "%s", qPrintable(err.message()));
#else
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
@@ -821,7 +821,7 @@ void Application::cleanup()
delete RSS::AutoDownloader::instance();
delete RSS::Session::instance();
ScanFoldersModel::freeInstance();
TorrentFilesWatcher::freeInstance();
BitTorrent::Session::freeInstance();
Net::GeoIPManager::freeInstance();
Net::DownloadManager::freeInstance();

View File

@@ -498,17 +498,6 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
return result;
}
CommandLineParameterError::CommandLineParameterError(const QString &messageForUser)
: std::runtime_error(messageForUser.toLocal8Bit().data())
, m_messageForUser(messageForUser)
{
}
const QString &CommandLineParameterError::messageForUser() const
{
return m_messageForUser;
}
QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN, int wrapAtColumn = WRAP_AT_COLUMN)
{
QStringList words = text.split(' ');

View File

@@ -31,11 +31,12 @@
#pragma once
#include <optional>
#include <stdexcept>
#include <QString>
#include <QStringList>
#include "base/exceptions.h"
class QProcessEnvironment;
struct QBtCommandLineParameters
@@ -67,14 +68,10 @@ struct QBtCommandLineParameters
QStringList paramList() const;
};
class CommandLineParameterError : public std::runtime_error
class CommandLineParameterError : public RuntimeError
{
public:
explicit CommandLineParameterError(const QString &messageForUser);
const QString &messageForUser() const;
private:
const QString m_messageForUser;
using RuntimeError::RuntimeError;
};
QBtCommandLineParameters parseCommandLine(const QStringList &args);

View File

@@ -311,7 +311,7 @@ int main(int argc, char *argv[])
}
catch (const CommandLineParameterError &er)
{
displayBadArgMessage(er.messageForUser());
displayBadArgMessage(er.message());
return EXIT_FAILURE;
}
}

View File

@@ -95,7 +95,7 @@ namespace QtLP_Private
const char* QtLocalPeer::ack = "ack";
QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
: QObject(parent)
, id(appId)
{
@@ -133,6 +133,15 @@ QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
lockFile.open(QIODevice::ReadWrite);
}
QtLocalPeer::~QtLocalPeer()
{
if (!isClient())
{
lockFile.unlock();
lockFile.remove();
}
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())

View File

@@ -78,6 +78,7 @@ class QtLocalPeer : public QObject
public:
QtLocalPeer(QObject *parent = nullptr, const QString &appId = QString());
~QtLocalPeer() override;
bool isClient();
bool sendMessage(const QString &message, int timeout);

View File

@@ -34,7 +34,6 @@ add_library(qbt_base STATIC
bittorrent/trackerentry.h
digest32.h
exceptions.h
filesystemwatcher.h
global.h
http/connection.h
http/httperror.h
@@ -67,12 +66,12 @@ add_library(qbt_base STATIC
rss/rss_item.h
rss/rss_parser.h
rss/rss_session.h
scanfoldersmodel.h
search/searchdownloadhandler.h
search/searchhandler.h
search/searchpluginmanager.h
settingsstorage.h
torrentfileguard.h
torrentfileswatcher.h
torrentfilter.h
types.h
unicodestrings.h
@@ -115,7 +114,6 @@ add_library(qbt_base STATIC
bittorrent/tracker.cpp
bittorrent/trackerentry.cpp
exceptions.cpp
filesystemwatcher.cpp
http/connection.cpp
http/httperror.cpp
http/requestparser.cpp
@@ -144,12 +142,12 @@ add_library(qbt_base STATIC
rss/rss_item.cpp
rss/rss_parser.cpp
rss/rss_session.cpp
scanfoldersmodel.cpp
search/searchdownloadhandler.cpp
search/searchhandler.cpp
search/searchpluginmanager.cpp
settingsstorage.cpp
torrentfileguard.cpp
torrentfileswatcher.cpp
torrentfilter.cpp
utils/bytearray.cpp
utils/foreignapps.cpp

View File

@@ -33,7 +33,6 @@ HEADERS += \
$$PWD/bittorrent/trackerentry.h \
$$PWD/digest32.h \
$$PWD/exceptions.h \
$$PWD/filesystemwatcher.h \
$$PWD/global.h \
$$PWD/http/connection.h \
$$PWD/http/httperror.h \
@@ -66,13 +65,13 @@ HEADERS += \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_parser.h \
$$PWD/rss/rss_session.h \
$$PWD/scanfoldersmodel.h \
$$PWD/search/searchdownloadhandler.h \
$$PWD/search/searchhandler.h \
$$PWD/search/searchpluginmanager.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfileswatcher.h \
$$PWD/torrentfilter.h \
$$PWD/types.h \
$$PWD/unicodestrings.h \
@@ -115,7 +114,6 @@ SOURCES += \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/exceptions.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/http/connection.cpp \
$$PWD/http/httperror.cpp \
$$PWD/http/requestparser.cpp \
@@ -144,12 +142,12 @@ SOURCES += \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_parser.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/search/searchdownloadhandler.cpp \
$$PWD/search/searchhandler.cpp \
$$PWD/search/searchpluginmanager.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfileswatcher.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/utils/bytearray.cpp \
$$PWD/utils/foreignapps.cpp \

View File

@@ -40,6 +40,8 @@ namespace BitTorrent
Q_DECLARE_TR_FUNCTIONS(AbstractFileStorage)
public:
virtual ~AbstractFileStorage() = default;
virtual int filesCount() const = 0;
virtual QString filePath(int index) const = 0;
virtual QString fileName(int index) const = 0;

View File

@@ -30,6 +30,7 @@
#include <optional>
#include <QMetaType>
#include <QSet>
#include <QString>
#include <QVector>
@@ -62,3 +63,5 @@ namespace BitTorrent
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
};
}
Q_DECLARE_METATYPE(BitTorrent::AddTorrentParams)

View File

@@ -34,7 +34,6 @@
#include <libtorrent/sha1_hash.hpp>
#include <QRegularExpression>
#include <QUrl>
#include "infohash.h"
@@ -59,6 +58,8 @@ namespace
using namespace BitTorrent;
const int magnetUriId = qRegisterMetaType<MagnetUri>();
MagnetUri::MagnetUri(const QString &source)
: m_valid(false)
, m_url(source)
@@ -82,13 +83,13 @@ MagnetUri::MagnetUri(const QString &source)
m_name = QString::fromStdString(m_addTorrentParams.name);
m_trackers.reserve(m_addTorrentParams.trackers.size());
m_trackers.reserve(static_cast<decltype(m_trackers)::size_type>(m_addTorrentParams.trackers.size()));
for (const std::string &tracker : m_addTorrentParams.trackers)
m_trackers.append({QString::fromStdString(tracker)});
m_urlSeeds.reserve(m_addTorrentParams.url_seeds.size());
m_urlSeeds.reserve(static_cast<decltype(m_urlSeeds)::size_type>(m_addTorrentParams.url_seeds.size()));
for (const std::string &urlSeed : m_addTorrentParams.url_seeds)
m_urlSeeds.append(QUrl(QString::fromStdString(urlSeed)));
m_urlSeeds.append(QString::fromStdString(urlSeed));
}
bool MagnetUri::isValid() const

View File

@@ -31,13 +31,12 @@
#include <libtorrent/add_torrent_params.hpp>
#include <QString>
#include <QUrl>
#include <QVector>
#include "infohash.h"
#include "trackerentry.h"
class QUrl;
namespace BitTorrent
{
class MagnetUri
@@ -64,3 +63,5 @@ namespace BitTorrent
lt::add_torrent_params m_addTorrentParams;
};
}
Q_DECLARE_METATYPE(BitTorrent::MagnetUri)

View File

@@ -338,6 +338,8 @@ namespace
#endif
}
const int addTorrentParamsId = qRegisterMetaType<AddTorrentParams>();
// Session
Session *Session::m_instance = nullptr;
@@ -357,7 +359,7 @@ Session::Session(QObject *parent)
, m_announceToAllTiers(BITTORRENT_SESSION_KEY("AnnounceToAllTiers"), true)
, m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 10)
, m_hashingThreads(BITTORRENT_SESSION_KEY("HashingThreadsCount"), 2)
, m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 40)
, m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 5000)
, m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32)
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1)
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
@@ -403,6 +405,7 @@ Session::Session(QObject *parent)
, m_IDNSupportEnabled(BITTORRENT_SESSION_KEY("IDNSupportEnabled"), false)
, m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
, m_validateHTTPSTrackerCertificate(BITTORRENT_SESSION_KEY("ValidateHTTPSTrackerCertificate"), true)
, m_SSRFMitigationEnabled(BITTORRENT_SESSION_KEY("SSRFMitigation"), true)
, m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY("BlockPeersOnPrivilegedPorts"), false)
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
, m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
@@ -855,17 +858,13 @@ bool Session::hasTag(const QString &tag) const
bool Session::addTag(const QString &tag)
{
if (!isValidTag(tag))
if (!isValidTag(tag) || hasTag(tag))
return false;
if (!hasTag(tag))
{
m_tags.insert(tag);
m_storedTags = m_tags.values();
emit tagAdded(tag);
return true;
}
return false;
m_tags.insert(tag);
m_storedTags = m_tags.values();
emit tagAdded(tag);
return true;
}
bool Session::removeTag(const QString &tag)
@@ -1404,6 +1403,8 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
settingsPack.set_bool(lt::settings_pack::validate_https_trackers, validateHTTPSTrackerCertificate());
settingsPack.set_bool(lt::settings_pack::ssrf_mitigation, isSSRFMitigationEnabled());
settingsPack.set_bool(lt::settings_pack::no_connect_privileged_ports, blockPeersOnPrivilegedPorts());
settingsPack.set_bool(lt::settings_pack::apply_ip_filter_to_trackers, isTrackerFilteringEnabled());
@@ -2081,7 +2082,6 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
LoadTorrentParams loadTorrentParams;
loadTorrentParams.name = addTorrentParams.name;
loadTorrentParams.tags = addTorrentParams.tags;
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
@@ -2093,8 +2093,10 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!isAutoTMMDisabledByDefault());
if (useAutoTMM)
loadTorrentParams.savePath = "";
else if (addTorrentParams.savePath.trimmed().isEmpty())
else if (addTorrentParams.savePath.isEmpty())
loadTorrentParams.savePath = defaultSavePath();
else if (QDir(addTorrentParams.savePath).isRelative())
loadTorrentParams.savePath = QDir(defaultSavePath()).absoluteFilePath(addTorrentParams.savePath);
else
loadTorrentParams.savePath = normalizePath(addTorrentParams.savePath);
@@ -2104,6 +2106,12 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
else
loadTorrentParams.category = addTorrentParams.category;
for (const QString &tag : addTorrentParams.tags)
{
if (hasTag(tag) || addTag(tag))
loadTorrentParams.tags.insert(tag);
}
return loadTorrentParams;
}
@@ -3731,6 +3739,19 @@ void Session::setValidateHTTPSTrackerCertificate(const bool enabled)
configureDeferred();
}
bool Session::isSSRFMitigationEnabled() const
{
return m_SSRFMitigationEnabled;
}
void Session::setSSRFMitigationEnabled(const bool enabled)
{
if (enabled == m_SSRFMitigationEnabled) return;
m_SSRFMitigationEnabled = enabled;
configureDeferred();
}
bool Session::blockPeersOnPrivilegedPorts() const
{
return m_blockPeersOnPrivilegedPorts;
@@ -4736,8 +4757,9 @@ void Session::handleFileErrorAlert(const lt::file_error_alert *p)
if (!torrent)
return;
const TorrentID id = torrent->id();
torrent->handleAlert(p);
const TorrentID id = torrent->id();
if (!m_recentErroredTorrents.contains(id))
{
m_recentErroredTorrents.insert(id);
@@ -4978,7 +5000,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
{
QVector<Torrent *> updatedTorrents;
updatedTorrents.reserve(p->status.size());
updatedTorrents.reserve(static_cast<decltype(updatedTorrents)::size_type>(p->status.size()));
for (const lt::torrent_status &status : p->status)
{

View File

@@ -422,6 +422,8 @@ namespace BitTorrent
void setMultiConnectionsPerIpEnabled(bool enabled);
bool validateHTTPSTrackerCertificate() const;
void setValidateHTTPSTrackerCertificate(bool enabled);
bool isSSRFMitigationEnabled() const;
void setSSRFMitigationEnabled(bool enabled);
bool blockPeersOnPrivilegedPorts() const;
void setBlockPeersOnPrivilegedPorts(bool enabled);
bool isTrackerFilteringEnabled() const;
@@ -692,6 +694,7 @@ namespace BitTorrent
CachedSettingValue<bool> m_IDNSupportEnabled;
CachedSettingValue<bool> m_multiConnectionsPerIpEnabled;
CachedSettingValue<bool> m_validateHTTPSTrackerCertificate;
CachedSettingValue<bool> m_SSRFMitigationEnabled;
CachedSettingValue<bool> m_blockPeersOnPrivilegedPorts;
CachedSettingValue<bool> m_isAddTrackersEnabled;
CachedSettingValue<QString> m_additionalTrackers;

View File

@@ -49,11 +49,21 @@ namespace BitTorrent
struct PeerAddress;
struct TrackerEntry;
enum class TorrentOperatingMode
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
// since `Q_NAMESPACE` cannot be used when the same namespace resides at different files.
// https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779
inline namespace TorrentOperatingModeNS
{
AutoManaged = 0,
Forced = 1
};
Q_NAMESPACE
enum class TorrentOperatingMode
{
AutoManaged = 0,
Forced = 1
};
Q_ENUM_NS(TorrentOperatingMode)
}
enum class TorrentState
{
@@ -84,12 +94,6 @@ namespace BitTorrent
Error
};
struct TrackerInfo
{
QString lastMessage;
int numPeers = 0;
};
uint qHash(TorrentState key, uint seed);
class Torrent : public AbstractFileStorage
@@ -211,7 +215,6 @@ namespace BitTorrent
virtual bool hasFilteredPieces() const = 0;
virtual int queuePosition() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0;
virtual QHash<QString, TrackerInfo> trackerInfos() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0;
virtual QString error() const = 0;
virtual qlonglong totalDownload() const = 0;

View File

@@ -224,9 +224,13 @@ void TorrentCreatorThread::run()
emit updateProgress(100);
emit creationSuccess(m_params.savePath, parentPath);
}
catch (const std::exception &e)
catch (const RuntimeError &err)
{
emit creationFailure(e.what());
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(err.message()));
}
catch (const std::exception &err)
{
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(QString::fromLocal8Bit(err.what())));
}
}

View File

@@ -96,9 +96,11 @@ namespace
}
#if (LIBTORRENT_VERSION_NUM >= 20000)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry, const lt::info_hash_t &hashes)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
#else
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
#endif
{
TrackerEntry trackerEntry {QString::fromStdString(nativeEntry.url), nativeEntry.tier};
@@ -106,9 +108,11 @@ namespace
int numUpdating = 0;
int numWorking = 0;
int numNotWorking = 0;
QString firstTrackerMessage;
QString firstErrorMessage;
#if (LIBTORRENT_VERSION_NUM >= 20000)
const int numEndpoints = nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1);
trackerEntry.endpoints.reserve(numEndpoints);
const size_t numEndpoints = nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1);
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
@@ -119,9 +123,11 @@ namespace
TrackerEntry::EndpointStats trackerEndpoint;
trackerEndpoint.protocolVersion = (protocolVersion == lt::protocol_version::V1) ? 1 : 2;
trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
trackerEndpoint.numSeeds = infoHash.scrape_complete;
trackerEndpoint.numLeeches = infoHash.scrape_incomplete;
trackerEndpoint.numDownloaded = infoHash.scrape_downloaded;
if (infoHash.updating)
{
trackerEndpoint.status = TrackerEntry::Updating;
@@ -141,11 +147,21 @@ namespace
{
trackerEndpoint.status = TrackerEntry::NotContacted;
}
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, infoHash.scrape_complete);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, infoHash.scrape_incomplete);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, infoHash.scrape_downloaded);
const QString trackerMessage = QString::fromStdString(infoHash.message);
const QString errorMessage = QString::fromLocal8Bit(infoHash.last_error.message().c_str());
trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, trackerEndpoint.numDownloaded);
if (firstTrackerMessage.isEmpty())
firstTrackerMessage = trackerMessage;
if (firstErrorMessage.isEmpty())
firstErrorMessage = errorMessage;
}
}
}
@@ -155,9 +171,11 @@ namespace
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
TrackerEntry::EndpointStats trackerEndpoint;
trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
trackerEndpoint.numSeeds = endpoint.scrape_complete;
trackerEndpoint.numLeeches = endpoint.scrape_incomplete;
trackerEndpoint.numDownloaded = endpoint.scrape_downloaded;
if (endpoint.updating)
{
trackerEndpoint.status = TrackerEntry::Updating;
@@ -177,22 +195,40 @@ namespace
{
trackerEndpoint.status = TrackerEntry::NotContacted;
}
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, endpoint.scrape_complete);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, endpoint.scrape_incomplete);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, endpoint.scrape_downloaded);
const QString trackerMessage = QString::fromStdString(endpoint.message);
const QString errorMessage = QString::fromLocal8Bit(endpoint.last_error.message().c_str());
trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, trackerEndpoint.numDownloaded);
if (firstTrackerMessage.isEmpty())
firstTrackerMessage = trackerMessage;
if (firstErrorMessage.isEmpty())
firstErrorMessage = errorMessage;
}
#endif
if (numEndpoints > 0)
{
if (numUpdating > 0)
{
trackerEntry.status = TrackerEntry::Updating;
}
else if (numWorking > 0)
{
trackerEntry.status = TrackerEntry::Working;
trackerEntry.message = firstTrackerMessage;
}
else if (numNotWorking == numEndpoints)
{
trackerEntry.status = TrackerEntry::NotWorking;
trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
}
}
return trackerEntry;
@@ -436,23 +472,21 @@ QVector<TrackerEntry> TorrentImpl::trackers() const
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
QVector<TrackerEntry> entries;
entries.reserve(nativeTrackers.size());
entries.reserve(static_cast<decltype(entries)::size_type>(nativeTrackers.size()));
for (const lt::announce_entry &tracker : nativeTrackers)
{
const QString trackerURL = QString::fromStdString(tracker.url);
#if (LIBTORRENT_VERSION_NUM >= 20000)
entries << fromNativeAnnouncerEntry(tracker, m_nativeHandle.info_hashes());
entries << fromNativeAnnouncerEntry(tracker, m_nativeHandle.info_hashes(), m_trackerPeerCounts[trackerURL]);
#else
entries << fromNativeAnnouncerEntry(tracker);
entries << fromNativeAnnouncerEntry(tracker, m_trackerPeerCounts[trackerURL]);
#endif
}
return entries;
}
QHash<QString, TrackerInfo> TorrentImpl::trackerInfos() const
{
return m_trackerInfos;
}
void TorrentImpl::addTrackers(const QVector<TrackerEntry> &trackers)
{
QSet<TrackerEntry> currentTrackers;
@@ -525,10 +559,10 @@ QVector<QUrl> TorrentImpl::urlSeeds() const
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
QVector<QUrl> urlSeeds;
urlSeeds.reserve(currentSeeds.size());
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
for (const std::string &urlSeed : currentSeeds)
urlSeeds.append(QUrl(urlSeed.c_str()));
urlSeeds.append(QString::fromStdString(urlSeed));
return urlSeeds;
}
@@ -970,7 +1004,13 @@ QString TorrentImpl::error() const
return QString::fromStdString(m_nativeStatus.errc.message());
if (m_nativeStatus.flags & lt::torrent_flags::upload_mode)
return tr("There's not enough space on disk. Torrent is currently in \"upload only\" mode.");
{
const QString writeErrorStr = tr("Couldn't write to file.");
const QString uploadModeStr = tr("Torrent is currently in \"upload only\" mode.");
const QString errorMessage = QString::fromLocal8Bit(m_lastFileError.error.message().c_str());
return writeErrorStr + QLatin1Char(' ') + errorMessage + QLatin1String(". ") + uploadModeStr;
}
return {};
}
@@ -1182,9 +1222,11 @@ QVector<PeerInfo> TorrentImpl::peers() const
m_nativeHandle.get_peer_info(nativePeers);
QVector<PeerInfo> peers;
peers.reserve(nativePeers.size());
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
for (const lt::peer_info &peer : nativePeers)
peers << PeerInfo(this, peer);
return peers;
}
@@ -1621,10 +1663,8 @@ void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob)
void TorrentImpl::handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
{
const QString trackerUrl(p->tracker_url());
qDebug("Received a tracker reply from %s (Num_peers = %d)", qUtf8Printable(trackerUrl), p->num_peers);
// Connection was successful now. Remove possible old errors
m_trackerInfos[trackerUrl] = {{}, p->num_peers};
const QString trackerUrl = p->tracker_url();
m_trackerPeerCounts[trackerUrl][p->local_endpoint] = p->num_peers;
m_session->handleTorrentTrackerReply(this, trackerUrl);
}
@@ -1632,20 +1672,12 @@ void TorrentImpl::handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
void TorrentImpl::handleTrackerWarningAlert(const lt::tracker_warning_alert *p)
{
const QString trackerUrl = p->tracker_url();
const QString message = p->warning_message();
// Connection was successful now but there is a warning message
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
m_session->handleTorrentTrackerWarning(this, trackerUrl);
}
void TorrentImpl::handleTrackerErrorAlert(const lt::tracker_error_alert *p)
{
const QString trackerUrl = p->tracker_url();
const QString message = p->error_message();
m_trackerInfos[trackerUrl].lastMessage = message;
// Starting with libtorrent 1.2.x each tracker has multiple local endpoints from which
// an announce is attempted. Some endpoints might succeed while others might fail.
@@ -1887,6 +1919,11 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
}
}
void TorrentImpl::handleFileErrorAlert(const lt::file_error_alert *p)
{
m_lastFileError = {p->error, p->op};
}
void TorrentImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
{
Q_UNUSED(p);
@@ -1933,6 +1970,9 @@ void TorrentImpl::handleAlert(const lt::alert *a)
case lt::file_completed_alert::alert_type:
handleFileCompletedAlert(static_cast<const lt::file_completed_alert*>(a));
break;
case lt::file_error_alert::alert_type:
handleFileErrorAlert(static_cast<const lt::file_error_alert*>(a));
break;
case lt::torrent_finished_alert::alert_type:
handleTorrentFinishedAlert(static_cast<const lt::torrent_finished_alert*>(a));
break;

View File

@@ -33,11 +33,13 @@
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
#include <QDateTime>
#include <QHash>
#include <QMap>
#include <QObject>
#include <QQueue>
#include <QSet>
@@ -86,6 +88,12 @@ namespace BitTorrent
HandleMetadata
};
struct FileErrorInfo
{
lt::error_code error;
lt::operation_t operation;
};
class TorrentImpl final : public QObject, public Torrent
{
Q_DISABLE_COPY(TorrentImpl)
@@ -164,7 +172,6 @@ namespace BitTorrent
bool hasFilteredPieces() const override;
int queuePosition() const override;
QVector<TrackerEntry> trackers() const override;
QHash<QString, TrackerInfo> trackerInfos() const override;
QVector<QUrl> urlSeeds() const override;
QString error() const override;
qlonglong totalDownload() const override;
@@ -264,6 +271,7 @@ namespace BitTorrent
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
void handleFileCompletedAlert(const lt::file_completed_alert *p);
void handleFileErrorAlert(const lt::file_error_alert *p);
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
@@ -314,7 +322,8 @@ namespace BitTorrent
// we will rely on this workaround to remove empty leftover folders
QHash<lt::file_index_t, QVector<QString>> m_oldPath;
QHash<QString, TrackerInfo> m_trackerInfos;
FileErrorInfo m_lastFileError;
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;
// Persistent data
QString m_name;

View File

@@ -75,6 +75,8 @@ namespace
}
}
const int torrentInfoId = qRegisterMetaType<TorrentInfo>();
TorrentInfo::TorrentInfo(std::shared_ptr<const lt::torrent_info> nativeInfo)
{
m_nativeInfo = std::const_pointer_cast<lt::torrent_info>(nativeInfo);
@@ -87,7 +89,10 @@ TorrentInfo::TorrentInfo(const TorrentInfo &other)
TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
{
m_nativeInfo = other.m_nativeInfo;
if (this != &other)
{
m_nativeInfo = other.m_nativeInfo;
}
return *this;
}
@@ -304,10 +309,11 @@ QVector<TrackerEntry> TorrentInfo::trackers() const
const std::vector<lt::announce_entry> trackers = m_nativeInfo->trackers();
QVector<TrackerEntry> ret;
ret.reserve(trackers.size());
ret.reserve(static_cast<decltype(ret)::size_type>(trackers.size()));
for (const lt::announce_entry &tracker : trackers)
ret.append({QString::fromStdString(tracker.url)});
return ret;
}
@@ -318,7 +324,7 @@ QVector<QUrl> TorrentInfo::urlSeeds() const
const std::vector<lt::web_seed_entry> &nativeWebSeeds = m_nativeInfo->web_seeds();
QVector<QUrl> urlSeeds;
urlSeeds.reserve(nativeWebSeeds.size());
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(nativeWebSeeds.size()));
for (const lt::web_seed_entry &webSeed : nativeWebSeeds)
{
@@ -358,11 +364,10 @@ QVector<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
return {};
const std::vector<lt::file_slice> files(
nativeInfo()->map_block(lt::piece_index_t {pieceIndex}, 0
, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex})));
const std::vector<lt::file_slice> files = nativeInfo()->map_block(
lt::piece_index_t {pieceIndex}, 0, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex}));
QVector<int> res;
res.reserve(int(files.size()));
res.reserve(static_cast<decltype(res)::size_type>(files.size()));
std::transform(files.begin(), files.end(), std::back_inserter(res),
[](const lt::file_slice &s) { return static_cast<int>(s.file_index); });

View File

@@ -110,3 +110,5 @@ namespace BitTorrent
std::shared_ptr<lt::torrent_info> m_nativeInfo;
};
}
Q_DECLARE_METATYPE(BitTorrent::TorrentInfo)

View File

@@ -265,7 +265,7 @@ Http::Response Tracker::processRequest(const Http::Request &request, const Http:
const lt::entry::dictionary_type bencodedEntry =
{
{ANNOUNCE_RESPONSE_FAILURE_REASON, {error.what()}}
{ANNOUNCE_RESPONSE_FAILURE_REASON, {error.message().toStdString()}}
};
QByteArray reply;
lt::bencode(std::back_inserter(reply), bencodedEntry);

View File

@@ -49,9 +49,11 @@ namespace BitTorrent
int protocolVersion = 1;
Status status = NotContacted;
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QString message;
};
QString url;
@@ -61,9 +63,11 @@ namespace BitTorrent
// Deprecated fields
Status status = NotContacted;
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QString message;
};
bool operator==(const TrackerEntry &left, const TrackerEntry &right);

View File

@@ -28,12 +28,12 @@
#include "exceptions.h"
RuntimeError::RuntimeError(const QString &message)
: std::runtime_error {message.toUtf8().data()}
Exception::Exception(const QString &message) noexcept
: m_message {message}
{
}
QString RuntimeError::message() const
QString Exception::message() const noexcept
{
return what();
return m_message;
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018, 2021 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -28,12 +28,26 @@
#pragma once
#include <stdexcept>
#include <QString>
class RuntimeError : public std::runtime_error
class Exception
{
public:
explicit RuntimeError(const QString &message = {});
QString message() const;
explicit Exception(const QString &message = {}) noexcept;
[[nodiscard]] QString message() const noexcept;
private:
QString m_message;
};
class RuntimeError : public Exception
{
public:
using Exception::Exception;
};
class InvalidArgument : public Exception
{
public:
using Exception::Exception;
};

View File

@@ -1,187 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018
*
* 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 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), 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.
*/
#include "filesystemwatcher.h"
#include <QtGlobal>
#include <chrono>
#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <cstring>
#include <sys/mount.h>
#include <sys/param.h>
#endif
#include "base/algorithm.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/utils/fs.h"
using namespace std::chrono_literals;
namespace
{
const std::chrono::duration WATCH_INTERVAL = 10s;
const int MAX_PARTIAL_RETRIES = 5;
}
FileSystemWatcher::FileSystemWatcher(QObject *parent)
: QFileSystemWatcher(parent)
{
connect(this, &QFileSystemWatcher::directoryChanged, this, &FileSystemWatcher::scanLocalFolder);
m_partialTorrentTimer.setSingleShot(true);
connect(&m_partialTorrentTimer, &QTimer::timeout, this, &FileSystemWatcher::processPartialTorrents);
connect(&m_watchTimer, &QTimer::timeout, this, &FileSystemWatcher::scanNetworkFolders);
}
QStringList FileSystemWatcher::directories() const
{
QStringList dirs = QFileSystemWatcher::directories();
for (const QDir &dir : asConst(m_watchedFolders))
dirs << dir.canonicalPath();
return dirs;
}
void FileSystemWatcher::addPath(const QString &path)
{
if (path.isEmpty()) return;
#if !defined Q_OS_HAIKU
const QDir dir(path);
if (!dir.exists()) return;
// Check if the path points to a network file system or not
if (Utils::Fs::isNetworkFileSystem(path))
{
// Network mode
LogMsg(tr("Watching remote folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
m_watchedFolders << dir;
m_watchTimer.start(WATCH_INTERVAL);
return;
}
#endif
// Normal mode
LogMsg(tr("Watching local folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
QFileSystemWatcher::addPath(path);
scanLocalFolder(path);
}
void FileSystemWatcher::removePath(const QString &path)
{
if (m_watchedFolders.removeOne(path))
{
if (m_watchedFolders.isEmpty())
m_watchTimer.stop();
return;
}
// Normal mode
QFileSystemWatcher::removePath(path);
}
void FileSystemWatcher::scanLocalFolder(const QString &path)
{
QTimer::singleShot(2000, this, [this, path]() { processTorrentsInDir(path); });
}
void FileSystemWatcher::scanNetworkFolders()
{
for (const QDir &dir : asConst(m_watchedFolders))
processTorrentsInDir(dir);
}
void FileSystemWatcher::processPartialTorrents()
{
QStringList noLongerPartial;
// Check which torrents are still partial
Algorithm::removeIf(m_partialTorrents, [&noLongerPartial](const QString &torrentPath, int &value)
{
if (!QFile::exists(torrentPath))
return true;
if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid())
{
noLongerPartial << torrentPath;
return true;
}
if (value >= MAX_PARTIAL_RETRIES)
{
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
return true;
}
++value;
return false;
});
// Stop the partial timer if necessary
if (m_partialTorrents.empty())
{
m_partialTorrentTimer.stop();
qDebug("No longer any partial torrent.");
}
else
{
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
m_partialTorrentTimer.start(WATCH_INTERVAL);
}
// Notify of new torrents
if (!noLongerPartial.isEmpty())
emit torrentsAdded(noLongerPartial);
}
void FileSystemWatcher::processTorrentsInDir(const QDir &dir)
{
QStringList torrents;
const QStringList files = dir.entryList({"*.torrent", "*.magnet"}, QDir::Files);
for (const QString &file : files)
{
const QString fileAbsPath = dir.absoluteFilePath(file);
if (file.endsWith(".magnet", Qt::CaseInsensitive))
torrents << fileAbsPath;
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid())
torrents << fileAbsPath;
else if (!m_partialTorrents.contains(fileAbsPath))
m_partialTorrents[fileAbsPath] = 0;
}
if (!torrents.empty())
emit torrentsAdded(torrents);
if (!m_partialTorrents.empty() && !m_partialTorrentTimer.isActive())
m_partialTorrentTimer.start(WATCH_INTERVAL);
}

View File

@@ -39,7 +39,7 @@ namespace
QVector<T> loadFromBuffer(const boost::circular_buffer_space_optimized<T> &src, const int offset = 0)
{
QVector<T> ret;
ret.reserve(src.size() - offset);
ret.reserve(static_cast<typename decltype(ret)::size_type>(src.size()) - offset);
std::copy((src.begin() + offset), src.end(), std::back_inserter(ret));
return ret;
}
@@ -95,7 +95,7 @@ QVector<Log::Msg> Logger::getMessages(const int lastKnownId) const
const QReadLocker locker(&m_lock);
const int diff = m_msgCounter - lastKnownId - 1;
const int size = m_messages.size();
const int size = static_cast<int>(m_messages.size());
if ((lastKnownId == -1) || (diff >= size))
return loadFromBuffer(m_messages);
@@ -111,7 +111,7 @@ QVector<Log::Peer> Logger::getPeers(const int lastKnownId) const
const QReadLocker locker(&m_lock);
const int diff = m_peerCounter - lastKnownId - 1;
const int size = m_peers.size();
const int size = static_cast<int>(m_peers.size());
if ((lastKnownId == -1) || (diff >= size))
return loadFromBuffer(m_peers);

View File

@@ -340,17 +340,6 @@ void Preferences::setLastLocationPath(const QString &path)
setValue("Preferences/Downloads/LastLocationPath", Utils::Fs::toUniformPath(path));
}
QVariantHash Preferences::getScanDirs() const
{
return value("Preferences/Downloads/ScanDirsV2").toHash();
}
// This must be called somewhere with data from the model
void Preferences::setScanDirs(const QVariantHash &dirs)
{
setValue("Preferences/Downloads/ScanDirsV2", dirs);
}
QString Preferences::getScanDirsLastPath() const
{
return Utils::Fs::toUniformPath(value("Preferences/Downloads/ScanDirsLastPath").toString());

View File

@@ -132,8 +132,6 @@ public:
// Downloads
QString lastLocationPath() const;
void setLastLocationPath(const QString &path);
QVariantHash getScanDirs() const;
void setScanDirs(const QVariantHash &dirs);
QString getScanDirsLastPath() const;
void setScanDirsLastPath(const QString &path);
bool isMailNotificationEnabled() const;

View File

@@ -114,7 +114,7 @@ AutoDownloader::AutoDownloader()
m_fileStorage = new AsyncFileStorage(
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Config) + ConfFolderName));
if (!m_fileStorage)
throw std::runtime_error("Directory for RSS AutoDownloader data is unavailable.");
throw RuntimeError(tr("Directory for RSS AutoDownloader data is unavailable."));
m_fileStorage->moveToThread(m_ioThread);
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
@@ -527,13 +527,3 @@ void AutoDownloader::timerEvent(QTimerEvent *event)
Q_UNUSED(event);
store();
}
ParsingError::ParsingError(const QString &message)
: std::runtime_error(message.toUtf8().data())
{
}
QString ParsingError::message() const
{
return what();
}

View File

@@ -28,8 +28,6 @@
#pragma once
#include <stdexcept>
#include <QBasicTimer>
#include <QHash>
#include <QList>
@@ -38,6 +36,8 @@
#include <QRegularExpression>
#include <QSharedPointer>
#include "base/exceptions.h"
class QThread;
class QTimer;
@@ -53,11 +53,10 @@ namespace RSS
class AutoDownloadRule;
class ParsingError : public std::runtime_error
class ParsingError : public RuntimeError
{
public:
explicit ParsingError(const QString &message);
QString message() const;
using RuntimeError::RuntimeError;
};
class AutoDownloader final : public QObject

View File

@@ -442,7 +442,10 @@ bool AutoDownloadRule::accepts(const QVariantHash &articleData)
AutoDownloadRule &AutoDownloadRule::operator=(const AutoDownloadRule &other)
{
m_dataPtr = other.m_dataPtr;
if (this != &other)
{
m_dataPtr = other.m_dataPtr;
}
return *this;
}

View File

@@ -313,7 +313,7 @@ void Feed::loadArticles(const QByteArray &data)
if (!addArticle(article))
delete article;
}
catch (const std::runtime_error&) {}
catch (const RuntimeError &) {}
}
}
@@ -335,7 +335,7 @@ void Feed::loadArticlesLegacy()
if (!addArticle(article))
delete article;
}
catch (const std::runtime_error&) {}
catch (const RuntimeError &) {}
}
}

View File

@@ -1,424 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez <chris@qbittorrent.org>
*
* 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 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), 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.
*/
#include "scanfoldersmodel.h"
#include <QDir>
#include <QFileInfo>
#include <QStringList>
#include <QTextStream>
#include "bittorrent/session.h"
#include "filesystemwatcher.h"
#include "global.h"
#include "preferences.h"
#include "utils/fs.h"
struct ScanFoldersModel::PathData
{
PathData(const QString &watchPath, const PathType &type, const QString &downloadPath)
: watchPath(watchPath)
, downloadType(type)
, downloadPath(downloadPath)
{
if (this->downloadPath.isEmpty() && downloadType == CUSTOM_LOCATION)
downloadType = DEFAULT_LOCATION;
}
QString watchPath;
PathType downloadType;
QString downloadPath; // valid for CUSTOM_LOCATION
};
ScanFoldersModel *ScanFoldersModel::m_instance = nullptr;
void ScanFoldersModel::initInstance()
{
if (!m_instance)
m_instance = new ScanFoldersModel;
}
void ScanFoldersModel::freeInstance()
{
delete m_instance;
m_instance = nullptr;
}
ScanFoldersModel *ScanFoldersModel::instance()
{
return m_instance;
}
ScanFoldersModel::ScanFoldersModel(QObject *parent)
: QAbstractListModel(parent)
, m_fsWatcher(nullptr)
{
configure();
connect(Preferences::instance(), &Preferences::changed, this, &ScanFoldersModel::configure);
}
ScanFoldersModel::~ScanFoldersModel()
{
qDeleteAll(m_pathList);
}
int ScanFoldersModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_pathList.count();
}
int ScanFoldersModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return NB_COLUMNS;
}
QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || (index.row() >= rowCount()))
return {};
const PathData *pathData = m_pathList.at(index.row());
QVariant value;
switch (index.column())
{
case WATCH:
if (role == Qt::DisplayRole)
value = Utils::Fs::toNativePath(pathData->watchPath);
break;
case DOWNLOAD:
if (role == Qt::UserRole)
{
value = pathData->downloadType;
}
else if (role == Qt::DisplayRole)
{
switch (pathData->downloadType)
{
case DOWNLOAD_IN_WATCH_FOLDER:
case DEFAULT_LOCATION:
value = pathTypeDisplayName(pathData->downloadType);
break;
case CUSTOM_LOCATION:
value = pathData->downloadPath;
break;
}
}
break;
}
return value;
}
QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole) || (section < 0) || (section >= columnCount()))
return {};
QVariant title;
switch (section)
{
case WATCH:
title = tr("Monitored Folder");
break;
case DOWNLOAD:
title = tr("Override Save Location");
break;
}
return title;
}
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const
{
if (!index.isValid() || (index.row() >= rowCount()))
return QAbstractListModel::flags(index);
Qt::ItemFlags flags;
switch (index.column())
{
case WATCH:
flags = QAbstractListModel::flags(index);
break;
case DOWNLOAD:
flags = QAbstractListModel::flags(index) | Qt::ItemIsEditable;
break;
}
return flags;
}
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || (index.row() >= rowCount()) || (index.column() >= columnCount())
|| (index.column() != DOWNLOAD))
return false;
if (role == Qt::UserRole)
{
const auto type = static_cast<PathType>(value.toInt());
if (type == CUSTOM_LOCATION)
return false;
m_pathList[index.row()]->downloadType = type;
m_pathList[index.row()]->downloadPath.clear();
emit dataChanged(index, index);
}
else if (role == Qt::DisplayRole)
{
const QString path = value.toString();
if (path.isEmpty()) // means we didn't pass CUSTOM_LOCATION type
return false;
m_pathList[index.row()]->downloadType = CUSTOM_LOCATION;
m_pathList[index.row()]->downloadPath = Utils::Fs::toNativePath(path);
emit dataChanged(index, index);
}
else
{
return false;
}
return true;
}
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath, bool addToFSWatcher)
{
const QDir watchDir(watchPath);
if (!watchDir.exists()) return DoesNotExist;
if (!watchDir.isReadable()) return CannotRead;
const QString canonicalWatchPath = watchDir.canonicalPath();
if (findPathData(canonicalWatchPath) != -1) return AlreadyInList;
const QDir downloadDir(downloadPath);
const QString canonicalDownloadPath = downloadDir.canonicalPath();
if (!m_fsWatcher)
{
m_fsWatcher = new FileSystemWatcher(this);
connect(m_fsWatcher, &FileSystemWatcher::torrentsAdded, this, &ScanFoldersModel::addTorrentsToSession);
}
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_pathList << new PathData(Utils::Fs::toNativePath(canonicalWatchPath), downloadType, Utils::Fs::toNativePath(canonicalDownloadPath));
endInsertRows();
// Start scanning
if (addToFSWatcher)
m_fsWatcher->addPath(canonicalWatchPath);
return Ok;
}
ScanFoldersModel::PathStatus ScanFoldersModel::updatePath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath)
{
const QDir watchDir(watchPath);
const QString canonicalWatchPath = watchDir.canonicalPath();
const int row = findPathData(canonicalWatchPath);
if (row == -1) return DoesNotExist;
const QDir downloadDir(downloadPath);
const QString canonicalDownloadPath = downloadDir.canonicalPath();
m_pathList.at(row)->downloadType = downloadType;
m_pathList.at(row)->downloadPath = Utils::Fs::toNativePath(canonicalDownloadPath);
return Ok;
}
void ScanFoldersModel::addToFSWatcher(const QStringList &watchPaths)
{
if (!m_fsWatcher)
return; // addPath() wasn't called before this
for (const QString &path : watchPaths)
{
const QDir watchDir(path);
const QString canonicalWatchPath = watchDir.canonicalPath();
m_fsWatcher->addPath(canonicalWatchPath);
}
}
void ScanFoldersModel::removePath(const int row, const bool removeFromFSWatcher)
{
Q_ASSERT((row >= 0) && (row < rowCount()));
beginRemoveRows(QModelIndex(), row, row);
if (removeFromFSWatcher)
m_fsWatcher->removePath(m_pathList.at(row)->watchPath);
delete m_pathList.takeAt(row);
endRemoveRows();
}
bool ScanFoldersModel::removePath(const QString &path, const bool removeFromFSWatcher)
{
const int row = findPathData(path);
if (row == -1) return false;
removePath(row, removeFromFSWatcher);
return true;
}
void ScanFoldersModel::removeFromFSWatcher(const QStringList &watchPaths)
{
for (const QString &path : watchPaths)
m_fsWatcher->removePath(path);
}
bool ScanFoldersModel::downloadInWatchFolder(const QString &filePath) const
{
const int row = findPathData(QFileInfo(filePath).dir().path());
Q_ASSERT(row != -1);
const PathData *data = m_pathList.at(row);
return (data->downloadType == DOWNLOAD_IN_WATCH_FOLDER);
}
bool ScanFoldersModel::downloadInDefaultFolder(const QString &filePath) const
{
const int row = findPathData(QFileInfo(filePath).dir().path());
Q_ASSERT(row != -1);
const PathData *data = m_pathList.at(row);
return (data->downloadType == DEFAULT_LOCATION);
}
QString ScanFoldersModel::downloadPathTorrentFolder(const QString &filePath) const
{
const int row = findPathData(QFileInfo(filePath).dir().path());
Q_ASSERT(row != -1);
const PathData *data = m_pathList.at(row);
if (data->downloadType == CUSTOM_LOCATION)
return data->downloadPath;
return {};
}
int ScanFoldersModel::findPathData(const QString &path) const
{
for (int i = 0; i < m_pathList.count(); ++i)
if (m_pathList.at(i)->watchPath == Utils::Fs::toNativePath(path))
return i;
return -1;
}
void ScanFoldersModel::makePersistent()
{
QVariantHash dirs;
for (const PathData *pathData : asConst(m_pathList))
{
if (pathData->downloadType == CUSTOM_LOCATION)
dirs.insert(Utils::Fs::toUniformPath(pathData->watchPath), Utils::Fs::toUniformPath(pathData->downloadPath));
else
dirs.insert(Utils::Fs::toUniformPath(pathData->watchPath), pathData->downloadType);
}
Preferences::instance()->setScanDirs(dirs);
}
void ScanFoldersModel::configure()
{
const QVariantHash dirs = Preferences::instance()->getScanDirs();
for (auto i = dirs.cbegin(); i != dirs.cend(); ++i)
{
if (i.value().type() == QVariant::Int)
addPath(i.key(), static_cast<PathType>(i.value().toInt()), QString());
else
addPath(i.key(), CUSTOM_LOCATION, i.value().toString());
}
}
void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
{
for (const QString &file : pathList)
{
qDebug("File %s added", qUtf8Printable(file));
BitTorrent::AddTorrentParams params;
if (downloadInWatchFolder(file))
{
params.savePath = QFileInfo(file).dir().path();
params.useAutoTMM = false;
}
else if (!downloadInDefaultFolder(file))
{
params.savePath = downloadPathTorrentFolder(file);
params.useAutoTMM = false;
}
if (file.endsWith(".magnet", Qt::CaseInsensitive))
{
QFile f(file);
if (f.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream str(&f);
while (!str.atEnd())
BitTorrent::Session::instance()->addTorrent(str.readLine(), params);
f.close();
Utils::Fs::forceRemove(file);
}
else
{
qDebug("Failed to open magnet file: %s", qUtf8Printable(f.errorString()));
}
}
else
{
const BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::loadFromFile(file);
if (torrentInfo.isValid())
{
BitTorrent::Session::instance()->addTorrent(torrentInfo, params);
Utils::Fs::forceRemove(file);
}
else
{
qDebug("Ignoring incomplete torrent file: %s", qUtf8Printable(file));
}
}
}
}
QString ScanFoldersModel::pathTypeDisplayName(const PathType type)
{
switch (type)
{
case DOWNLOAD_IN_WATCH_FOLDER:
return tr("Monitored folder");
case DEFAULT_LOCATION:
return tr("Default save location");
case CUSTOM_LOCATION:
return tr("Browse...");
default:
qDebug("Invalid PathType: %d", type);
};
return {};
}

View File

@@ -1,111 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez <chris@qbittorrent.org>
*
* 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 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), 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.
*/
#pragma once
#include <QAbstractListModel>
#include <QList>
#include <QtContainerFwd>
class FileSystemWatcher;
class ScanFoldersModel final : public QAbstractListModel
{
Q_OBJECT
Q_DISABLE_COPY(ScanFoldersModel)
public:
enum PathStatus
{
Ok,
DoesNotExist,
CannotRead,
CannotWrite,
AlreadyInList
};
enum Column
{
WATCH,
DOWNLOAD,
NB_COLUMNS
};
enum PathType
{
DOWNLOAD_IN_WATCH_FOLDER,
DEFAULT_LOCATION,
CUSTOM_LOCATION
};
static void initInstance();
static void freeInstance();
static ScanFoldersModel *instance();
static QString pathTypeDisplayName(PathType type);
int rowCount(const QModelIndex &parent = {}) const override;
int columnCount(const QModelIndex &parent = {}) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
// TODO: removePaths(); singular version becomes private helper functions;
// also: remove functions should take modelindexes
PathStatus addPath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath, bool addToFSWatcher = true);
PathStatus updatePath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath);
// PRECONDITION: The paths must have been added with addPath() first.
void addToFSWatcher(const QStringList &watchPaths);
void removePath(int row, bool removeFromFSWatcher = true);
bool removePath(const QString &path, bool removeFromFSWatcher = true);
void removeFromFSWatcher(const QStringList &watchPaths);
void makePersistent();
public slots:
void configure();
private slots:
void addTorrentsToSession(const QStringList &pathList);
private:
explicit ScanFoldersModel(QObject *parent = nullptr);
~ScanFoldersModel();
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool downloadInWatchFolder(const QString &filePath) const;
bool downloadInDefaultFolder(const QString &filePath) const;
QString downloadPathTorrentFolder(const QString &filePath) const;
int findPathData(const QString &path) const;
static ScanFoldersModel *m_instance;
struct PathData;
QList<PathData*> m_pathList;
FileSystemWatcher *m_fsWatcher;
};

View File

@@ -0,0 +1,662 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez <chris@qbittorrent.org>
*
* 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 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), 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.
*/
#include "torrentfileswatcher.h"
#include <chrono>
#include <QtGlobal>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileSystemWatcher>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QSaveFile>
#include <QSet>
#include <QTextStream>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include "base/algorithm.h"
#include "base/bittorrent/magneturi.h"
#include "base/bittorrent/torrentcontentlayout.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/exceptions.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/profile.h"
#include "base/settingsstorage.h"
#include "base/utils/fs.h"
#include "base/utils/string.h"
using namespace std::chrono_literals;
const std::chrono::duration WATCH_INTERVAL = 10s;
const int MAX_FAILED_RETRIES = 5;
const QString CONF_FILE_NAME {QStringLiteral("watched_folders.json")};
const QString OPTION_ADDTORRENTPARAMS {QStringLiteral("add_torrent_params")};
const QString OPTION_RECURSIVE {QStringLiteral("recursive")};
const QString PARAM_CATEGORY {QStringLiteral("category")};
const QString PARAM_TAGS {QStringLiteral("tags")};
const QString PARAM_SAVEPATH {QStringLiteral("save_path")};
const QString PARAM_OPERATINGMODE {QStringLiteral("operating_mode")};
const QString PARAM_STOPPED {QStringLiteral("stopped")};
const QString PARAM_CONTENTLAYOUT {QStringLiteral("content_layout")};
const QString PARAM_AUTOTMM {QStringLiteral("use_auto_tmm")};
const QString PARAM_UPLOADLIMIT {QStringLiteral("upload_limit")};
const QString PARAM_DOWNLOADLIMIT {QStringLiteral("download_limit")};
const QString PARAM_SEEDINGTIMELIMIT {QStringLiteral("seeding_time_limit")};
const QString PARAM_RATIOLIMIT {QStringLiteral("ratio_limit")};
namespace
{
QSet<QString> parseTagSet(const QJsonArray &jsonArr)
{
QSet<QString> tags;
for (const QJsonValue &jsonVal : jsonArr)
tags.insert(jsonVal.toString());
return tags;
}
QJsonArray serializeTagSet(const QSet<QString> &tags)
{
QJsonArray arr;
for (const QString &tag : tags)
arr.append(tag);
return arr;
}
std::optional<bool> getOptionalBool(const QJsonObject &jsonObj, const QString &key)
{
const QJsonValue jsonVal = jsonObj.value(key);
if (jsonVal.isUndefined() || jsonVal.isNull())
return std::nullopt;
return jsonVal.toBool();
}
template <typename Enum>
std::optional<Enum> getOptionalEnum(const QJsonObject &jsonObj, const QString &key)
{
const QJsonValue jsonVal = jsonObj.value(key);
if (jsonVal.isUndefined() || jsonVal.isNull())
return std::nullopt;
return Utils::String::toEnum<Enum>(jsonVal.toString(), {});
}
template <typename Enum>
Enum getEnum(const QJsonObject &jsonObj, const QString &key)
{
const QJsonValue jsonVal = jsonObj.value(key);
return Utils::String::toEnum<Enum>(jsonVal.toString(), {});
}
BitTorrent::AddTorrentParams parseAddTorrentParams(const QJsonObject &jsonObj)
{
BitTorrent::AddTorrentParams params;
params.category = jsonObj.value(PARAM_CATEGORY).toString();
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
params.savePath = jsonObj.value(PARAM_SAVEPATH).toString();
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
params.contentLayout = getOptionalEnum<BitTorrent::TorrentContentLayout>(jsonObj, PARAM_CONTENTLAYOUT);
params.useAutoTMM = getOptionalBool(jsonObj, PARAM_AUTOTMM);
params.uploadLimit = jsonObj.value(PARAM_UPLOADLIMIT).toInt(-1);
params.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1);
params.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
params.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(BitTorrent::Torrent::USE_GLOBAL_RATIO);
return params;
}
QJsonObject serializeAddTorrentParams(const BitTorrent::AddTorrentParams &params)
{
QJsonObject jsonObj {
{PARAM_CATEGORY, params.category},
{PARAM_TAGS, serializeTagSet(params.tags)},
{PARAM_SAVEPATH, params.savePath},
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
{PARAM_UPLOADLIMIT, params.uploadLimit},
{PARAM_DOWNLOADLIMIT, params.downloadLimit},
{PARAM_SEEDINGTIMELIMIT, params.seedingTimeLimit},
{PARAM_RATIOLIMIT, params.ratioLimit}
};
if (params.addPaused)
jsonObj[PARAM_STOPPED] = *params.addPaused;
if (params.contentLayout)
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
if (params.useAutoTMM)
jsonObj[PARAM_AUTOTMM] = *params.useAutoTMM;
return jsonObj;
}
TorrentFilesWatcher::WatchedFolderOptions parseWatchedFolderOptions(const QJsonObject &jsonObj)
{
TorrentFilesWatcher::WatchedFolderOptions options;
options.addTorrentParams = parseAddTorrentParams(jsonObj.value(OPTION_ADDTORRENTPARAMS).toObject());
options.recursive = jsonObj.value(OPTION_RECURSIVE).toBool();
return options;
}
QJsonObject serializeWatchedFolderOptions(const TorrentFilesWatcher::WatchedFolderOptions &options)
{
return {
{OPTION_ADDTORRENTPARAMS, serializeAddTorrentParams(options.addTorrentParams)},
{OPTION_RECURSIVE, options.recursive}
};
}
}
class TorrentFilesWatcher::Worker final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Worker)
public:
Worker();
public slots:
void setWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
void removeWatchedFolder(const QString &path);
signals:
void magnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
void torrentFound(const BitTorrent::TorrentInfo &torrentInfo, const BitTorrent::AddTorrentParams &addTorrentParams);
private:
void onTimeout();
void scheduleWatchedFolderProcessing(const QString &path);
void processWatchedFolder(const QString &path);
void processFolder(const QString &path, const QString &watchedFolderPath, const TorrentFilesWatcher::WatchedFolderOptions &options);
void processFailedTorrents();
void addWatchedFolder(const QString &watchedFolderID, const TorrentFilesWatcher::WatchedFolderOptions &options);
void updateWatchedFolder(const QString &watchedFolderID, const TorrentFilesWatcher::WatchedFolderOptions &options);
QFileSystemWatcher *m_watcher = nullptr;
QTimer *m_watchTimer = nullptr;
QHash<QString, TorrentFilesWatcher::WatchedFolderOptions> m_watchedFolders;
QSet<QString> m_watchedByTimeoutFolders;
// Failed torrents
QTimer *m_retryTorrentTimer = nullptr;
QHash<QString, QHash<QString, int>> m_failedTorrents;
};
TorrentFilesWatcher *TorrentFilesWatcher::m_instance = nullptr;
void TorrentFilesWatcher::initInstance()
{
if (!m_instance)
m_instance = new TorrentFilesWatcher;
}
void TorrentFilesWatcher::freeInstance()
{
delete m_instance;
m_instance = nullptr;
}
TorrentFilesWatcher *TorrentFilesWatcher::instance()
{
return m_instance;
}
TorrentFilesWatcher::TorrentFilesWatcher(QObject *parent)
: QObject {parent}
, m_ioThread {new QThread(this)}
, m_asyncWorker {new TorrentFilesWatcher::Worker}
{
connect(m_asyncWorker, &TorrentFilesWatcher::Worker::magnetFound, this, &TorrentFilesWatcher::onMagnetFound);
connect(m_asyncWorker, &TorrentFilesWatcher::Worker::torrentFound, this, &TorrentFilesWatcher::onTorrentFound);
m_asyncWorker->moveToThread(m_ioThread);
m_ioThread->start();
load();
}
TorrentFilesWatcher::~TorrentFilesWatcher()
{
m_ioThread->quit();
m_ioThread->wait();
delete m_asyncWorker;
}
QString TorrentFilesWatcher::makeCleanPath(const QString &path)
{
if (path.isEmpty())
throw InvalidArgument(tr("Watched folder path cannot be empty."));
if (QDir::isRelativePath(path))
throw InvalidArgument(tr("Watched folder path cannot be relative."));
return QDir::cleanPath(path);
}
void TorrentFilesWatcher::load()
{
QFile confFile {QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CONF_FILE_NAME)};
if (!confFile.exists())
{
loadLegacy();
return;
}
if (!confFile.open(QFile::ReadOnly))
{
LogMsg(tr("Couldn't load Watched Folders configuration from %1. Error: %2")
.arg(confFile.fileName(), confFile.errorString()), Log::WARNING);
return;
}
QJsonParseError jsonError;
const QJsonDocument jsonDoc = QJsonDocument::fromJson(confFile.readAll(), &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
LogMsg(tr("Couldn't parse Watched Folders configuration from %1. Error: %2")
.arg(confFile.fileName(), jsonError.errorString()), Log::WARNING);
return;
}
if (!jsonDoc.isObject())
{
LogMsg(tr("Couldn't load Watched Folders configuration from %1. Invalid data format.")
.arg(confFile.fileName()), Log::WARNING);
return;
}
const QJsonObject jsonObj = jsonDoc.object();
for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it)
{
const QString &watchedFolder = it.key();
const WatchedFolderOptions options = parseWatchedFolderOptions(it.value().toObject());
try
{
doSetWatchedFolder(watchedFolder, options);
}
catch (const InvalidArgument &err)
{
LogMsg(err.message(), Log::WARNING);
}
}
}
void TorrentFilesWatcher::loadLegacy()
{
const auto dirs = SettingsStorage::instance()->loadValue<QVariantHash>("Preferences/Downloads/ScanDirsV2");
for (auto i = dirs.cbegin(); i != dirs.cend(); ++i)
{
const QString watchedFolder = i.key();
BitTorrent::AddTorrentParams params;
if (i.value().type() == QVariant::Int)
{
if (i.value().toInt() == 0)
{
params.savePath = watchedFolder;
params.useAutoTMM = false;
}
}
else
{
const QString customSavePath = i.value().toString();
params.savePath = customSavePath;
params.useAutoTMM = false;
}
try
{
doSetWatchedFolder(watchedFolder, {params, false});
}
catch (const InvalidArgument &err)
{
LogMsg(err.message(), Log::WARNING);
}
}
store();
SettingsStorage::instance()->removeValue("Preferences/Downloads/ScanDirsV2");
}
void TorrentFilesWatcher::store() const
{
QJsonObject jsonObj;
for (auto it = m_watchedFolders.cbegin(); it != m_watchedFolders.cend(); ++it)
{
const QString &watchedFolder = it.key();
const WatchedFolderOptions &options = it.value();
jsonObj[watchedFolder] = serializeWatchedFolderOptions(options);
}
const QByteArray data = QJsonDocument(jsonObj).toJson();
QSaveFile confFile {QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CONF_FILE_NAME)};
if (!confFile.open(QIODevice::WriteOnly) || (confFile.write(data) != data.size()) || !confFile.commit())
{
LogMsg(tr("Couldn't store Watched Folders configuration to %1. Error: %2")
.arg(confFile.fileName(), confFile.errorString()), Log::WARNING);
}
}
QHash<QString, TorrentFilesWatcher::WatchedFolderOptions> TorrentFilesWatcher::folders() const
{
return m_watchedFolders;
}
void TorrentFilesWatcher::setWatchedFolder(const QString &path, const WatchedFolderOptions &options)
{
doSetWatchedFolder(path, options);
store();
}
void TorrentFilesWatcher::doSetWatchedFolder(const QString &path, const WatchedFolderOptions &options)
{
const QString cleanPath = makeCleanPath(path);
m_watchedFolders[cleanPath] = options;
QMetaObject::invokeMethod(m_asyncWorker, [this, path, options]()
{
m_asyncWorker->setWatchedFolder(path, options);
});
emit watchedFolderSet(cleanPath, options);
}
void TorrentFilesWatcher::removeWatchedFolder(const QString &path)
{
const QString cleanPath = makeCleanPath(path);
if (m_watchedFolders.remove(cleanPath))
{
QMetaObject::invokeMethod(m_asyncWorker, [this, cleanPath]()
{
m_asyncWorker->removeWatchedFolder(cleanPath);
});
emit watchedFolderRemoved(cleanPath);
store();
}
}
void TorrentFilesWatcher::onMagnetFound(const BitTorrent::MagnetUri &magnetURI
, const BitTorrent::AddTorrentParams &addTorrentParams)
{
BitTorrent::Session::instance()->addTorrent(magnetURI, addTorrentParams);
}
void TorrentFilesWatcher::onTorrentFound(const BitTorrent::TorrentInfo &torrentInfo
, const BitTorrent::AddTorrentParams &addTorrentParams)
{
BitTorrent::Session::instance()->addTorrent(torrentInfo, addTorrentParams);
}
TorrentFilesWatcher::Worker::Worker()
: m_watcher {new QFileSystemWatcher(this)}
, m_watchTimer {new QTimer(this)}
, m_retryTorrentTimer {new QTimer(this)}
{
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &Worker::scheduleWatchedFolderProcessing);
connect(m_watchTimer, &QTimer::timeout, this, &Worker::onTimeout);
connect(m_retryTorrentTimer, &QTimer::timeout, this, &Worker::processFailedTorrents);
}
void TorrentFilesWatcher::Worker::onTimeout()
{
for (const QString &path : asConst(m_watchedByTimeoutFolders))
processWatchedFolder(path);
}
void TorrentFilesWatcher::Worker::setWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
{
if (m_watchedFolders.contains(path))
updateWatchedFolder(path, options);
else
addWatchedFolder(path, options);
}
void TorrentFilesWatcher::Worker::removeWatchedFolder(const QString &path)
{
m_watchedFolders.remove(path);
m_watcher->removePath(path);
m_watchedByTimeoutFolders.remove(path);
if (m_watchedByTimeoutFolders.isEmpty())
m_watchTimer->stop();
m_failedTorrents.remove(path);
if (m_failedTorrents.isEmpty())
m_retryTorrentTimer->stop();
}
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const QString &path)
{
QTimer::singleShot(2000, this, [this, path]()
{
processWatchedFolder(path);
});
}
void TorrentFilesWatcher::Worker::processWatchedFolder(const QString &path)
{
const TorrentFilesWatcher::WatchedFolderOptions options = m_watchedFolders.value(path);
processFolder(path, path, options);
if (!m_failedTorrents.empty() && !m_retryTorrentTimer->isActive())
m_retryTorrentTimer->start(WATCH_INTERVAL);
}
void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QString &watchedFolderPath
, const TorrentFilesWatcher::WatchedFolderOptions &options)
{
const QDir watchedDir {watchedFolderPath};
QDirIterator dirIter {path, {"*.torrent", "*.magnet"}, QDir::Files};
while (dirIter.hasNext())
{
const QString filePath = dirIter.next();
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
if (path != watchedFolderPath)
{
const QString subdirPath = watchedDir.relativeFilePath(path);
addTorrentParams.savePath = QDir::cleanPath(QDir(addTorrentParams.savePath).filePath(subdirPath));
}
if (filePath.endsWith(QLatin1String(".magnet"), Qt::CaseInsensitive))
{
QFile file {filePath};
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream str {&file};
while (!str.atEnd())
emit magnetFound(BitTorrent::MagnetUri(str.readLine()), addTorrentParams);
file.close();
Utils::Fs::forceRemove(filePath);
}
else
{
LogMsg(tr("Failed to open magnet file: %1").arg(file.errorString()));
}
}
else
{
const auto torrentInfo = BitTorrent::TorrentInfo::loadFromFile(filePath);
if (torrentInfo.isValid())
{
emit torrentFound(torrentInfo, addTorrentParams);
Utils::Fs::forceRemove(filePath);
}
else
{
if (!m_failedTorrents.value(path).contains(filePath))
{
m_failedTorrents[path][filePath] = 0;
}
}
}
}
if (options.recursive)
{
QDirIterator dirIter {path, (QDir::Dirs | QDir::NoDot | QDir::NoDotDot)};
while (dirIter.hasNext())
{
const QString folderPath = dirIter.next();
// Skip processing of subdirectory that is explicitly set as watched folder
if (!m_watchedFolders.contains(folderPath))
processFolder(folderPath, watchedFolderPath, options);
}
}
}
void TorrentFilesWatcher::Worker::processFailedTorrents()
{
// Check which torrents are still partial
Algorithm::removeIf(m_failedTorrents, [this](const QString &watchedFolderPath, QHash<QString, int> &partialTorrents)
{
const QDir dir {watchedFolderPath};
const TorrentFilesWatcher::WatchedFolderOptions options = m_watchedFolders.value(watchedFolderPath);
Algorithm::removeIf(partialTorrents, [this, &dir, &options](const QString &torrentPath, int &value)
{
if (!QFile::exists(torrentPath))
return true;
const auto torrentInfo = BitTorrent::TorrentInfo::loadFromFile(torrentPath);
if (torrentInfo.isValid())
{
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
const QString exactDirPath = QFileInfo(torrentPath).canonicalPath();
if (exactDirPath != dir.path())
{
const QString subdirPath = dir.relativeFilePath(exactDirPath);
addTorrentParams.savePath = QDir(addTorrentParams.savePath).filePath(subdirPath);
}
emit torrentFound(torrentInfo, addTorrentParams);
Utils::Fs::forceRemove(torrentPath);
return true;
}
if (value >= MAX_FAILED_RETRIES)
{
LogMsg(tr("Rejecting failed torrent file: %1").arg(torrentPath));
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
return true;
}
++value;
return false;
});
if (partialTorrents.isEmpty())
return true;
return false;
});
// Stop the partial timer if necessary
if (m_failedTorrents.empty())
m_retryTorrentTimer->stop();
else
m_retryTorrentTimer->start(WATCH_INTERVAL);
}
void TorrentFilesWatcher::Worker::addWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
{
#if !defined Q_OS_HAIKU
// Check if the path points to a network file system or not
if (Utils::Fs::isNetworkFileSystem(path) || options.recursive)
#else
if (options.recursive)
#endif
{
m_watchedByTimeoutFolders.insert(path);
if (!m_watchTimer->isActive())
m_watchTimer->start(WATCH_INTERVAL);
}
else
{
m_watcher->addPath(path);
scheduleWatchedFolderProcessing(path);
}
m_watchedFolders[path] = options;
LogMsg(tr("Watching folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
}
void TorrentFilesWatcher::Worker::updateWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
{
const bool recursiveModeChanged = (m_watchedFolders[path].recursive != options.recursive);
#if !defined Q_OS_HAIKU
if (recursiveModeChanged && !Utils::Fs::isNetworkFileSystem(path))
#else
if (recursiveModeChanged)
#endif
{
if (options.recursive)
{
m_watcher->removePath(path);
m_watchedByTimeoutFolders.insert(path);
if (!m_watchTimer->isActive())
m_watchTimer->start(WATCH_INTERVAL);
}
else
{
m_watchedByTimeoutFolders.remove(path);
if (m_watchedByTimeoutFolders.isEmpty())
m_watchTimer->stop();
m_watcher->addPath(path);
scheduleWatchedFolderProcessing(path);
}
}
m_watchedFolders[path] = options;
}
#include "torrentfileswatcher.moc"

View File

@@ -0,0 +1,96 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez <chris@qbittorrent.org>
*
* 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 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), 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.
*/
#pragma once
#include <QHash>
#include "base/bittorrent/addtorrentparams.h"
class QThread;
namespace BitTorrent
{
class MagnetUri;
}
/*
* Watches the configured directories for new .torrent files in order
* to add torrents to BitTorrent session. Supports Network File System
* watching (NFS, CIFS) on Linux and Mac OS.
*/
class TorrentFilesWatcher final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(TorrentFilesWatcher)
public:
struct WatchedFolderOptions
{
BitTorrent::AddTorrentParams addTorrentParams;
bool recursive = false;
};
static void initInstance();
static void freeInstance();
static TorrentFilesWatcher *instance();
static QString makeCleanPath(const QString &path);
QHash<QString, WatchedFolderOptions> folders() const;
void setWatchedFolder(const QString &path, const WatchedFolderOptions &options);
void removeWatchedFolder(const QString &path);
signals:
void watchedFolderSet(const QString &path, const WatchedFolderOptions &options);
void watchedFolderRemoved(const QString &path);
private slots:
void onMagnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
void onTorrentFound(const BitTorrent::TorrentInfo &torrentInfo, const BitTorrent::AddTorrentParams &addTorrentParams);
private:
explicit TorrentFilesWatcher(QObject *parent = nullptr);
~TorrentFilesWatcher() override;
void load();
void loadLegacy();
void store() const;
void doSetWatchedFolder(const QString &path, const WatchedFolderOptions &options);
static TorrentFilesWatcher *m_instance;
QHash<QString, WatchedFolderOptions> m_watchedFolders;
QThread *m_ioThread = nullptr;
class Worker;
Worker *m_asyncWorker = nullptr;
};

View File

@@ -76,8 +76,10 @@ const char C_LOCALE_LATGALIAN[] = "Latgalīšu volūda";
const char C_LOCALE_LATVIAN[] = "Latviešu valoda";
const char C_LOCALE_LITHUANIAN[] = "Lietuvių";
const char C_LOCALE_MALAY[] = "بهاس ملايو";
const char C_LOCALE_MONGOLIAN[] = "Монгол хэл";
const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_OCCITAN[] = "lenga d'òc";
const char C_LOCALE_PERSIAN[] = "فارسی";
const char C_LOCALE_POLISH[] = "Polski";
const char C_LOCALE_PORTUGUESE[] = "Português";
const char C_LOCALE_PORTUGUESE_BRAZIL[] = "Português brasileiro";
@@ -88,6 +90,7 @@ const char C_LOCALE_SLOVAK[] = "Slovenčina";
const char C_LOCALE_SLOVENIAN[] = "Slovenščina";
const char C_LOCALE_SPANISH[] = "Español";
const char C_LOCALE_SWEDISH[] = "Svenska";
const char C_LOCALE_THAI[] = "ไทย";
const char C_LOCALE_TURKISH[] = "Türkçe";
const char C_LOCALE_UKRAINIAN[] = "Українська";
const char C_LOCALE_UZBEK[] = "أۇزبېك‎";

View File

@@ -76,7 +76,7 @@ namespace
{
info = {exeName, versionStr.left(idx)};
}
catch (const std::runtime_error &)
catch (const RuntimeError &)
{
return false;
}

View File

@@ -48,7 +48,7 @@ QByteArray Utils::Gzip::compress(const QByteArray &data, const int level, bool *
const int BUFSIZE = 128 * 1024;
std::vector<char> tmpBuf(BUFSIZE);
z_stream strm;
z_stream strm {};
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
@@ -109,7 +109,7 @@ QByteArray Utils::Gzip::decompress(const QByteArray &data, bool *ok)
const int BUFSIZE = 1024 * 1024;
std::vector<char> tmpBuf(BUFSIZE);
z_stream strm;
z_stream strm {};
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;

View File

@@ -29,11 +29,12 @@
#pragma once
#include <array>
#include <stdexcept>
#include <QDebug>
#include <QString>
#include "base/exceptions.h"
namespace Utils
{
template <typename T, std::size_t N, std::size_t Mandatory = N>
@@ -64,7 +65,7 @@ namespace Utils
* @brief Creates version from string in format "x.y.z"
*
* @param version Version string in format "x.y.z"
* @throws std::runtime_error if parsing fails
* @throws RuntimeError if parsing fails
*/
Version(const QString &version)
: Version {version.split(QLatin1Char('.'))}
@@ -75,7 +76,7 @@ namespace Utils
* @brief Creates version from byte array in format "x.y.z"
*
* @param version Version string in format "x.y.z"
* @throws std::runtime_error if parsing fails
* @throws RuntimeError if parsing fails
*/
Version(const QByteArray &version)
: Version {version.split('.')}
@@ -150,9 +151,9 @@ namespace Utils
{
return Version(s);
}
catch (const std::runtime_error &er)
catch (const RuntimeError &er)
{
qDebug() << "Error parsing version:" << er.what();
qDebug() << "Error parsing version:" << er.message();
return defaultVersion;
}
}
@@ -165,7 +166,9 @@ namespace Utils
{
if ((static_cast<std::size_t>(versionParts.size()) > N)
|| (static_cast<std::size_t>(versionParts.size()) < Mandatory))
throw std::runtime_error("Incorrect number of version components");
{
throw RuntimeError(QLatin1String("Incorrect number of version components"));
}
bool ok = false;
ComponentsArray res {{}};
@@ -173,7 +176,7 @@ namespace Utils
{
res[i] = static_cast<T>(versionParts[static_cast<typename StringsList::size_type>(i)].toInt(&ok));
if (!ok)
throw std::runtime_error("Can not parse version component");
throw RuntimeError(QLatin1String("Can not parse version component"));
}
return res;
}

View File

@@ -30,7 +30,7 @@
#define QBT_VERSION_MAJOR 4
#define QBT_VERSION_MINOR 3
#define QBT_VERSION_BUGFIX 5
#define QBT_VERSION_BUGFIX 9
#define QBT_VERSION_BUILD 0
#define QBT_VERSION_STATUS "" // Should be empty for stable releases!

View File

@@ -46,7 +46,6 @@ add_library(qbt_gui STATIC
rss/feedlistwidget.h
rss/htmlbrowser.h
rss/rsswidget.h
scanfoldersdelegate.h
search/pluginselectdialog.h
search/pluginsourcedialog.h
search/searchjobwidget.h
@@ -78,6 +77,8 @@ add_library(qbt_gui STATIC
tristatewidget.h
uithememanager.h
utils.h
watchedfolderoptionsdialog.h
watchedfoldersmodel.h
# sources
aboutdialog.cpp
@@ -126,7 +127,6 @@ add_library(qbt_gui STATIC
rss/feedlistwidget.cpp
rss/htmlbrowser.cpp
rss/rsswidget.cpp
scanfoldersdelegate.cpp
search/pluginselectdialog.cpp
search/pluginsourcedialog.cpp
search/searchjobwidget.cpp
@@ -158,6 +158,8 @@ add_library(qbt_gui STATIC
tristatewidget.cpp
uithememanager.cpp
utils.cpp
watchedfolderoptionsdialog.cpp
watchedfoldersmodel.cpp
# forms
aboutdialog.ui
@@ -188,6 +190,7 @@ add_library(qbt_gui STATIC
torrentcreatordialog.ui
torrentoptionsdialog.ui
trackerentriesdialog.ui
watchedfolderoptionsdialog.ui
)
target_sources(qbt_gui INTERFACE about.qrc)

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