You've already forked qBittorrent
mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-10-09 18:32:15 +02:00
Compare commits
83 Commits
release-4.
...
release-4.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d2f3d1ec2c | ||
![]() |
bc6e3ae40d | ||
![]() |
b02e239f7c | ||
![]() |
397cd4bf60 | ||
![]() |
409f972ad3 | ||
![]() |
267961ffca | ||
![]() |
dff753c452 | ||
![]() |
ce3bafd30d | ||
![]() |
e5538d9f25 | ||
![]() |
7a8a32b1c3 | ||
![]() |
cac5e0391b | ||
![]() |
726790fa93 | ||
![]() |
c9c7a5be53 | ||
![]() |
1495513cfc | ||
![]() |
085afaac14 | ||
![]() |
d58a54c758 | ||
![]() |
78d9bcb6a1 | ||
![]() |
1a43cd329d | ||
![]() |
2fe687eeca | ||
![]() |
107bd8a54f | ||
![]() |
865df3fcf1 | ||
![]() |
cbf9c52462 | ||
![]() |
171e25e962 | ||
![]() |
239d14fd10 | ||
![]() |
0b7a175156 | ||
![]() |
b37dbb60b5 | ||
![]() |
d5acd1f210 | ||
![]() |
65eda4a68e | ||
![]() |
7209881025 | ||
![]() |
a5d0a4b619 | ||
![]() |
79276a8786 | ||
![]() |
fa2b645a64 | ||
![]() |
4d5d6df734 | ||
![]() |
2c39b69c18 | ||
![]() |
13c0077e95 | ||
![]() |
9299e3f371 | ||
![]() |
b9ddc6ee86 | ||
![]() |
276856a614 | ||
![]() |
fbd6a8a0da | ||
![]() |
6fc18b4af6 | ||
![]() |
44d633fb68 | ||
![]() |
eb4bf6cc68 | ||
![]() |
6db6c850eb | ||
![]() |
02ae1e3734 | ||
![]() |
eb887139fd | ||
![]() |
84805f7fb8 | ||
![]() |
2719131ed2 | ||
![]() |
52401bd2b0 | ||
![]() |
4834703bc4 | ||
![]() |
3ed73244b1 | ||
![]() |
97cd430125 | ||
![]() |
d202b85d51 | ||
![]() |
c51b79e9fc | ||
![]() |
4449018207 | ||
![]() |
ced8e41473 | ||
![]() |
2c66ed6708 | ||
![]() |
c7d3d6ac90 | ||
![]() |
13210b3e9f | ||
![]() |
6e622fc23b | ||
![]() |
ae35111b59 | ||
![]() |
e1c3d419a7 | ||
![]() |
7396b8adba | ||
![]() |
c09001545d | ||
![]() |
f8d4315f7e | ||
![]() |
1fa2957d27 | ||
![]() |
ade50d2b53 | ||
![]() |
0fa1d35b87 | ||
![]() |
6486fc5f4d | ||
![]() |
1e059ab1a2 | ||
![]() |
15b137211b | ||
![]() |
6f8f1d7bad | ||
![]() |
a31f0c0a3d | ||
![]() |
f977d1293a | ||
![]() |
1399be50cb | ||
![]() |
52dcf32cc8 | ||
![]() |
52b2b807ab | ||
![]() |
5cf4f00824 | ||
![]() |
faa6fad025 | ||
![]() |
9f94bbce3a | ||
![]() |
5c49b2486c | ||
![]() |
4f6e7f97c6 | ||
![]() |
7751c5b75c | ||
![]() |
a1a9f3317b |
19
.travis.yml
19
.travis.yml
@@ -76,10 +76,13 @@ before_install:
|
||||
|
||||
- shopt -s expand_aliases
|
||||
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
|
||||
#- libt_path="$HOME/libt_install"
|
||||
#- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
|
||||
- qbt_path="$HOME/qbt_install"
|
||||
- qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH=/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
else
|
||||
qbtconf="$qbtconf --prefix="$qbt_path""
|
||||
fi
|
||||
|
||||
# options for specific branches
|
||||
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
|
||||
@@ -131,13 +134,13 @@ install:
|
||||
cp "version" $HOME/hombebrew_cache
|
||||
cd "$HOME/hombebrew_cache"
|
||||
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
|
||||
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.11+git20170910.6d5625e0ea.el_capitan.bottle.tar.gz
|
||||
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz
|
||||
fi
|
||||
|
||||
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it
|
||||
# Also install our custom libtorrent formula by passing the local path to it
|
||||
# These 2 files are restored from Travis' cache.
|
||||
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.11+git20170910.6d5625e0ea.el_capitan.bottle.tar.gz" "$(brew --cache)"
|
||||
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)"
|
||||
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
|
||||
|
||||
if [ "$build_system" = "cmake" ]; then
|
||||
@@ -168,11 +171,15 @@ script:
|
||||
BUILD_TOOL="ninja"
|
||||
fi
|
||||
if [ "$build_system" = "qmake" ]; then
|
||||
./bootstrap.sh && ./configure $qbtconf
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
# For some reason for RC_1_1 we need to also specify the OpenSSL compiler/linker flags
|
||||
# Homebrew doesn't symlink OpenSSL for security reasons
|
||||
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --cflags openssl)" LDFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --libs openssl)"
|
||||
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
|
||||
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
|
||||
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
|
||||
else
|
||||
./bootstrap.sh && ./configure $qbtconf
|
||||
fi
|
||||
BUILD_TOOL="make"
|
||||
fi
|
||||
|
@@ -240,7 +240,23 @@ Example:
|
||||
|
||||
```
|
||||
|
||||
### 9. Misc. ###
|
||||
### 9. Include guard. ###
|
||||
`#pragma once` should be used instead of "include guard" in new code:
|
||||
```c++
|
||||
// examplewidget.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class ExampleWidget : public QWidget
|
||||
{
|
||||
// (some code omitted)
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### 10. Misc. ###
|
||||
|
||||
* Line breaks for long lines with operation:
|
||||
|
||||
|
62
Changelog
62
Changelog
@@ -1,3 +1,65 @@
|
||||
* Fri Feb 16 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.4
|
||||
- FEATURE: Add source field in Torrent creator. Closes #7965. (Chocobo1)
|
||||
- FEATURE: Torrent creator: raise maximum piece size to 32 MiB (Chocobo1)
|
||||
- FEATURE: Add a force reannounce option in the transfer list context menu. Closes #6448. (Jesse Bryan)
|
||||
- BUGFIX: Fix sorting of country flags column in Peers tab. (sledgehammer999)
|
||||
- BUGFIX: Fix natural sorting when the common part of 2 strings ends partially in a number which continues in the uncommon part. Closes #8080 #6732. (sledgehammer999)
|
||||
- BUGFIX: Fix application of speed limits on LAN and μTP connections. Closes #7745. (sledgehammer999)
|
||||
- BUGFIX: Make peer information flags in peerlist more readable. (thalieht)
|
||||
- BUGFIX: Fix gui issues on high DPI monitor. (Chocobo1)
|
||||
- BUGFIX: Fix dialog and column size on high DPI monitors. (Chocobo1)
|
||||
- BUGFIX: Fix constant status of '[F] Downloading'. Closes #7628. (sledgehammer999)
|
||||
- BUGFIX: Fix translation context. Closes #8211. (sledgehammer999)
|
||||
- BUGFIX: Separate subnet whitelist options into two lines. (Thomas Piccirello)
|
||||
- BUGFIX: Don't set application name twice. (Luís Pereira)
|
||||
- BUGFIX: Set default file log size to 65 KiB and delete backup logs older than 1 month. (sledgehammer999)
|
||||
- WEBUI: Only prepend scheme when it is not present. Closes #8057. (Chocobo1)
|
||||
- WEBUI: Add "Remaining" and "Availability" columns to webui Content tab. (Thomas Piccirello)
|
||||
- WEBUI: Make value formatting consistent with GUI (Thomas Piccirello)
|
||||
- WEBUI: Reposition Total Size column to match gui (Thomas Piccirello)
|
||||
- WEBUI: Add Tags and Time Active columns (Thomas Piccirello)
|
||||
- WEBUI: Use https for www.qbittorrent.org (Thomas Piccirello)
|
||||
- WEBUI: Match webui statuses to gui, closes #7516 (Thomas Piccirello)
|
||||
- WEBUI: Right-align stat values (Thomas Piccirello)
|
||||
- WEBUI: Add missing units. (Thomas Piccirello)
|
||||
- RSS: Fix crash when deleting rule because it tries to update. Closes #8094 (glassez)
|
||||
- RSS: Don't process new/updated RSS rules when disabled (glassez)
|
||||
- RSS: Remove legacy and corrupted RSS settings (glassez)
|
||||
- SEARCH: Search only when category is supported by plugin. Closes #8053. (jan.karberg)
|
||||
- SEARCH: Only add search separators as needed. (Thomas Piccirello)
|
||||
- COSMETIC: Tweak spacing in torrent properties widget and speed widget. (Chocobo1)
|
||||
- WINDOWS: Use standard folder icon for open file behavior on Windows. Closes #7880. (Chocobo1)
|
||||
- WINDOWS: Revert "Run external program" function. Now you will not be able to directly run batch scripts. (Chocobo1)
|
||||
- MACOS: Fix torrent file selection in Finder on mac (vit9696)
|
||||
- MACOS: Fix Finder reveal in preview and torrent contents (vit9696)
|
||||
- MACOS: Fix cmd+w not closing the main window on macOS (vit9696)
|
||||
- OTHER: Fix splitting of compiler flags in configure. Autoconf removes a set of [] during script translation, resulting in a wrong sed command. (sledgehammer999)
|
||||
- OTHER: configure: Parse all compiler related flags together. (sledgehammer999)
|
||||
- OTHER: Update copyright year. (sledgehammer999)
|
||||
|
||||
* Sun Dec 17 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.3
|
||||
- BUGFIX: Add height padding to the transfer list icons. Closes #7951. (sledgehammer999)
|
||||
- BUGFIX: Allow to drag-n-drop URLs into mainwindow to initiate download. (Chocobo1)
|
||||
- BUGFIX: Fix crash when fitlering search results. Stable sorting is removed. Closes #7952.(Chocobo1)
|
||||
- WEBUI: Fix missing qbt logo on login page in webUI. Closes #7953. (Chocobo1)
|
||||
- WEBUI: Add check to avoid type error after logout. (Chocobo1)
|
||||
- WEBUI: Use POST for logout command. This is to avoid browser being smart to prefetch the link then logging out the user. (Chocobo1)
|
||||
- WEBUI: Fix WebUI is not reachable via IPv6. (glassez)
|
||||
- WINDOWS: Disable the "?" help button in all dialogs on Windows. Closes #7365. Requires Qt 5.10. (Chocobo1)
|
||||
|
||||
* Fri Dec 01 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.2
|
||||
- BUGFIX: Fix crash on some systems when creating address object for 255.255.255.255. Closes #7735. (sledgehammer999)
|
||||
- PERFORMANCE: Change MixedModeAlgorithm default to TCP. This was the v3_3_x default and should sustain higher speeds. Closes #7779. (Chocobo1)
|
||||
- PERFORMANCE: Stop logging IP filter parsing errors after a while, otherwise the GUI freezes or qBittorrent doesn't start. (sledgehammer999)
|
||||
- GUI: Implement stable sort. Rows in transfer list shouldn't flicker anymore. (Chocobo1)
|
||||
- WEBUI: Fix build when webui is disabled. (Heiko Becker)
|
||||
- RSS: Fix build because of missing header. Closes #7805. (thoradia)
|
||||
- RSS: Fix RSS parser. (glassez)
|
||||
- RSS: Implement Import/Export RSS rules in legacy(aka v3_3_x) format. (glassez)
|
||||
- RSS: Implement Import/Export RSS rules in JSON format. (glassez)
|
||||
- WINDOWS: Fixed blurry text under Windows by setting DPI awareness to default. (TheNicker)
|
||||
- LINUX: Fix i386 build. (Evgeny Lensky)
|
||||
|
||||
* Wed Nov 22 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.1
|
||||
- BUGFIX: Fix crash on opening torrent/magnet (uninitialized pointer). Closes #7739 #7723. (sledgehammer999)
|
||||
- BUGFIX: Enable preferences Apply button when ip banlist is modified (Thomas Piccirello)
|
||||
|
@@ -35,7 +35,7 @@ You can also download it from [here](https://github.com/qbittorrent/qBittorrent/
|
||||
|
||||
### Misc:
|
||||
For more information please visit:
|
||||
http://www.qbittorrent.org
|
||||
https://www.qbittorrent.org
|
||||
|
||||
or our wiki here:
|
||||
http://wiki.qbittorrent.org
|
||||
|
40
configure
vendored
40
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for qbittorrent v3.2.0alpha.
|
||||
# Generated by GNU Autoconf 2.69 for qbittorrent v4.0.4.
|
||||
#
|
||||
# Report bugs to <bugs.qbittorrent.org>.
|
||||
#
|
||||
@@ -580,10 +580,10 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='qbittorrent'
|
||||
PACKAGE_TARNAME='qbittorrent'
|
||||
PACKAGE_VERSION='v3.2.0alpha'
|
||||
PACKAGE_STRING='qbittorrent v3.2.0alpha'
|
||||
PACKAGE_VERSION='v4.0.4'
|
||||
PACKAGE_STRING='qbittorrent v4.0.4'
|
||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||
PACKAGE_URL='http://www.qbittorrent.org/'
|
||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||
|
||||
ac_subst_vars='am__EXEEXT_FALSE
|
||||
am__EXEEXT_TRUE
|
||||
@@ -1296,7 +1296,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 v3.2.0alpha to adapt to many kinds of systems.
|
||||
\`configure' configures qbittorrent v4.0.4 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1367,7 +1367,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of qbittorrent v3.2.0alpha:";;
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.0.4:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1438,7 +1438,7 @@ Use these variables to override the choices made by `configure' or to help
|
||||
it to find libraries and programs with nonstandard names/locations.
|
||||
|
||||
Report bugs to <bugs.qbittorrent.org>.
|
||||
qbittorrent home page: <http://www.qbittorrent.org/>.
|
||||
qbittorrent home page: <https://www.qbittorrent.org/>.
|
||||
_ACEOF
|
||||
ac_status=$?
|
||||
fi
|
||||
@@ -1501,7 +1501,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
qbittorrent configure v3.2.0alpha
|
||||
qbittorrent configure v4.0.4
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -1640,7 +1640,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 v3.2.0alpha, which was
|
||||
It was created by qbittorrent $as_me v4.0.4, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3818,7 +3818,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='qbittorrent'
|
||||
VERSION='v3.2.0alpha'
|
||||
VERSION='v4.0.4'
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
@@ -4705,9 +4705,8 @@ fi
|
||||
libsubdirs="lib64 libx32 lib lib64" ;; #(
|
||||
ppc64|s390x|sparc64|aarch64|ppc64le) :
|
||||
libsubdirs="lib64 lib lib64" ;; #(
|
||||
libsubdirs="lib") :
|
||||
;; #(
|
||||
*) :
|
||||
libsubdirs="lib"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -5502,7 +5501,7 @@ extract() {
|
||||
new_line='
|
||||
'
|
||||
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
|
||||
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[:space:]*//')
|
||||
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
|
||||
SAVEIFS=$IFS
|
||||
IFS=$(printf "\n\b")
|
||||
for i in $string; do
|
||||
@@ -5516,9 +5515,8 @@ extract() {
|
||||
IFS=$SAVEIFS
|
||||
}
|
||||
|
||||
extract $CPPFLAGS
|
||||
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
|
||||
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
|
||||
QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS $CXXFLAGS"
|
||||
|
||||
# Substitute the values of these vars in conf.pri.in
|
||||
|
||||
@@ -6100,7 +6098,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 v3.2.0alpha, which was
|
||||
This file was extended by qbittorrent $as_me v4.0.4, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -6152,13 +6150,13 @@ Configuration commands:
|
||||
$config_commands
|
||||
|
||||
Report bugs to <bugs.qbittorrent.org>.
|
||||
qbittorrent home page: <http://www.qbittorrent.org/>."
|
||||
qbittorrent home page: <https://www.qbittorrent.org/>."
|
||||
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v3.2.0alpha
|
||||
qbittorrent config.status v4.0.4
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
@@ -7415,7 +7413,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 v3.2.0alpha, which was
|
||||
This file was extended by qbittorrent $as_me v4.0.4, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -7467,13 +7465,13 @@ Configuration commands:
|
||||
$config_commands
|
||||
|
||||
Report bugs to <bugs.qbittorrent.org>.
|
||||
qbittorrent home page: <http://www.qbittorrent.org/>."
|
||||
qbittorrent home page: <https://www.qbittorrent.org/>."
|
||||
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v3.2.0alpha
|
||||
qbittorrent config.status v4.0.4
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
AC_INIT([qbittorrent], [v3.2.0alpha], [bugs.qbittorrent.org], [], [http://www.qbittorrent.org/])
|
||||
AC_INIT([qbittorrent], [v4.0.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_PROG_CC
|
||||
@@ -190,7 +190,7 @@ extract() {
|
||||
new_line='
|
||||
'
|
||||
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
|
||||
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
|
||||
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[[:space:]]]*//')
|
||||
SAVEIFS=$IFS
|
||||
IFS=$(printf "\n\b")
|
||||
for i in $string; do
|
||||
@@ -204,9 +204,8 @@ extract() {
|
||||
IFS=$SAVEIFS
|
||||
}
|
||||
|
||||
extract $CPPFLAGS
|
||||
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
|
||||
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
|
||||
QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS $CXXFLAGS"
|
||||
|
||||
# Substitute the values of these vars in conf.pri.in
|
||||
AC_SUBST(QBT_CONF_INCLUDES)
|
||||
|
4
dist/mac/Info.plist
vendored
4
dist/mac/Info.plist
vendored
@@ -45,7 +45,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.1</string>
|
||||
<string>4.0.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>qBit</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@@ -59,7 +59,7 @@
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<string>YES</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2006-2017 The qBittorrent project</string>
|
||||
<string>Copyright © 2006-2018 The qBittorrent project</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
2
dist/unix/qbittorrent.appdata.xml
vendored
2
dist/unix/qbittorrent.appdata.xml
vendored
@@ -56,7 +56,7 @@
|
||||
</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<url type="homepage">http://www.qbittorrent.org/</url>
|
||||
<url type="homepage">https://www.qbittorrent.org/</url>
|
||||
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
|
||||
<developer_name>The qBittorrent Project</developer_name>
|
||||
<url type="bugtracker">http://bugs.qbittorrent.org/</url>
|
||||
|
2
dist/windows/installer.nsi
vendored
2
dist/windows/installer.nsi
vendored
@@ -86,7 +86,7 @@ Section $(inst_qbt_req) ;"qBittorrent (required)"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "UninstallString" '"$INSTDIR\uninst.exe"'
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "DisplayIcon" '"$INSTDIR\qbittorrent.exe",0'
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "Publisher" "The qBittorrent project"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "URLInfoAbout" "http://www.qbittorrent.org"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "URLInfoAbout" "https://www.qbittorrent.org"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "DisplayVersion" "${PROG_VERSION}"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoRepair" 1
|
||||
|
4
dist/windows/options.nsi
vendored
4
dist/windows/options.nsi
vendored
@@ -27,7 +27,7 @@ XPStyle on
|
||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||
|
||||
; Program specific
|
||||
!define PROG_VERSION "4.0.1"
|
||||
!define PROG_VERSION "4.0.4"
|
||||
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
@@ -50,7 +50,7 @@ XPStyle on
|
||||
;Installer Version Information
|
||||
VIAddVersionKey "ProductName" "qBittorrent"
|
||||
VIAddVersionKey "CompanyName" "The qBittorrent project"
|
||||
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2017 The qBittorrent project"
|
||||
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2018 The qBittorrent project"
|
||||
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
|
||||
VIAddVersionKey "FileVersion" "${PROG_VERSION}"
|
||||
|
||||
|
2
dist/windows/qt.conf
vendored
2
dist/windows/qt.conf
vendored
@@ -2,4 +2,4 @@
|
||||
Translations = translations
|
||||
|
||||
[Platforms]
|
||||
WindowsArguments = dpiawareness=1
|
||||
;WindowsArguments = dpiawareness=1
|
||||
|
@@ -114,7 +114,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
|
||||
AS_CASE([${host_cpu}],
|
||||
[x86_64],[libsubdirs="lib64 libx32 lib lib64"],
|
||||
[ppc64|s390x|sparc64|aarch64|ppc64le],[libsubdirs="lib64 lib lib64"],
|
||||
[libsubdirs="lib"],
|
||||
[libsubdirs="lib"]
|
||||
)
|
||||
|
||||
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
|
||||
|
@@ -87,7 +87,7 @@ namespace
|
||||
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
|
||||
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
|
||||
const QString KEY_FILELOGGER_DELETEOLD = FILELOGGER_SETTINGS_KEY("DeleteOld");
|
||||
const QString KEY_FILELOGGER_MAXSIZE = FILELOGGER_SETTINGS_KEY("MaxSize");
|
||||
const QString KEY_FILELOGGER_MAXSIZEBYTES = FILELOGGER_SETTINGS_KEY("MaxSizeBytes");
|
||||
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
|
||||
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
|
||||
|
||||
@@ -98,6 +98,10 @@ namespace
|
||||
const char PARAMS_SEPARATOR[] = "|";
|
||||
|
||||
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
|
||||
|
||||
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
||||
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
||||
const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB
|
||||
}
|
||||
|
||||
Application::Application(const QString &id, int &argc, char **argv)
|
||||
@@ -105,7 +109,9 @@ Application::Application(const QString &id, int &argc, char **argv)
|
||||
, m_running(false)
|
||||
, m_shutdownAct(ShutdownDialogAction::Exit)
|
||||
, m_commandLineArgs(parseCommandLine(this->arguments()))
|
||||
#ifndef DISABLE_WEBUI
|
||||
, m_webui(nullptr)
|
||||
#endif
|
||||
{
|
||||
qRegisterMetaType<Log::Msg>("Log::Msg");
|
||||
|
||||
@@ -126,7 +132,6 @@ Application::Application(const QString &id, int &argc, char **argv)
|
||||
if (m_commandLineArgs.webUiPort > 0) // it will be -1 when user did not set any value
|
||||
Preferences::instance()->setWebUiPort(m_commandLineArgs.webUiPort);
|
||||
|
||||
setApplicationName("qBittorrent");
|
||||
initializeTranslation();
|
||||
|
||||
#if !defined(DISABLE_GUI)
|
||||
@@ -219,29 +224,22 @@ void Application::setFileLoggerDeleteOld(bool value)
|
||||
|
||||
int Application::fileLoggerMaxSize() const
|
||||
{
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZE, 10).toInt();
|
||||
if (val < 1)
|
||||
return 1;
|
||||
if (val > 1000)
|
||||
return 1000;
|
||||
return val;
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZEBYTES, DEFAULT_FILELOG_SIZE).toInt();
|
||||
return std::min(std::max(val, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
|
||||
}
|
||||
|
||||
void Application::setFileLoggerMaxSize(const int value)
|
||||
void Application::setFileLoggerMaxSize(const int bytes)
|
||||
{
|
||||
int clampedValue = std::min(std::max(bytes, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
|
||||
if (m_fileLogger)
|
||||
m_fileLogger->setMaxSize(value);
|
||||
settings()->storeValue(KEY_FILELOGGER_MAXSIZE, std::min(std::max(value, 1), 1000));
|
||||
m_fileLogger->setMaxSize(clampedValue);
|
||||
settings()->storeValue(KEY_FILELOGGER_MAXSIZEBYTES, clampedValue);
|
||||
}
|
||||
|
||||
int Application::fileLoggerAge() const
|
||||
{
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 6).toInt();
|
||||
if (val < 1)
|
||||
return 1;
|
||||
if (val > 365)
|
||||
return 365;
|
||||
return val;
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 1).toInt();
|
||||
return std::min(std::max(val, 1), 365);
|
||||
}
|
||||
|
||||
void Application::setFileLoggerAge(const int value)
|
||||
@@ -289,27 +287,6 @@ void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) c
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
|
||||
#elif defined(Q_OS_WIN) // test cmd: `echo "%F" > "c:\ab ba.txt"`
|
||||
program.prepend(QLatin1String("\"")).append(QLatin1String("\""));
|
||||
program.prepend(Utils::Misc::windowsSystemPath() + QLatin1String("\\cmd.exe /C "));
|
||||
const int cmdMaxLength = 32768; // max length (incl. terminate char) for `lpCommandLine` in `CreateProcessW()`
|
||||
if ((program.size() + 1) > cmdMaxLength) {
|
||||
logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(cmdMaxLength), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
|
||||
STARTUPINFOW si = {0};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
|
||||
WCHAR *arg = new WCHAR[program.size() + 1];
|
||||
program.toWCharArray(arg);
|
||||
arg[program.size()] = L'\0';
|
||||
if (CreateProcessW(NULL, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
delete[] arg;
|
||||
#else
|
||||
QProcess::startDetached(program);
|
||||
#endif
|
||||
|
@@ -101,7 +101,7 @@ public:
|
||||
bool isFileLoggerDeleteOld() const;
|
||||
void setFileLoggerDeleteOld(bool value);
|
||||
int fileLoggerMaxSize() const;
|
||||
void setFileLoggerMaxSize(const int value);
|
||||
void setFileLoggerMaxSize(const int bytes);
|
||||
int fileLoggerAge() const;
|
||||
void setFileLoggerAge(const int value);
|
||||
int fileLoggerAgeType() const;
|
||||
|
@@ -135,7 +135,7 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
|
||||
|
||||
str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl;
|
||||
|
||||
if (m_backup && (m_logFile->size() >= (m_maxSize * 1024 * 1024))) {
|
||||
if (m_backup && (m_logFile->size() >= m_maxSize)) {
|
||||
closeLogFile();
|
||||
int counter = 0;
|
||||
QString backupLogFilename = m_path + ".bak";
|
||||
|
@@ -212,6 +212,10 @@ int main(int argc, char *argv[])
|
||||
// 3. https://bugreports.qt.io/browse/QTBUG-46015
|
||||
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||
// this is the default in Qt6
|
||||
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
|
@@ -1,124 +1,124 @@
|
||||
HEADERS += \
|
||||
$$PWD/asyncfilestorage.h \
|
||||
$$PWD/types.h \
|
||||
$$PWD/tristatebool.h \
|
||||
$$PWD/bittorrent/addtorrentparams.h \
|
||||
$$PWD/bittorrent/cachestatus.h \
|
||||
$$PWD/bittorrent/infohash.h \
|
||||
$$PWD/bittorrent/magneturi.h \
|
||||
$$PWD/bittorrent/peerinfo.h \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.h \
|
||||
$$PWD/bittorrent/private/filterparserthread.h \
|
||||
$$PWD/bittorrent/private/resumedatasavingmanager.h \
|
||||
$$PWD/bittorrent/private/speedmonitor.h \
|
||||
$$PWD/bittorrent/private/statistics.h \
|
||||
$$PWD/bittorrent/session.h \
|
||||
$$PWD/bittorrent/sessionstatus.h \
|
||||
$$PWD/bittorrent/torrentcreatorthread.h \
|
||||
$$PWD/bittorrent/torrenthandle.h \
|
||||
$$PWD/bittorrent/torrentinfo.h \
|
||||
$$PWD/bittorrent/tracker.h \
|
||||
$$PWD/bittorrent/trackerentry.h \
|
||||
$$PWD/filesystemwatcher.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/settingsstorage.h \
|
||||
$$PWD/settingvalue.h \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/indexrange.h \
|
||||
$$PWD/iconprovider.h \
|
||||
$$PWD/http/irequesthandler.h \
|
||||
$$PWD/global.h \
|
||||
$$PWD/http/connection.h \
|
||||
$$PWD/http/irequesthandler.h \
|
||||
$$PWD/http/requestparser.h \
|
||||
$$PWD/http/responsebuilder.h \
|
||||
$$PWD/http/responsegenerator.h \
|
||||
$$PWD/http/server.h \
|
||||
$$PWD/http/types.h \
|
||||
$$PWD/http/responsebuilder.h \
|
||||
$$PWD/iconprovider.h \
|
||||
$$PWD/indexrange.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/net/dnsupdater.h \
|
||||
$$PWD/net/downloadmanager.h \
|
||||
$$PWD/net/downloadhandler.h \
|
||||
$$PWD/net/downloadmanager.h \
|
||||
$$PWD/net/geoipmanager.h \
|
||||
$$PWD/net/portforwarder.h \
|
||||
$$PWD/net/private/geoipdatabase.h \
|
||||
$$PWD/net/proxyconfigurationmanager.h \
|
||||
$$PWD/net/reverseresolution.h \
|
||||
$$PWD/net/smtp.h \
|
||||
$$PWD/net/private/geoipdatabase.h \
|
||||
$$PWD/bittorrent/addtorrentparams.h \
|
||||
$$PWD/bittorrent/infohash.h \
|
||||
$$PWD/bittorrent/session.h \
|
||||
$$PWD/bittorrent/sessionstatus.h \
|
||||
$$PWD/bittorrent/cachestatus.h \
|
||||
$$PWD/bittorrent/magneturi.h \
|
||||
$$PWD/bittorrent/torrentinfo.h \
|
||||
$$PWD/bittorrent/torrenthandle.h \
|
||||
$$PWD/bittorrent/peerinfo.h \
|
||||
$$PWD/bittorrent/trackerentry.h \
|
||||
$$PWD/bittorrent/tracker.h \
|
||||
$$PWD/bittorrent/torrentcreatorthread.h \
|
||||
$$PWD/bittorrent/private/speedmonitor.h \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.h \
|
||||
$$PWD/bittorrent/private/filterparserthread.h \
|
||||
$$PWD/bittorrent/private/statistics.h \
|
||||
$$PWD/bittorrent/private/resumedatasavingmanager.h \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/private/profile_p.h \
|
||||
$$PWD/profile.h \
|
||||
$$PWD/rss/private/rss_parser.h \
|
||||
$$PWD/rss/rss_article.h \
|
||||
$$PWD/rss/rss_item.h \
|
||||
$$PWD/rss/rss_feed.h \
|
||||
$$PWD/rss/rss_folder.h \
|
||||
$$PWD/rss/rss_session.h \
|
||||
$$PWD/rss/rss_autodownloader.h \
|
||||
$$PWD/rss/rss_autodownloadrule.h \
|
||||
$$PWD/rss/private/rss_parser.h \
|
||||
$$PWD/rss/rss_feed.h \
|
||||
$$PWD/rss/rss_folder.h \
|
||||
$$PWD/rss/rss_item.h \
|
||||
$$PWD/rss/rss_session.h \
|
||||
$$PWD/scanfoldersmodel.h \
|
||||
$$PWD/searchengine.h \
|
||||
$$PWD/settingsstorage.h \
|
||||
$$PWD/settingvalue.h \
|
||||
$$PWD/torrentfileguard.h \
|
||||
$$PWD/torrentfilter.h \
|
||||
$$PWD/tristatebool.h \
|
||||
$$PWD/types.h \
|
||||
$$PWD/unicodestrings.h \
|
||||
$$PWD/utils/fs.h \
|
||||
$$PWD/utils/gzip.h \
|
||||
$$PWD/utils/misc.h \
|
||||
$$PWD/utils/net.h \
|
||||
$$PWD/utils/random.h \
|
||||
$$PWD/utils/string.h \
|
||||
$$PWD/utils/version.h \
|
||||
$$PWD/profile.h \
|
||||
$$PWD/private/profile_p.h \
|
||||
$$PWD/unicodestrings.h \
|
||||
$$PWD/torrentfileguard.h \
|
||||
$$PWD/torrentfilter.h \
|
||||
$$PWD/scanfoldersmodel.h \
|
||||
$$PWD/searchengine.h \
|
||||
$$PWD/global.h
|
||||
$$PWD/utils/version.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/asyncfilestorage.cpp \
|
||||
$$PWD/tristatebool.cpp \
|
||||
$$PWD/bittorrent/infohash.cpp \
|
||||
$$PWD/bittorrent/magneturi.cpp \
|
||||
$$PWD/bittorrent/peerinfo.cpp \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/private/filterparserthread.cpp \
|
||||
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
|
||||
$$PWD/bittorrent/private/speedmonitor.cpp \
|
||||
$$PWD/bittorrent/private/statistics.cpp \
|
||||
$$PWD/bittorrent/session.cpp \
|
||||
$$PWD/bittorrent/torrentcreatorthread.cpp \
|
||||
$$PWD/bittorrent/torrenthandle.cpp \
|
||||
$$PWD/bittorrent/torrentinfo.cpp \
|
||||
$$PWD/bittorrent/tracker.cpp \
|
||||
$$PWD/bittorrent/trackerentry.cpp \
|
||||
$$PWD/filesystemwatcher.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/settingsstorage.cpp \
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/iconprovider.cpp \
|
||||
$$PWD/http/connection.cpp \
|
||||
$$PWD/http/requestparser.cpp \
|
||||
$$PWD/http/responsebuilder.cpp \
|
||||
$$PWD/http/responsegenerator.cpp \
|
||||
$$PWD/http/server.cpp \
|
||||
$$PWD/http/responsebuilder.cpp \
|
||||
$$PWD/iconprovider.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/net/dnsupdater.cpp \
|
||||
$$PWD/net/downloadmanager.cpp \
|
||||
$$PWD/net/downloadhandler.cpp \
|
||||
$$PWD/net/downloadmanager.cpp \
|
||||
$$PWD/net/geoipmanager.cpp \
|
||||
$$PWD/net/portforwarder.cpp \
|
||||
$$PWD/net/private/geoipdatabase.cpp \
|
||||
$$PWD/net/proxyconfigurationmanager.cpp \
|
||||
$$PWD/net/reverseresolution.cpp \
|
||||
$$PWD/net/smtp.cpp \
|
||||
$$PWD/net/private/geoipdatabase.cpp \
|
||||
$$PWD/bittorrent/infohash.cpp \
|
||||
$$PWD/bittorrent/session.cpp \
|
||||
$$PWD/bittorrent/magneturi.cpp \
|
||||
$$PWD/bittorrent/torrentinfo.cpp \
|
||||
$$PWD/bittorrent/torrenthandle.cpp \
|
||||
$$PWD/bittorrent/peerinfo.cpp \
|
||||
$$PWD/bittorrent/trackerentry.cpp \
|
||||
$$PWD/bittorrent/tracker.cpp \
|
||||
$$PWD/bittorrent/torrentcreatorthread.cpp \
|
||||
$$PWD/bittorrent/private/speedmonitor.cpp \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/private/filterparserthread.cpp \
|
||||
$$PWD/bittorrent/private/statistics.cpp \
|
||||
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/private/profile_p.cpp \
|
||||
$$PWD/profile.cpp \
|
||||
$$PWD/rss/private/rss_parser.cpp \
|
||||
$$PWD/rss/rss_article.cpp \
|
||||
$$PWD/rss/rss_item.cpp \
|
||||
$$PWD/rss/rss_feed.cpp \
|
||||
$$PWD/rss/rss_folder.cpp \
|
||||
$$PWD/rss/rss_session.cpp \
|
||||
$$PWD/rss/rss_autodownloader.cpp \
|
||||
$$PWD/rss/rss_autodownloadrule.cpp \
|
||||
$$PWD/rss/private/rss_parser.cpp \
|
||||
$$PWD/rss/rss_feed.cpp \
|
||||
$$PWD/rss/rss_folder.cpp \
|
||||
$$PWD/rss/rss_item.cpp \
|
||||
$$PWD/rss/rss_session.cpp \
|
||||
$$PWD/scanfoldersmodel.cpp \
|
||||
$$PWD/searchengine.cpp \
|
||||
$$PWD/settingsstorage.cpp \
|
||||
$$PWD/torrentfileguard.cpp \
|
||||
$$PWD/torrentfilter.cpp \
|
||||
$$PWD/tristatebool.cpp \
|
||||
$$PWD/utils/fs.cpp \
|
||||
$$PWD/utils/gzip.cpp \
|
||||
$$PWD/utils/misc.cpp \
|
||||
$$PWD/utils/net.cpp \
|
||||
$$PWD/utils/random.cpp \
|
||||
$$PWD/utils/string.cpp \
|
||||
$$PWD/profile.cpp \
|
||||
$$PWD/private/profile_p.cpp \
|
||||
$$PWD/torrentfileguard.cpp \
|
||||
$$PWD/torrentfilter.cpp \
|
||||
$$PWD/scanfoldersmodel.cpp \
|
||||
$$PWD/searchengine.cpp
|
||||
$$PWD/utils/string.cpp
|
||||
|
@@ -273,18 +273,20 @@ qreal PeerInfo::relevance() const
|
||||
|
||||
void PeerInfo::determineFlags()
|
||||
{
|
||||
QStringList flagsDescriptionList;
|
||||
|
||||
if (isInteresting()) {
|
||||
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
|
||||
if (isRemoteChocked()) {
|
||||
m_flags += "d ";
|
||||
m_flagsDescription += tr("interested(local) and choked(peer)");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "d = "
|
||||
+ tr("Interested(local) and Choked(peer)");
|
||||
}
|
||||
else {
|
||||
// D = Currently downloading (interested and not choked)
|
||||
m_flags += "D ";
|
||||
m_flagsDescription += tr("interested(local) and unchoked(peer)");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "D = "
|
||||
+ tr("interested(local) and unchoked(peer)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,97 +294,95 @@ void PeerInfo::determineFlags()
|
||||
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
|
||||
if (isChocked()) {
|
||||
m_flags += "u ";
|
||||
m_flagsDescription += tr("interested(peer) and choked(local)");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "u = "
|
||||
+ tr("interested(peer) and choked(local)");
|
||||
}
|
||||
else {
|
||||
// U = Currently uploading (interested and not choked)
|
||||
m_flags += "U ";
|
||||
m_flagsDescription += tr("interested(peer) and unchoked(local)");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "U = "
|
||||
+ tr("interested(peer) and unchoked(local)");
|
||||
}
|
||||
}
|
||||
|
||||
// O = Optimistic unchoke
|
||||
if (optimisticUnchoke()) {
|
||||
m_flags += "O ";
|
||||
m_flagsDescription += tr("optimistic unchoke");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "O = "
|
||||
+ tr("optimistic unchoke");
|
||||
}
|
||||
|
||||
// S = Peer is snubbed
|
||||
if (isSnubbed()) {
|
||||
m_flags += "S ";
|
||||
m_flagsDescription += tr("peer snubbed");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "S = "
|
||||
+ tr("peer snubbed");
|
||||
}
|
||||
|
||||
// I = Peer is an incoming connection
|
||||
if (!isLocalConnection()) {
|
||||
m_flags += "I ";
|
||||
m_flagsDescription += tr("incoming connection");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "I = "
|
||||
+ tr("incoming connection");
|
||||
}
|
||||
|
||||
// K = Peer is unchoking your client, but your client is not interested
|
||||
if (!isRemoteChocked() && !isInteresting()) {
|
||||
m_flags += "K ";
|
||||
m_flagsDescription += tr("not interested(local) and unchoked(peer)");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "K = "
|
||||
+ tr("not interested(local) and unchoked(peer)");
|
||||
}
|
||||
|
||||
// ? = Your client unchoked the peer but the peer is not interested
|
||||
if (!isChocked() && !isRemoteInterested()) {
|
||||
m_flags += "? ";
|
||||
m_flagsDescription += tr("not interested(peer) and unchoked(local)");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "? = "
|
||||
+ tr("not interested(peer) and unchoked(local)");
|
||||
}
|
||||
|
||||
// X = Peer was included in peerlists obtained through Peer Exchange (PEX)
|
||||
if (fromPeX()) {
|
||||
m_flags += "X ";
|
||||
m_flagsDescription += tr("peer from PEX");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "X = "
|
||||
+ tr("peer from PEX");
|
||||
}
|
||||
|
||||
// H = Peer was obtained through DHT
|
||||
if (fromDHT()) {
|
||||
m_flags += "H ";
|
||||
m_flagsDescription += tr("peer from DHT");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "H = "
|
||||
+ tr("peer from DHT");
|
||||
}
|
||||
|
||||
// E = Peer is using Protocol Encryption (all traffic)
|
||||
if (isRC4Encrypted()) {
|
||||
m_flags += "E ";
|
||||
m_flagsDescription += tr("encrypted traffic");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "E = "
|
||||
+ tr("encrypted traffic");
|
||||
}
|
||||
|
||||
// e = Peer is using Protocol Encryption (handshake)
|
||||
if (isPlaintextEncrypted()) {
|
||||
m_flags += "e ";
|
||||
m_flagsDescription += tr("encrypted handshake");
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "e = "
|
||||
+ tr("encrypted handshake");
|
||||
}
|
||||
|
||||
// P = Peer is using uTorrent uTP
|
||||
if (useUTPSocket()) {
|
||||
m_flags += "P ";
|
||||
m_flagsDescription += QString::fromUtf8(C_UTP);
|
||||
m_flagsDescription += ", ";
|
||||
flagsDescriptionList += "P = "
|
||||
+ QString::fromUtf8(C_UTP);
|
||||
}
|
||||
|
||||
// L = Peer is local
|
||||
if (fromLSD()) {
|
||||
m_flags += "L";
|
||||
m_flagsDescription += tr("peer from LSD");
|
||||
flagsDescriptionList += "L = "
|
||||
+ tr("peer from LSD");
|
||||
}
|
||||
|
||||
m_flags = m_flags.trimmed();
|
||||
m_flagsDescription = m_flagsDescription.trimmed();
|
||||
if (m_flagsDescription.endsWith(',', Qt::CaseInsensitive))
|
||||
m_flagsDescription.chop(1);
|
||||
m_flagsDescription = flagsDescriptionList.join('\n');
|
||||
}
|
||||
|
||||
QString PeerInfo::flags() const
|
||||
|
@@ -102,6 +102,7 @@ namespace
|
||||
}
|
||||
|
||||
const int BUFFER_SIZE = 2 * 1024 * 1024; // 2 MiB
|
||||
const int MAX_LOGGED_ERRORS = 5;
|
||||
}
|
||||
|
||||
FilterParserThread::FilterParserThread(QObject *parent)
|
||||
@@ -134,6 +135,12 @@ int FilterParserThread::parseDATFilterFile()
|
||||
int start = 0;
|
||||
int endOfLine = -1;
|
||||
int nbLine = 0;
|
||||
int parseErrorCount = 0;
|
||||
const auto addLog = [&parseErrorCount](const QString &msg)
|
||||
{
|
||||
if (parseErrorCount <= MAX_LOGGED_ERRORS)
|
||||
LogMsg(msg, Log::CRITICAL);
|
||||
};
|
||||
|
||||
while (true) {
|
||||
bytesRead = file.read(buffer.data() + offset, BUFFER_SIZE - offset - 1);
|
||||
@@ -202,7 +209,8 @@ int FilterParserThread::parseDATFilterFile()
|
||||
int endOfIPRange = ((firstComma == -1) ? (endOfLine - 1) : (firstComma - 1));
|
||||
int delimIP = findAndNullDelimiter(buffer.data(), '-', start, endOfIPRange);
|
||||
if (delimIP == -1) {
|
||||
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -210,7 +218,8 @@ int FilterParserThread::parseDATFilterFile()
|
||||
libt::address startAddr;
|
||||
int newStart = trim(buffer.data(), start, delimIP - 1);
|
||||
if (!parseIPAddress(buffer.data() + newStart, startAddr)) {
|
||||
LogMsg(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -218,14 +227,16 @@ int FilterParserThread::parseDATFilterFile()
|
||||
libt::address endAddr;
|
||||
newStart = trim(buffer.data(), delimIP + 1, endOfIPRange);
|
||||
if (!parseIPAddress(buffer.data() + newStart, endAddr)) {
|
||||
LogMsg(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((startAddr.is_v4() != endAddr.is_v4())
|
||||
|| (startAddr.is_v6() != endAddr.is_v6())) {
|
||||
LogMsg(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -238,8 +249,9 @@ int FilterParserThread::parseDATFilterFile()
|
||||
++ruleCount;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
LogMsg(tr("IP filter exception thrown for line %1. Exception is: %2").arg(nbLine)
|
||||
.arg(QString::fromLocal8Bit(e.what())), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter exception thrown for line %1. Exception is: %2")
|
||||
.arg(nbLine).arg(QString::fromLocal8Bit(e.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +259,9 @@ int FilterParserThread::parseDATFilterFile()
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (parseErrorCount > MAX_LOGGED_ERRORS)
|
||||
LogMsg(tr("%1 extra IP filter parsing errors occurred.", "513 extra IP filter parsing errors occurred.")
|
||||
.arg(parseErrorCount - MAX_LOGGED_ERRORS), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
@@ -268,6 +283,12 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
int start = 0;
|
||||
int endOfLine = -1;
|
||||
int nbLine = 0;
|
||||
int parseErrorCount = 0;
|
||||
const auto addLog = [&parseErrorCount](const QString &msg)
|
||||
{
|
||||
if (parseErrorCount <= MAX_LOGGED_ERRORS)
|
||||
LogMsg(msg, Log::CRITICAL);
|
||||
};
|
||||
|
||||
while (true) {
|
||||
bytesRead = file.read(buffer.data() + offset, BUFFER_SIZE - offset - 1);
|
||||
@@ -319,7 +340,8 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
// The "Some organization" part might contain a ':' char itself so we find the last occurrence
|
||||
int partsDelimiter = findAndNullDelimiter(buffer.data(), ':', start, endOfLine, true);
|
||||
if (partsDelimiter == -1) {
|
||||
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -327,7 +349,8 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
// IP Range should be split by a dash
|
||||
int delimIP = findAndNullDelimiter(buffer.data(), '-', partsDelimiter + 1, endOfLine);
|
||||
if (delimIP == -1) {
|
||||
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -335,7 +358,8 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
libt::address startAddr;
|
||||
int newStart = trim(buffer.data(), partsDelimiter + 1, delimIP - 1);
|
||||
if (!parseIPAddress(buffer.data() + newStart, startAddr)) {
|
||||
LogMsg(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -343,14 +367,16 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
libt::address endAddr;
|
||||
newStart = trim(buffer.data(), delimIP + 1, endOfLine);
|
||||
if (!parseIPAddress(buffer.data() + newStart, endAddr)) {
|
||||
LogMsg(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((startAddr.is_v4() != endAddr.is_v4())
|
||||
|| (startAddr.is_v6() != endAddr.is_v6())) {
|
||||
LogMsg(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine));
|
||||
start = endOfLine;
|
||||
continue;
|
||||
}
|
||||
@@ -362,8 +388,9 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
++ruleCount;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
LogMsg(tr("IP filter exception thrown for line %1. Exception is: %2").arg(nbLine)
|
||||
.arg(QString::fromLocal8Bit(e.what())), Log::CRITICAL);
|
||||
++parseErrorCount;
|
||||
addLog(tr("IP filter exception thrown for line %1. Exception is: %2")
|
||||
.arg(nbLine).arg(QString::fromLocal8Bit(e.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,6 +398,9 @@ int FilterParserThread::parseP2PFilterFile()
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (parseErrorCount > MAX_LOGGED_ERRORS)
|
||||
LogMsg(tr("%1 extra IP filter parsing errors occurred.", "513 extra IP filter parsing errors occurred.")
|
||||
.arg(parseErrorCount - MAX_LOGGED_ERRORS), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
|
@@ -304,7 +304,7 @@ Session::Session(QObject *parent)
|
||||
, m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
|
||||
, clampValue(BTProtocol::Both, BTProtocol::UTP))
|
||||
, m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
|
||||
, m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::Proportional
|
||||
, m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::TCP
|
||||
, clampValue(MixedModeAlgorithm::TCP, MixedModeAlgorithm::Proportional))
|
||||
, m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
|
||||
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
|
||||
@@ -1418,8 +1418,11 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
|
||||
void Session::configurePeerClasses()
|
||||
{
|
||||
libt::ip_filter f;
|
||||
f.add_rule(libt::address_v4::from_string("0.0.0.0")
|
||||
, libt::address_v4::from_string("255.255.255.255")
|
||||
// address_v4::from_string("255.255.255.255") crashes on some people's systems
|
||||
// so instead we use address_v4::broadcast()
|
||||
// Proactively do the same for 0.0.0.0 and address_v4::any()
|
||||
f.add_rule(libt::address_v4::any()
|
||||
, libt::address_v4::broadcast()
|
||||
, 1 << libt::session::global_peer_class_id);
|
||||
#if TORRENT_USE_IPV6
|
||||
// IPv6 may not be available on OS and the parsing
|
||||
@@ -1478,14 +1481,10 @@ void Session::configurePeerClasses()
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::tcp_socket, libt::session::tcp_peer_class_id);
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_tcp_socket, libt::session::tcp_peer_class_id);
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::i2p_socket, libt::session::tcp_peer_class_id);
|
||||
if (isUTPRateLimited()) {
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::utp_socket
|
||||
, libt::session::local_peer_class_id);
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::utp_socket
|
||||
if (!isUTPRateLimited()) {
|
||||
peerClassTypeFilter.disallow(libt::peer_class_type_filter::utp_socket
|
||||
, libt::session::global_peer_class_id);
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_utp_socket
|
||||
, libt::session::local_peer_class_id);
|
||||
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_utp_socket
|
||||
peerClassTypeFilter.disallow(libt::peer_class_type_filter::ssl_utp_socket
|
||||
, libt::session::global_peer_class_id);
|
||||
}
|
||||
m_nativeSession->set_peer_class_type_filter(peerClassTypeFilter);
|
||||
|
@@ -59,8 +59,6 @@ using namespace BitTorrent;
|
||||
|
||||
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
|
||||
: QThread(parent)
|
||||
, m_private(false)
|
||||
, m_pieceSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,97 +68,91 @@ TorrentCreatorThread::~TorrentCreatorThread()
|
||||
wait();
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
|
||||
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize)
|
||||
void TorrentCreatorThread::create(const TorrentCreatorParams ¶ms)
|
||||
{
|
||||
m_inputPath = Utils::Fs::fromNativePath(inputPath);
|
||||
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||
if (QFile(m_savePath).exists())
|
||||
Utils::Fs::forceRemove(m_savePath);
|
||||
m_trackers = trackers;
|
||||
m_urlSeeds = urlSeeds;
|
||||
m_comment = comment;
|
||||
m_private = isPrivate;
|
||||
m_pieceSize = pieceSize;
|
||||
|
||||
m_params = params;
|
||||
start();
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::sendProgressSignal(int numHashes, int numPieces)
|
||||
void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPieces)
|
||||
{
|
||||
emit updateProgress(static_cast<int>((numHashes * 100.) / numPieces));
|
||||
emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::run()
|
||||
{
|
||||
const QString creatorStr("qBittorrent " QBT_VERSION);
|
||||
|
||||
emit updateProgress(0);
|
||||
|
||||
QString creator_str("qBittorrent " QBT_VERSION);
|
||||
try {
|
||||
libt::file_storage fs;
|
||||
// Adding files to the torrent
|
||||
libt::add_files(fs, Utils::Fs::toNativePath(m_inputPath).toStdString(), fileFilter);
|
||||
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
|
||||
libt::create_torrent t(fs, m_pieceSize);
|
||||
libt::create_torrent newTorrent(fs, m_params.pieceSize);
|
||||
|
||||
// Add url seeds
|
||||
foreach (const QString &seed, m_urlSeeds)
|
||||
t.add_url_seed(seed.trimmed().toStdString());
|
||||
foreach (QString seed, m_params.urlSeeds) {
|
||||
seed = seed.trimmed();
|
||||
if (!seed.isEmpty())
|
||||
newTorrent.add_url_seed(seed.toStdString());
|
||||
}
|
||||
|
||||
int tier = 0;
|
||||
bool newline = false;
|
||||
foreach (const QString &tracker, m_trackers) {
|
||||
if (tracker.isEmpty()) {
|
||||
if (newline)
|
||||
continue;
|
||||
foreach (const QString &tracker, m_params.trackers) {
|
||||
if (tracker.isEmpty())
|
||||
++tier;
|
||||
newline = true;
|
||||
continue;
|
||||
}
|
||||
t.add_tracker(tracker.trimmed().toStdString(), tier);
|
||||
newline = false;
|
||||
else
|
||||
newTorrent.add_tracker(tracker.trimmed().toStdString(), tier);
|
||||
}
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
|
||||
// calculate the hash for all pieces
|
||||
const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/";
|
||||
libt::set_piece_hashes(t, Utils::Fs::toNativePath(parentPath).toStdString(), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces()));
|
||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
|
||||
libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
|
||||
, [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
|
||||
// Set qBittorrent as creator and add user comment to
|
||||
// torrent_info structure
|
||||
t.set_creator(creator_str.toUtf8().constData());
|
||||
t.set_comment(m_comment.toUtf8().constData());
|
||||
newTorrent.set_creator(creatorStr.toUtf8().constData());
|
||||
newTorrent.set_comment(m_params.comment.toUtf8().constData());
|
||||
// Is private ?
|
||||
t.set_priv(m_private);
|
||||
newTorrent.set_priv(m_params.isPrivate);
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
|
||||
// create the torrent and print it to out
|
||||
qDebug("Saving to %s", qUtf8Printable(m_savePath));
|
||||
libt::entry entry = newTorrent.generate();
|
||||
|
||||
// add source field
|
||||
if (!m_params.source.isEmpty())
|
||||
entry["info"]["source"] = m_params.source.toStdString();
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
|
||||
// create the torrent
|
||||
std::ofstream outfile(
|
||||
#ifdef _MSC_VER
|
||||
wchar_t *savePathW = new wchar_t[m_savePath.length() + 1];
|
||||
int len = Utils::Fs::toNativePath(m_savePath).toWCharArray(savePathW);
|
||||
savePathW[len] = L'\0';
|
||||
std::ofstream outfile(savePathW, std::ios_base::out | std::ios_base::binary);
|
||||
delete[] savePathW;
|
||||
Utils::Fs::toNativePath(m_params.savePath).toStdWString().c_str()
|
||||
#else
|
||||
std::ofstream outfile(Utils::Fs::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary);
|
||||
Utils::Fs::toNativePath(m_params.savePath).toUtf8().constData()
|
||||
#endif
|
||||
, (std::ios_base::out | std::ios_base::binary | std::ios_base::trunc));
|
||||
if (outfile.fail())
|
||||
throw std::exception();
|
||||
throw std::runtime_error(tr("create new torrent file failed").toStdString());
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
|
||||
libt::bencode(std::ostream_iterator<char>(outfile), t.generate());
|
||||
libt::bencode(std::ostream_iterator<char>(outfile), entry);
|
||||
outfile.close();
|
||||
|
||||
emit updateProgress(100);
|
||||
emit creationSuccess(m_savePath, parentPath);
|
||||
emit creationSuccess(m_params.savePath, parentPath);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
emit creationFailure(QString::fromStdString(e.what()));
|
||||
catch (const std::exception &e) {
|
||||
emit creationFailure(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -36,16 +36,27 @@
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
struct TorrentCreatorParams
|
||||
{
|
||||
bool isPrivate;
|
||||
int pieceSize;
|
||||
QString inputPath;
|
||||
QString savePath;
|
||||
QString comment;
|
||||
QString source;
|
||||
QStringList trackers;
|
||||
QStringList urlSeeds;
|
||||
};
|
||||
|
||||
class TorrentCreatorThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TorrentCreatorThread(QObject *parent = 0);
|
||||
TorrentCreatorThread(QObject *parent = nullptr);
|
||||
~TorrentCreatorThread();
|
||||
|
||||
void create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
|
||||
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize);
|
||||
void create(const TorrentCreatorParams ¶ms);
|
||||
|
||||
static int calculateTotalPieces(const QString &inputPath, const int pieceSize);
|
||||
|
||||
@@ -58,15 +69,9 @@ namespace BitTorrent
|
||||
void updateProgress(int progress);
|
||||
|
||||
private:
|
||||
void sendProgressSignal(int numHashes, int numPieces);
|
||||
void sendProgressSignal(int currentPieceIdx, int totalPieces);
|
||||
|
||||
QString m_inputPath;
|
||||
QString m_savePath;
|
||||
QStringList m_trackers;
|
||||
QStringList m_urlSeeds;
|
||||
QString m_comment;
|
||||
bool m_private;
|
||||
int m_pieceSize;
|
||||
TorrentCreatorParams m_params;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -156,6 +156,8 @@ const int TorrentHandle::MAX_SEEDING_TIME = 525600;
|
||||
// The following can be removed after one or two libtorrent releases on each branch.
|
||||
namespace
|
||||
{
|
||||
const char i18nContext[] = "TorrentHandle";
|
||||
|
||||
// new constructor is available
|
||||
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
|
||||
T makeTorrentCreator(const libtorrent::torrent_info & ti)
|
||||
@@ -1469,7 +1471,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::instance()->addMessage(tr("Could not move torrent: '%1'. Reason: %2")
|
||||
LogMsg(QCoreApplication::translate(i18nContext, "Could not move torrent: '%1'. Reason: %2")
|
||||
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
|
||||
|
||||
m_moveStorageInfo.newPath.clear();
|
||||
@@ -1647,13 +1649,13 @@ void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejecte
|
||||
updateStatus();
|
||||
if (p->error.value() == libt::errors::mismatching_file_size) {
|
||||
// Mismatching file size (files were probably moved)
|
||||
logger->addMessage(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
||||
logger->addMessage(QCoreApplication::translate(i18nContext, "File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
||||
m_hasMissingFiles = true;
|
||||
if (!isPaused())
|
||||
pause();
|
||||
}
|
||||
else {
|
||||
logger->addMessage(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
||||
logger->addMessage(QCoreApplication::translate(i18nContext, "Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
||||
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
@@ -59,6 +59,7 @@ namespace Http
|
||||
const char CONTENT_TYPE_JSON[] = "application/json";
|
||||
const char CONTENT_TYPE_PNG[] = "image/png";
|
||||
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
|
||||
const char CONTENT_TYPE_SVG[] = "image/svg+xml";
|
||||
|
||||
// portability: "\r\n" doesn't guarantee mapping to the correct value
|
||||
const char CRLF[] = {0x0D, 0x0A, '\0'};
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QLocale>
|
||||
#include <QMutableListIterator>
|
||||
#include <QPair>
|
||||
#include <QSettings>
|
||||
|
||||
@@ -487,13 +488,17 @@ QList<Utils::Net::Subnet> Preferences::getWebUiAuthSubnetWhitelist() const
|
||||
return subnets;
|
||||
}
|
||||
|
||||
void Preferences::setWebUiAuthSubnetWhitelist(const QList<Utils::Net::Subnet> &subnets)
|
||||
void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets)
|
||||
{
|
||||
QStringList subnetsStringList;
|
||||
for (const Utils::Net::Subnet &subnet : subnets)
|
||||
subnetsStringList.append(Utils::Net::subnetToString(subnet));
|
||||
QMutableListIterator<QString> i(subnets);
|
||||
while (i.hasNext()) {
|
||||
bool ok = false;
|
||||
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(i.next().trimmed(), &ok);
|
||||
if (!ok)
|
||||
i.remove();
|
||||
}
|
||||
|
||||
setValue("Preferences/WebUI/AuthSubnetWhitelist", subnetsStringList);
|
||||
setValue("Preferences/WebUI/AuthSubnetWhitelist", subnets);
|
||||
}
|
||||
|
||||
QString Preferences::getServerDomains() const
|
||||
@@ -1216,9 +1221,9 @@ void Preferences::setMainLastDir(const QString &path)
|
||||
setValue("MainWindowLastDir", path);
|
||||
}
|
||||
|
||||
QSize Preferences::getPrefSize(const QSize &defaultSize) const
|
||||
QSize Preferences::getPrefSize() const
|
||||
{
|
||||
return value("Preferences/State/size", defaultSize).toSize();
|
||||
return value("Preferences/State/size").toSize();
|
||||
}
|
||||
|
||||
void Preferences::setPrefSize(const QSize &size)
|
||||
@@ -1296,9 +1301,9 @@ void Preferences::setPropTrackerListState(const QByteArray &state)
|
||||
setValue("TorrentProperties/Trackers/qt5/TrackerListState", state);
|
||||
}
|
||||
|
||||
QSize Preferences::getRssGeometrySize(const QSize &defaultSize) const
|
||||
QSize Preferences::getRssGeometrySize() const
|
||||
{
|
||||
return value("RssFeedDownloader/geometrySize", defaultSize).toSize();
|
||||
return value("RssFeedDownloader/geometrySize").toSize();
|
||||
}
|
||||
|
||||
void Preferences::setRssGeometrySize(const QSize &geometry)
|
||||
|
@@ -191,7 +191,7 @@ public:
|
||||
bool isWebUiAuthSubnetWhitelistEnabled() const;
|
||||
void setWebUiAuthSubnetWhitelistEnabled(bool enabled);
|
||||
QList<Utils::Net::Subnet> getWebUiAuthSubnetWhitelist() const;
|
||||
void setWebUiAuthSubnetWhitelist(const QList<Utils::Net::Subnet> &subnets);
|
||||
void setWebUiAuthSubnetWhitelist(QStringList subnets);
|
||||
QString getWebUiUsername() const;
|
||||
void setWebUiUsername(const QString &username);
|
||||
QString getWebUiPassword() const;
|
||||
@@ -301,7 +301,7 @@ public:
|
||||
void setMainVSplitterState(const QByteArray &state);
|
||||
QString getMainLastDir() const;
|
||||
void setMainLastDir(const QString &path);
|
||||
QSize getPrefSize(const QSize &defaultSize) const;
|
||||
QSize getPrefSize() const;
|
||||
void setPrefSize(const QSize &size);
|
||||
QStringList getPrefHSplitterSizes() const;
|
||||
void setPrefHSplitterSizes(const QStringList &sizes);
|
||||
@@ -317,7 +317,7 @@ public:
|
||||
void setPropVisible(const bool visible);
|
||||
QByteArray getPropTrackerListState() const;
|
||||
void setPropTrackerListState(const QByteArray &state);
|
||||
QSize getRssGeometrySize(const QSize &defaultSize) const;
|
||||
QSize getRssGeometrySize() const;
|
||||
void setRssGeometrySize(const QSize &geometry);
|
||||
QByteArray getRssHSplitterSizes() const;
|
||||
void setRssHSplitterSizes(const QByteArray &sizes);
|
||||
|
@@ -227,10 +227,9 @@ void Parser::parse(const QByteArray &feedData)
|
||||
// read and create items from a rss document
|
||||
void Parser::parse_impl(const QByteArray &feedData)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
QXmlStreamReader xml(feedData);
|
||||
bool foundChannel = false;
|
||||
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "rss") {
|
||||
// Find channels
|
||||
@@ -258,11 +257,15 @@ void Parser::parse_impl(const QByteArray &feedData)
|
||||
}
|
||||
}
|
||||
|
||||
if (xml.hasError())
|
||||
m_result.error = xml.errorString();
|
||||
else if (!foundChannel)
|
||||
if (!foundChannel) {
|
||||
m_result.error = tr("Invalid RSS feed.");
|
||||
else
|
||||
}
|
||||
else if (xml.hasError()) {
|
||||
m_result.error = tr("%1 (line: %2, column: %3, offset: %4).")
|
||||
.arg(xml.errorString()).arg(xml.lineNumber())
|
||||
.arg(xml.columnNumber()).arg(xml.characterOffset());
|
||||
}
|
||||
else {
|
||||
// Sort article list chronologically
|
||||
// NOTE: We don't need to sort it here if articles are always
|
||||
// sorted in fetched XML in reverse chronological order
|
||||
@@ -271,6 +274,7 @@ void Parser::parse_impl(const QByteArray &feedData)
|
||||
{
|
||||
return a1["date"].toDateTime() < a2["date"].toDateTime();
|
||||
});
|
||||
}
|
||||
|
||||
emit finished(m_result);
|
||||
m_result.articles.clear(); // clear articles only
|
||||
@@ -288,35 +292,34 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
|
||||
break;
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
const QString text(xml.readElementText().trimmed());
|
||||
|
||||
if (name == QLatin1String("title")) {
|
||||
article[Article::KeyTitle] = text;
|
||||
article[Article::KeyTitle] = xml.readElementText().trimmed();
|
||||
}
|
||||
else if (name == QLatin1String("enclosure")) {
|
||||
if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent"))
|
||||
article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString();
|
||||
}
|
||||
else if (name == QLatin1String("link")) {
|
||||
const QString text {xml.readElementText().trimmed()};
|
||||
if (text.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
|
||||
article[Article::KeyTorrentURL] = text; // magnet link instead of a news URL
|
||||
else
|
||||
article[Article::KeyLink] = text;
|
||||
}
|
||||
else if (name == QLatin1String("description")) {
|
||||
article[Article::KeyDescription] = text;
|
||||
article[Article::KeyDescription] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
}
|
||||
else if (name == QLatin1String("pubDate")) {
|
||||
article[Article::KeyDate] = parseDate(text);
|
||||
article[Article::KeyDate] = parseDate(xml.readElementText().trimmed());
|
||||
}
|
||||
else if (name == QLatin1String("author")) {
|
||||
article[Article::KeyAuthor] = text;
|
||||
article[Article::KeyAuthor] = xml.readElementText().trimmed();
|
||||
}
|
||||
else if (name == QLatin1String("guid")) {
|
||||
article[Article::KeyId] = text;
|
||||
article[Article::KeyId] = xml.readElementText().trimmed();
|
||||
}
|
||||
else {
|
||||
article[name] = text;
|
||||
article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,17 +329,14 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
|
||||
|
||||
void Parser::parseRSSChannel(QXmlStreamReader &xml)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
Q_ASSERT(xml.isStartElement() && xml.name() == "channel");
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name() == "title") {
|
||||
if (xml.name() == QLatin1String("title")) {
|
||||
m_result.title = xml.readElementText();
|
||||
}
|
||||
else if (xml.name() == "lastBuildDate") {
|
||||
else if (xml.name() == QLatin1String("lastBuildDate")) {
|
||||
QString lastBuildDate = xml.readElementText();
|
||||
if (!lastBuildDate.isEmpty()) {
|
||||
if (m_result.lastBuildDate == lastBuildDate) {
|
||||
@@ -346,7 +346,7 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml)
|
||||
m_result.lastBuildDate = lastBuildDate;
|
||||
}
|
||||
}
|
||||
else if (xml.name() == "item") {
|
||||
else if (xml.name() == QLatin1String("item")) {
|
||||
parseRssArticle(xml);
|
||||
}
|
||||
}
|
||||
@@ -366,14 +366,12 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
|
||||
break;
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
const QString text(xml.readElementText().trimmed());
|
||||
|
||||
if (name == QLatin1String("title")) {
|
||||
article[Article::KeyTitle] = text;
|
||||
article[Article::KeyTitle] = xml.readElementText().trimmed();
|
||||
}
|
||||
else if (name == QLatin1String("link")) {
|
||||
QString link = (xml.attributes().isEmpty()
|
||||
? text
|
||||
? xml.readElementText().trimmed()
|
||||
: xml.attributes().value(QLatin1String("href")).toString());
|
||||
|
||||
if (link.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
|
||||
@@ -385,42 +383,38 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
|
||||
article[Article::KeyLink] = (m_baseUrl.isEmpty() ? link : m_baseUrl + link);
|
||||
|
||||
}
|
||||
else if ((name == QLatin1String("summary")) || (name == QLatin1String("content"))){
|
||||
else if ((name == QLatin1String("summary")) || (name == QLatin1String("content"))) {
|
||||
if (doubleContent) { // Duplicate content -> ignore
|
||||
xml.readNext();
|
||||
|
||||
while ((xml.name() != QLatin1String("summary")) && (xml.name() != QLatin1String("content")))
|
||||
xml.readNext();
|
||||
|
||||
xml.skipCurrentElement();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to also parse broken articles, which don't use html '&' escapes
|
||||
// Actually works great for non-broken content too
|
||||
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
if (!feedText.isEmpty())
|
||||
article[Article::KeyDescription] = feedText.trimmed();
|
||||
|
||||
doubleContent = true;
|
||||
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements).trimmed();
|
||||
if (!feedText.isEmpty()) {
|
||||
article[Article::KeyDescription] = feedText;
|
||||
doubleContent = true;
|
||||
}
|
||||
}
|
||||
else if (name == QLatin1String("updated")) {
|
||||
// ATOM uses standard compliant date, don't do fancy stuff
|
||||
QDateTime articleDate = QDateTime::fromString(text, Qt::ISODate);
|
||||
QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate);
|
||||
article[Article::KeyDate] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime());
|
||||
}
|
||||
else if (name == QLatin1String("author")) {
|
||||
xml.readNext();
|
||||
while (xml.name() != QLatin1String("author")) {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == QLatin1String("name"))
|
||||
article[Article::KeyAuthor] = xml.readElementText().trimmed();
|
||||
xml.readNext();
|
||||
else
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
else if (name == QLatin1String("id")) {
|
||||
article[Article::KeyId] = text;
|
||||
article[Article::KeyId] = xml.readElementText().trimmed();
|
||||
}
|
||||
else {
|
||||
article[name] = text;
|
||||
article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -430,19 +424,16 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
|
||||
|
||||
void Parser::parseAtomChannel(QXmlStreamReader &xml)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
Q_ASSERT(xml.isStartElement() && xml.name() == "feed");
|
||||
|
||||
m_baseUrl = xml.attributes().value("xml:base").toString();
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name() == "title") {
|
||||
if (xml.name() == QLatin1String("title")) {
|
||||
m_result.title = xml.readElementText();
|
||||
}
|
||||
else if (xml.name() == "updated") {
|
||||
else if (xml.name() == QLatin1String("updated")) {
|
||||
QString lastBuildDate = xml.readElementText();
|
||||
if (!lastBuildDate.isEmpty()) {
|
||||
if (m_result.lastBuildDate == lastBuildDate) {
|
||||
@@ -452,7 +443,7 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
|
||||
m_result.lastBuildDate = lastBuildDate;
|
||||
}
|
||||
}
|
||||
else if (xml.name() == "entry") {
|
||||
else if (xml.name() == QLatin1String("entry")) {
|
||||
parseAtomArticle(xml);
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "rss_autodownloader.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -37,6 +38,7 @@
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "../bittorrent/magneturi.h"
|
||||
#include "../bittorrent/session.h"
|
||||
@@ -63,6 +65,32 @@ const QString RulesFileName(QStringLiteral("download_rules.json"));
|
||||
|
||||
const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing"));
|
||||
|
||||
namespace
|
||||
{
|
||||
QVector<RSS::AutoDownloadRule> rulesFromJSON(const QByteArray &jsonData)
|
||||
{
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
throw RSS::ParsingError(jsonError.errorString());
|
||||
|
||||
if (!jsonDoc.isObject())
|
||||
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
|
||||
|
||||
const QJsonObject jsonObj {jsonDoc.object()};
|
||||
QVector<RSS::AutoDownloadRule> rules;
|
||||
for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it) {
|
||||
const QJsonValue jsonVal {it.value()};
|
||||
if (!jsonVal.isObject())
|
||||
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
|
||||
|
||||
rules.append(RSS::AutoDownloadRule::fromJsonObject(jsonVal.toObject(), it.key()));
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace RSS;
|
||||
|
||||
QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr;
|
||||
@@ -84,8 +112,8 @@ AutoDownloader::AutoDownloader()
|
||||
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_fileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString)
|
||||
{
|
||||
Logger::instance()->addMessage(QString("Couldn't save RSS AutoDownloader data in %1. Error: %2")
|
||||
.arg(fileName).arg(errorString), Log::WARNING);
|
||||
LogMsg(tr("Couldn't save RSS AutoDownloader data in %1. Error: %2")
|
||||
.arg(fileName).arg(errorString), Log::CRITICAL);
|
||||
});
|
||||
|
||||
m_ioThread->start();
|
||||
@@ -174,6 +202,70 @@ void AutoDownloader::removeRule(const QString &ruleName)
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray AutoDownloader::exportRules(AutoDownloader::RulesFileFormat format) const
|
||||
{
|
||||
switch (format) {
|
||||
case RulesFileFormat::Legacy:
|
||||
return exportRulesToLegacyFormat();
|
||||
default:
|
||||
return exportRulesToJSONFormat();
|
||||
}
|
||||
}
|
||||
|
||||
void AutoDownloader::importRules(const QByteArray &data, AutoDownloader::RulesFileFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case RulesFileFormat::Legacy:
|
||||
importRulesFromLegacyFormat(data);
|
||||
break;
|
||||
default:
|
||||
importRulesFromJSONFormat(data);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray AutoDownloader::exportRulesToJSONFormat() const
|
||||
{
|
||||
QJsonObject jsonObj;
|
||||
for (const auto &rule : rules())
|
||||
jsonObj.insert(rule.name(), rule.toJsonObject());
|
||||
|
||||
return QJsonDocument(jsonObj).toJson();
|
||||
}
|
||||
|
||||
void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data)
|
||||
{
|
||||
const auto rules = rulesFromJSON(data);
|
||||
for (const auto &rule : rules)
|
||||
insertRule(rule);
|
||||
}
|
||||
|
||||
QByteArray AutoDownloader::exportRulesToLegacyFormat() const
|
||||
{
|
||||
QVariantHash dict;
|
||||
for (const auto &rule : rules())
|
||||
dict[rule.name()] = rule.toLegacyDict();
|
||||
|
||||
QByteArray data;
|
||||
QDataStream out(&data, QIODevice::WriteOnly);
|
||||
out.setVersion(QDataStream::Qt_4_5);
|
||||
out << dict;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
|
||||
{
|
||||
QDataStream in(data);
|
||||
in.setVersion(QDataStream::Qt_4_5);
|
||||
QVariantHash dict;
|
||||
in >> dict;
|
||||
if (in.status() != QDataStream::Ok)
|
||||
throw ParsingError(tr("Invalid data format"));
|
||||
|
||||
for (const QVariant &val : dict)
|
||||
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
|
||||
}
|
||||
|
||||
void AutoDownloader::process()
|
||||
{
|
||||
if (m_processingQueue.isEmpty()) return; // processing was disabled
|
||||
@@ -276,39 +368,20 @@ void AutoDownloader::load()
|
||||
else if (rulesFile.open(QFile::ReadOnly))
|
||||
loadRules(rulesFile.readAll());
|
||||
else
|
||||
Logger::instance()->addMessage(
|
||||
QString("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
|
||||
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
|
||||
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::CRITICAL);
|
||||
}
|
||||
|
||||
void AutoDownloader::loadRules(const QByteArray &data)
|
||||
{
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
Logger::instance()->addMessage(
|
||||
QString("Couldn't parse RSS AutoDownloader rules. Error: %1")
|
||||
.arg(jsonError.errorString()), Log::WARNING);
|
||||
return;
|
||||
try {
|
||||
const auto rules = rulesFromJSON(data);
|
||||
for (const auto &rule : rules)
|
||||
setRule_impl(rule);
|
||||
}
|
||||
|
||||
if (!jsonDoc.isObject()) {
|
||||
Logger::instance()->addMessage(
|
||||
QString("Couldn't load RSS AutoDownloader rules. Invalid data format."), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
foreach (const QString &key, jsonObj.keys()) {
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
if (!jsonVal.isObject()) {
|
||||
Logger::instance()->addMessage(
|
||||
QString("Couldn't load RSS AutoDownloader rule '%1'. Invalid data format.")
|
||||
.arg(key), Log::WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
setRule_impl(AutoDownloadRule::fromJsonObject(jsonVal.toObject(), key));
|
||||
catch (const ParsingError &error) {
|
||||
LogMsg(tr("Couldn't load RSS AutoDownloader rules. Reason: %1")
|
||||
.arg(error.message()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +390,7 @@ void AutoDownloader::loadRulesLegacy()
|
||||
SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss"));
|
||||
QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash();
|
||||
foreach (const QVariant &ruleVar, rules) {
|
||||
auto rule = AutoDownloadRule::fromVariantHash(ruleVar.toHash());
|
||||
auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
|
||||
if (!rule.name().isEmpty())
|
||||
insertRule(rule);
|
||||
}
|
||||
@@ -351,6 +424,8 @@ bool AutoDownloader::isProcessingEnabled() const
|
||||
void AutoDownloader::resetProcessingQueue()
|
||||
{
|
||||
m_processingQueue.clear();
|
||||
if (!m_processingEnabled) return;
|
||||
|
||||
foreach (Article *article, Session::instance()->rootFolder()->articles()) {
|
||||
if (!article->isRead() && !article->torrentUrl().isEmpty())
|
||||
addJobForArticle(article);
|
||||
@@ -385,3 +460,13 @@ 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,6 +28,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QBasicTimer>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
@@ -49,6 +51,13 @@ namespace RSS
|
||||
|
||||
class AutoDownloadRule;
|
||||
|
||||
class ParsingError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit ParsingError(const QString &message);
|
||||
QString message() const;
|
||||
};
|
||||
|
||||
class AutoDownloader final: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -60,6 +69,12 @@ namespace RSS
|
||||
~AutoDownloader() override;
|
||||
|
||||
public:
|
||||
enum class RulesFileFormat
|
||||
{
|
||||
Legacy,
|
||||
JSON
|
||||
};
|
||||
|
||||
static AutoDownloader *instance();
|
||||
|
||||
bool isProcessingEnabled() const;
|
||||
@@ -73,6 +88,9 @@ namespace RSS
|
||||
bool renameRule(const QString &ruleName, const QString &newRuleName);
|
||||
void removeRule(const QString &ruleName);
|
||||
|
||||
QByteArray exportRules(RulesFileFormat format = RulesFileFormat::JSON) const;
|
||||
void importRules(const QByteArray &data, RulesFileFormat format = RulesFileFormat::JSON);
|
||||
|
||||
signals:
|
||||
void processingStateChanged(bool enabled);
|
||||
void ruleAdded(const QString &ruleName);
|
||||
@@ -98,6 +116,10 @@ namespace RSS
|
||||
void loadRulesLegacy();
|
||||
void store();
|
||||
void storeDeferred();
|
||||
QByteArray exportRulesToJSONFormat() const;
|
||||
void importRulesFromJSONFormat(const QByteArray &data);
|
||||
QByteArray exportRulesToLegacyFormat() const;
|
||||
void importRulesFromLegacyFormat(const QByteArray &data);
|
||||
|
||||
static QPointer<AutoDownloader> m_instance;
|
||||
|
||||
|
@@ -63,11 +63,29 @@ namespace
|
||||
QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool)
|
||||
{
|
||||
switch (static_cast<int>(triStateBool)) {
|
||||
case 0: return false; break;
|
||||
case 1: return true; break;
|
||||
case 0: return false;
|
||||
case 1: return true;
|
||||
default: return QJsonValue();
|
||||
}
|
||||
}
|
||||
|
||||
TriStateBool addPausedLegacyToTriStateBool(int val)
|
||||
{
|
||||
switch (val) {
|
||||
case 1: return TriStateBool::True; // always
|
||||
case 2: return TriStateBool::False; // never
|
||||
default: return TriStateBool::Undefined; // default
|
||||
}
|
||||
}
|
||||
|
||||
int triStateBoolToAddPausedLegacy(const TriStateBool &triStateBool)
|
||||
{
|
||||
switch (static_cast<int>(triStateBool)) {
|
||||
case 0: return 2; // never
|
||||
case 1: return 1; // always
|
||||
default: return 0; // default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QString Str_Name(QStringLiteral("name"));
|
||||
@@ -378,21 +396,37 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
|
||||
return rule;
|
||||
}
|
||||
|
||||
AutoDownloadRule AutoDownloadRule::fromVariantHash(const QVariantHash &varHash)
|
||||
QVariantHash AutoDownloadRule::toLegacyDict() const
|
||||
{
|
||||
AutoDownloadRule rule(varHash.value("name").toString());
|
||||
return {{"name", name()},
|
||||
{"must_contain", mustContain()},
|
||||
{"must_not_contain", mustNotContain()},
|
||||
{"save_path", savePath()},
|
||||
{"affected_feeds", feedURLs()},
|
||||
{"enabled", isEnabled()},
|
||||
{"category_assigned", assignedCategory()},
|
||||
{"use_regex", useRegex()},
|
||||
{"add_paused", triStateBoolToAddPausedLegacy(addPaused())},
|
||||
{"episode_filter", episodeFilter()},
|
||||
{"last_match", lastMatch()},
|
||||
{"ignore_days", ignoreDays()}};
|
||||
}
|
||||
|
||||
rule.setUseRegex(varHash.value("use_regex", false).toBool());
|
||||
rule.setMustContain(varHash.value("must_contain").toString());
|
||||
rule.setMustNotContain(varHash.value("must_not_contain").toString());
|
||||
rule.setEpisodeFilter(varHash.value("episode_filter").toString());
|
||||
rule.setFeedURLs(varHash.value("affected_feeds").toStringList());
|
||||
rule.setEnabled(varHash.value("enabled", false).toBool());
|
||||
rule.setSavePath(varHash.value("save_path").toString());
|
||||
rule.setCategory(varHash.value("category_assigned").toString());
|
||||
rule.setAddPaused(TriStateBool(varHash.value("add_paused").toInt() - 1));
|
||||
rule.setLastMatch(varHash.value("last_match").toDateTime());
|
||||
rule.setIgnoreDays(varHash.value("ignore_days").toInt());
|
||||
AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
|
||||
{
|
||||
AutoDownloadRule rule(dict.value("name").toString());
|
||||
|
||||
rule.setUseRegex(dict.value("use_regex", false).toBool());
|
||||
rule.setMustContain(dict.value("must_contain").toString());
|
||||
rule.setMustNotContain(dict.value("must_not_contain").toString());
|
||||
rule.setEpisodeFilter(dict.value("episode_filter").toString());
|
||||
rule.setFeedURLs(dict.value("affected_feeds").toStringList());
|
||||
rule.setEnabled(dict.value("enabled", false).toBool());
|
||||
rule.setSavePath(dict.value("save_path").toString());
|
||||
rule.setCategory(dict.value("category_assigned").toString());
|
||||
rule.setAddPaused(addPausedLegacyToTriStateBool(dict.value("add_paused").toInt()));
|
||||
rule.setLastMatch(dict.value("last_match").toDateTime());
|
||||
rule.setIgnoreDays(dict.value("ignore_days").toInt());
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
@@ -84,7 +84,9 @@ namespace RSS
|
||||
|
||||
QJsonObject toJsonObject() const;
|
||||
static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = "");
|
||||
static AutoDownloadRule fromVariantHash(const QVariantHash &varHash);
|
||||
|
||||
QVariantHash toLegacyDict() const;
|
||||
static AutoDownloadRule fromLegacyDict(const QVariantHash &dict);
|
||||
|
||||
private:
|
||||
bool matches(const QString &articleTitle, const QString &expression) const;
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include "rss_feed.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user