mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-23 16:58:06 -06:00
Compare commits
78 Commits
release-4.
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01519b5e77 | ||
|
|
95ad606db1 | ||
|
|
7a31c4c1fd | ||
|
|
74b4c24c8f | ||
|
|
fac91a29e8 | ||
|
|
a58cfe3f00 | ||
|
|
1bae770b2c | ||
|
|
298d63d47c | ||
|
|
c598bdd290 | ||
|
|
8ba8d69bef | ||
|
|
57ec71b4d3 | ||
|
|
c561c28614 | ||
|
|
ac5c264e66 | ||
|
|
338d9a0848 | ||
|
|
0efd667d7e | ||
|
|
5fd6e18390 | ||
|
|
b5f4afabe0 | ||
|
|
9392ce436d | ||
|
|
8ceb2d8832 | ||
|
|
40ec873199 | ||
|
|
f3a5a43a20 | ||
|
|
72ba22afd1 | ||
|
|
423232b4b9 | ||
|
|
86a6b53f82 | ||
|
|
618afb3609 | ||
|
|
fefce03379 | ||
|
|
250cac73b6 | ||
|
|
84ec5a6acf | ||
|
|
0e2aca7583 | ||
|
|
3bded7e71b | ||
|
|
51ca65eea1 | ||
|
|
f21f7b8edc | ||
|
|
331304159d | ||
|
|
2aab675dc4 | ||
|
|
9c8016dc65 | ||
|
|
b99262bc36 | ||
|
|
078cfe656a | ||
|
|
89ac9bf2c9 | ||
|
|
81932b6313 | ||
|
|
4ce5d9d5e2 | ||
|
|
0415c0c6f8 | ||
|
|
def5765160 | ||
|
|
9977395c03 | ||
|
|
2b05b2b471 | ||
|
|
d1bd426618 | ||
|
|
b84c5edf51 | ||
|
|
3ac8c97e6f | ||
|
|
ede42910da | ||
|
|
500d0e717b | ||
|
|
a3f039ffcb | ||
|
|
81ad324209 | ||
|
|
81de07789a | ||
|
|
6a8a1f602f | ||
|
|
9dfaaa2dd1 | ||
|
|
c06d6eaa77 | ||
|
|
d198ee97a5 | ||
|
|
ac8105c304 | ||
|
|
6cb16cfbb7 | ||
|
|
2b475c4296 | ||
|
|
3a9e4397fd | ||
|
|
1ce0bb7cee | ||
|
|
dd4b09f3a9 | ||
|
|
8c2df049f1 | ||
|
|
8b66c444ee | ||
|
|
0ccbdbccf4 | ||
|
|
d08b6c81ba | ||
|
|
5f897709cf | ||
|
|
e25948e737 | ||
|
|
144956a209 | ||
|
|
667d4e4211 | ||
|
|
d957eef331 | ||
|
|
baa32a20e0 | ||
|
|
eff465126e | ||
|
|
09089b2d33 | ||
|
|
3aa36ad40c | ||
|
|
24bc5a9875 | ||
|
|
744a2cb5a3 | ||
|
|
33e090cfcb |
@@ -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%"
|
||||
|
||||
240
.github/workflows/ci.yaml
vendored
240
.github/workflows/ci.yaml
vendored
@@ -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
20
.github/workflows/ci_file_health.yaml
vendored
Normal 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
73
.github/workflows/ci_macos.yaml
vendored
Normal 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
84
.github/workflows/ci_ubuntu.yaml
vendored
Normal 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
32
.github/workflows/ci_webui.yaml
vendored
Normal 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
85
.github/workflows/ci_windows.yaml
vendored
Normal 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
|
||||
4
.github/workflows/file_health.sh
vendored
4
.github/workflows/file_health.sh
vendored
@@ -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"
|
||||
|
||||
|
||||
22
.github/workflows/file_health.yaml
vendored
22
.github/workflows/file_health.yaml
vendored
@@ -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
|
||||
31
.github/workflows/webui_ci.yaml
vendored
31
.github/workflows/webui_ci.yaml
vendored
@@ -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
|
||||
188
.travis.yml
188
.travis.yml
@@ -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
|
||||
@@ -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
65
COPYING
@@ -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.
|
||||
|
||||
38
Changelog
38
Changelog
@@ -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)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
qBittorrent - A BitTorrent client in Qt
|
||||
------------------------------------------
|
||||
|
||||
[](https://travis-ci.org/qbittorrent/qBittorrent)
|
||||
[](https://ci.appveyor.com/project/qbittorrent/qBittorrent)
|
||||
[](https://github.com/qbittorrent/qBittorrent/actions)
|
||||
[](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
20
configure
vendored
@@ -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\\"
|
||||
|
||||
|
||||
@@ -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
2
dist/mac/Info.plist
vendored
@@ -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>
|
||||
|
||||
BIN
dist/qt-translations/qt_fa.qm
vendored
BIN
dist/qt-translations/qt_fa.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qt_gl.qm
vendored
BIN
dist/qt-translations/qt_gl.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qt_lt.qm
vendored
BIN
dist/qt-translations/qt_lt.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qt_pt.qm
vendored
BIN
dist/qt-translations/qt_pt.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qt_sl.qm
vendored
BIN
dist/qt-translations/qt_sl.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qt_zh_CN.qm
vendored
BIN
dist/qt-translations/qt_zh_CN.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_ar.qm
vendored
BIN
dist/qt-translations/qtbase_ar.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_bg.qm
vendored
BIN
dist/qt-translations/qtbase_bg.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_ca.qm
vendored
BIN
dist/qt-translations/qtbase_ca.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_cs.qm
vendored
BIN
dist/qt-translations/qtbase_cs.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_da.qm
vendored
BIN
dist/qt-translations/qtbase_da.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_de.qm
vendored
BIN
dist/qt-translations/qtbase_de.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_es.qm
vendored
BIN
dist/qt-translations/qtbase_es.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_fi.qm
vendored
BIN
dist/qt-translations/qtbase_fi.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_fr.qm
vendored
BIN
dist/qt-translations/qtbase_fr.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_gd.qm
vendored
BIN
dist/qt-translations/qtbase_gd.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_he.qm
vendored
BIN
dist/qt-translations/qtbase_he.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_hu.qm
vendored
BIN
dist/qt-translations/qtbase_hu.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_it.qm
vendored
BIN
dist/qt-translations/qtbase_it.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_ja.qm
vendored
BIN
dist/qt-translations/qtbase_ja.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_ko.qm
vendored
BIN
dist/qt-translations/qtbase_ko.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_lv.qm
vendored
BIN
dist/qt-translations/qtbase_lv.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_pl.qm
vendored
BIN
dist/qt-translations/qtbase_pl.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_ru.qm
vendored
BIN
dist/qt-translations/qtbase_ru.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_sk.qm
vendored
BIN
dist/qt-translations/qtbase_sk.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_tr.qm
vendored
Normal file
BIN
dist/qt-translations/qtbase_tr.qm
vendored
Normal file
Binary file not shown.
BIN
dist/qt-translations/qtbase_uk.qm
vendored
BIN
dist/qt-translations/qtbase_uk.qm
vendored
Binary file not shown.
BIN
dist/qt-translations/qtbase_zh_TW.qm
vendored
BIN
dist/qt-translations/qtbase_zh_TW.qm
vendored
Binary file not shown.
@@ -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>
|
||||
|
||||
26
dist/unix/org.qbittorrent.qBittorrent.desktop
vendored
26
dist/unix/org.qbittorrent.qBittorrent.desktop
vendored
@@ -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
|
||||
|
||||
10
dist/windows/installer-translations/danish.nsi
vendored
10
dist/windows/installer-translations/danish.nsi
vendored
@@ -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
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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
|
||||
|
||||
54
dist/windows/installer-translations/korean.nsi
vendored
54
dist/windows/installer-translations/korean.nsi
vendored
@@ -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} "자석 연결을 제거하지 않습니다. 관련 항목:"
|
||||
|
||||
54
dist/windows/installer-translations/polish.nsi
vendored
54
dist/windows/installer-translations/polish.nsi
vendored
@@ -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:"
|
||||
|
||||
54
dist/windows/installer-translations/swedish.nsi
vendored
54
dist/windows/installer-translations/swedish.nsi
vendored
@@ -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:"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -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
|
||||
|
||||
@@ -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 ¶ms)
|
||||
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 ¶ms)
|
||||
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();
|
||||
|
||||
@@ -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(' ');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -311,7 +311,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
catch (const CommandLineParameterError &er)
|
||||
{
|
||||
displayBadArgMessage(er.messageForUser());
|
||||
displayBadArgMessage(er.message());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); });
|
||||
|
||||
|
||||
@@ -110,3 +110,5 @@ namespace BitTorrent
|
||||
std::shared_ptr<lt::torrent_info> m_nativeInfo;
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(BitTorrent::TorrentInfo)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 &) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
662
src/base/torrentfileswatcher.cpp
Normal file
662
src/base/torrentfileswatcher.cpp
Normal 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 ¶ms)
|
||||
{
|
||||
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"
|
||||
96
src/base/torrentfileswatcher.h
Normal file
96
src/base/torrentfileswatcher.h
Normal 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;
|
||||
};
|
||||
@@ -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[] = "أۇزبېك";
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace
|
||||
{
|
||||
info = {exeName, versionStr.left(idx)};
|
||||
}
|
||||
catch (const std::runtime_error &)
|
||||
catch (const RuntimeError &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user