mirror of
https://github.com/kiwix/libkiwix.git
synced 2026-01-02 03:18:04 -05:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d55976271f | ||
|
|
5eeccbae21 | ||
|
|
77770e1bd4 | ||
|
|
654a8e304c | ||
|
|
ed7a8343fa | ||
|
|
5adf7891cc | ||
|
|
93c404a952 | ||
|
|
f33f4eb381 | ||
|
|
4bece7017f | ||
|
|
cab8eec00b | ||
|
|
f41155e060 | ||
|
|
c17abdae5e | ||
|
|
8164b4dadc | ||
|
|
31c9375a3a | ||
|
|
21592af8b2 | ||
|
|
15b3ed24b7 | ||
|
|
3d689e790b | ||
|
|
e740a511c6 | ||
|
|
87f5b56b72 | ||
|
|
cfdd634c3c | ||
|
|
df76db4f47 | ||
|
|
15f7eaa400 | ||
|
|
6fe6f88b10 | ||
|
|
687a15877a | ||
|
|
4e746916a7 | ||
|
|
7c1d051305 | ||
|
|
5dc96d7145 | ||
|
|
3721d7439d | ||
|
|
701829ae11 | ||
|
|
91a0e100cc | ||
|
|
1f672056a9 | ||
|
|
48347825a9 | ||
|
|
d0d7e11093 | ||
|
|
519dd110f5 | ||
|
|
568b2b0e0c | ||
|
|
491b6d655b | ||
|
|
ec8f1ffe9c | ||
|
|
12ffad55f7 | ||
|
|
8698407e1e | ||
|
|
0b2c9fa7ce | ||
|
|
f93c31754a | ||
|
|
fe682f855a |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.idea/
|
||||
@@ -1,6 +1,6 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
dist: xenial
|
||||
sudo: true
|
||||
cache: ccache
|
||||
before_install:
|
||||
- PATH=$PATH:$HOME/bin
|
||||
|
||||
17
ChangeLog
17
ChangeLog
@@ -1,3 +1,20 @@
|
||||
kiwix-lib 5.2.0
|
||||
===============
|
||||
|
||||
* kiwix-serve integration (as a seperated process).
|
||||
* Fix crash in the suggestion search.
|
||||
* Better API to filter the library books.
|
||||
* New kiwix-lib application for android. (.aar)
|
||||
* Use ReLinker to link with libkiwix.so in android.
|
||||
* Correctly set the verbosity of zim search.
|
||||
|
||||
kiwix-lib 5.1.0
|
||||
===============
|
||||
|
||||
* Add function to pause, resume and stop downloads.
|
||||
* Add zim's tags in the opds stream.
|
||||
* Addapt to new libzim 5.0.0 API.
|
||||
|
||||
kiwix-lib 5.0.0
|
||||
===============
|
||||
|
||||
|
||||
46
README.md
46
README.md
@@ -27,17 +27,13 @@ The Kiwix library relies on many third parts software libraries. They
|
||||
are prerequisites to the Kiwix library compilation. Following
|
||||
libraries need to be available:
|
||||
|
||||
* ICU ................................... http://site.icu-project.org/
|
||||
(package libicu-dev on Ubuntu)
|
||||
* ZIM ........................................ http://www.openzim.org/
|
||||
(package libzim-dev on Ubuntu)
|
||||
* Pugixml ........................................ http://pugixml.org/
|
||||
(package libpugixml-dev on Ubuntu)
|
||||
* libaria2 .................................. https://aria2.github.io/
|
||||
(no package on Ubuntu)
|
||||
* Mustache ....................... https://github.com/kainjow/Mustache
|
||||
(Just copy the header mustache.hpp somewhere it can be found by the
|
||||
compiler and/or set CPPFLAGS with correct '-I' option)
|
||||
* [ICU](https://site.icu-project.org/) (package `libicu-dev` on Ubuntu)
|
||||
* [ZIM](https://openzim.org/) (package `libzim-dev` on Ubuntu)
|
||||
* [Pugixml](https://pugixml.org/) (package `libpugixml-dev` on Ubuntu)
|
||||
* [Aria2](https://aria2.github.io/) (package `aria2` on Ubuntu)
|
||||
* [Mustache](https://github.com/kainjow/Mustache) (Just copy the
|
||||
header `mustache.hpp` somewhere it can be found by the compiler and/or
|
||||
set CPPFLAGS with correct `-I` option)
|
||||
|
||||
These dependencies may or may not be packaged by your operating
|
||||
system. They may also be packaged but only in an older version. The
|
||||
@@ -46,13 +42,13 @@ In the worse case, you will have to download and compile bleeding edge
|
||||
version by hand.
|
||||
|
||||
If you want to install these dependencies locally, then use the
|
||||
kiwix-lib directory as install prefix.
|
||||
`kiwix-lib` directory as install prefix.
|
||||
|
||||
Environment
|
||||
-------------
|
||||
|
||||
The Kiwix library builds using [Meson](http://mesonbuild.com/) version
|
||||
0.39 or higher. Meson relies itself on Ninja, pkg-config and few other
|
||||
The Kiwix library builds using [Meson](https://mesonbuild.com/) version
|
||||
0.43 or higher. Meson relies itself on Ninja, pkg-config and few other
|
||||
compilation tools.
|
||||
|
||||
Install first the few common compilation tools:
|
||||
@@ -61,14 +57,16 @@ Install first the few common compilation tools:
|
||||
* Pkg-config
|
||||
|
||||
These tools should be packaged if you use a cutting edge operating
|
||||
system. If not, have a look to the "Troubleshooting" section.
|
||||
system. If not, have a look to the [Troubleshooting](#Troubleshooting)
|
||||
section.
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
Once all dependencies are installed, you can compile the Kiwix library
|
||||
with:
|
||||
```
|
||||
|
||||
```bash
|
||||
meson . build
|
||||
ninja -C build
|
||||
```
|
||||
@@ -86,31 +84,32 @@ Installation
|
||||
If you want to install the Kiwix library and the headers you just have
|
||||
compiled on your system, here we go:
|
||||
|
||||
```
|
||||
```bash
|
||||
ninja -C build install
|
||||
```
|
||||
|
||||
You might need to run the command as root (or using 'sudo'), depending
|
||||
You might need to run the command as root (or using `sudo`), depending
|
||||
where you want to install the libraries. After the installation
|
||||
succeeded, you may need to run ldconfig (as root).
|
||||
succeeded, you may need to run `ldconfig` (as root).
|
||||
|
||||
Uninstallation
|
||||
------------
|
||||
|
||||
If you want to uninstall the Kiwix library:
|
||||
|
||||
```
|
||||
```bash
|
||||
ninja -C build uninstall
|
||||
```
|
||||
|
||||
Like for the installation, you might need to run the command as root
|
||||
(or using 'sudo').
|
||||
(or using `sudo`).
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
If you need to install Meson "manually":
|
||||
```
|
||||
|
||||
```bash
|
||||
virtualenv -p python3 ./ # Create virtualenv
|
||||
source bin/activate # Activate the virtualenv
|
||||
pip3 install meson # Install Meson
|
||||
@@ -118,7 +117,8 @@ hash -r # Refresh bash paths
|
||||
```
|
||||
|
||||
If you need to install Ninja "manually":
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone git://github.com/ninja-build/ninja.git
|
||||
cd ninja
|
||||
git checkout release
|
||||
|
||||
13
android-kiwix-lib-publisher/.gitignore
vendored
Normal file
13
android-kiwix-lib-publisher/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
25
android-kiwix-lib-publisher/build.gradle
Normal file
25
android-kiwix-lib-publisher/build.gradle
Normal file
@@ -0,0 +1,25 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
15
android-kiwix-lib-publisher/gradle.properties
Normal file
15
android-kiwix-lib-publisher/gradle.properties
Normal file
@@ -0,0 +1,15 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
BIN
android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
android-kiwix-lib-publisher/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Wed Jun 19 15:28:39 BST 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||
172
android-kiwix-lib-publisher/gradlew
vendored
Normal file
172
android-kiwix-lib-publisher/gradlew
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
android-kiwix-lib-publisher/gradlew.bat
vendored
Normal file
84
android-kiwix-lib-publisher/gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
android-kiwix-lib-publisher/kiwixLibAndroid/.gitignore
vendored
Normal file
1
android-kiwix-lib-publisher/kiwixLibAndroid/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
21
android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
Normal file
21
android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 28
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.getkeepsafe.relinker:relinker:1.3.1'
|
||||
}
|
||||
21
android-kiwix-lib-publisher/kiwixLibAndroid/proguard-rules.pro
vendored
Normal file
21
android-kiwix-lib-publisher/kiwixLibAndroid/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="kiwix.org.kiwixlib">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:supportsRtl="true">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
1
android-kiwix-lib-publisher/settings.gradle
Normal file
1
android-kiwix-lib-publisher/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
include ':kiwixLibAndroid'
|
||||
@@ -54,6 +54,9 @@ class Download {
|
||||
m_status(K_UNKNOWN),
|
||||
m_did(did) {};
|
||||
void updateStatus(bool follow=false);
|
||||
void pauseDownload();
|
||||
void resumeDownload();
|
||||
void cancelDownload();
|
||||
StatusResult getStatus() { return m_status; }
|
||||
std::string getDid() { return m_did; }
|
||||
std::string getFollowedBy() { return m_followedBy; }
|
||||
|
||||
27
include/kiwixserve.h
Normal file
27
include/kiwixserve.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef KIWIXLIB_KIWIX_SERVE_H_
|
||||
#define KIWIXLIB_KIWIX_SERVE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Subprocess;
|
||||
namespace kiwix {
|
||||
|
||||
class KiwixServe
|
||||
{
|
||||
public:
|
||||
KiwixServe(int port = 8181);
|
||||
~KiwixServe();
|
||||
|
||||
void run();
|
||||
void shutDown();
|
||||
bool isRunning();
|
||||
int getPort() { return m_port; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Subprocess> mp_kiwixServe;
|
||||
int m_port;
|
||||
};
|
||||
|
||||
}; //end namespace kiwix
|
||||
|
||||
#endif // KIWIXLIB_KIWIX_SERVE_H_
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "book.h"
|
||||
#include "bookmark.h"
|
||||
#include "common.h"
|
||||
|
||||
#define KIWIX_LIBRARY_VERSION "20110515"
|
||||
|
||||
@@ -44,6 +45,65 @@ enum supportedListMode {
|
||||
VALID = 1 << 4,
|
||||
NOVALID = 1 << 5
|
||||
};
|
||||
|
||||
class Filter {
|
||||
private:
|
||||
uint64_t activeFilters;
|
||||
std::vector<std::string> _acceptTags;
|
||||
std::vector<std::string> _rejectTags;
|
||||
std::string _lang;
|
||||
std::string _publisher;
|
||||
std::string _creator;
|
||||
size_t _maxSize;
|
||||
std::string _query;
|
||||
|
||||
public:
|
||||
Filter();
|
||||
~Filter() = default;
|
||||
|
||||
/**
|
||||
* Set the filter to check local.
|
||||
*
|
||||
* A local book is a book with a path.
|
||||
* If accept is true, only local book are accepted.
|
||||
* If accept is false, only non local book are accepted.
|
||||
*/
|
||||
Filter& local(bool accept);
|
||||
|
||||
/**
|
||||
* Set the filter to check remote.
|
||||
*
|
||||
* A remote book is a book with a url.
|
||||
* If accept is true, only remote book are accepted.
|
||||
* If accept is false, only non remote book are accepted.
|
||||
*/
|
||||
Filter& remote(bool accept);
|
||||
|
||||
/**
|
||||
* Set the filter to check validity.
|
||||
*
|
||||
* A valid book is a book with a path pointing to a existing zim file.
|
||||
* If accept is true, only valid book are accepted.
|
||||
* If accept is false, only non valid book are accepted.
|
||||
*/
|
||||
Filter& valid(bool accept);
|
||||
|
||||
/**
|
||||
* Set the filter to only accept book with corresponding tag.
|
||||
*/
|
||||
Filter& acceptTags(std::vector<std::string> tags);
|
||||
Filter& rejectTags(std::vector<std::string> tags);
|
||||
|
||||
Filter& lang(std::string lang);
|
||||
Filter& publisher(std::string publisher);
|
||||
Filter& creator(std::string creator);
|
||||
Filter& maxSize(size_t size);
|
||||
Filter& query(std::string query);
|
||||
|
||||
bool accept(const Book& book) const;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A Library store several books.
|
||||
*/
|
||||
@@ -162,9 +222,27 @@ class Library
|
||||
* @param search List only books with search in the title or description.
|
||||
* @return The list of bookIds corresponding to the query.
|
||||
*/
|
||||
std::vector<std::string> filter(const std::string& search);
|
||||
DEPRECATED std::vector<std::string> filter(const std::string& search);
|
||||
|
||||
|
||||
/**
|
||||
* Filter the library and return the id of the keep elements.
|
||||
*
|
||||
* @param filter The filter to use.
|
||||
* @return The list of bookIds corresponding to the filter.
|
||||
*/
|
||||
std::vector<std::string> filter(const Filter& filter);
|
||||
|
||||
|
||||
/**
|
||||
* Sort (in place) bookIds using the given comparator.
|
||||
*
|
||||
* @param bookIds the list of book Ids to sort
|
||||
* @param comparator how to sort the books
|
||||
* @return The sorted list of books
|
||||
*/
|
||||
void sort(std::vector<std::string>& bookIds, supportedListSortBy sortBy, bool ascending);
|
||||
|
||||
/**
|
||||
* List books in the library.
|
||||
*
|
||||
@@ -187,7 +265,7 @@ class Library
|
||||
* Set to 0 to cancel this filter.
|
||||
* @return The list of bookIds corresponding to the query.
|
||||
*/
|
||||
std::vector<std::string> listBooksIds(
|
||||
DEPRECATED std::vector<std::string> listBooksIds(
|
||||
int supportedListMode = ALL,
|
||||
supportedListSortBy sortBy = UNSORTED,
|
||||
const std::string& search = "",
|
||||
|
||||
@@ -58,9 +58,6 @@ class DefaultLibraryManipulator : public LibraryManipulator {
|
||||
|
||||
/**
|
||||
* A tool to manage a `Library`.
|
||||
*
|
||||
* A `Manager` handle a internal `Library`.
|
||||
* This `Library` can be retrived with `cloneLibrary` method.
|
||||
*/
|
||||
class Manager
|
||||
{
|
||||
@@ -158,55 +155,6 @@ class Manager
|
||||
const std::string& url = "",
|
||||
const bool checkMetaData = false);
|
||||
|
||||
/**
|
||||
* Get the book corresponding to an id.
|
||||
*
|
||||
* @param[in] id The id of the book
|
||||
* @param[out] book The book corresponding to the id.
|
||||
* @return True if the book has been found.
|
||||
*/
|
||||
bool getBookById(const std::string& id, Book& book);
|
||||
|
||||
/**
|
||||
* Update the "last open date" of a book
|
||||
*
|
||||
* @param id the id of the book.
|
||||
* @return True if the book is in the library.
|
||||
*/
|
||||
bool updateBookLastOpenDateById(const std::string& id);
|
||||
|
||||
/**
|
||||
* Remove (set to empty) paths of all books in the library.
|
||||
*/
|
||||
void removeBookPaths();
|
||||
|
||||
/**
|
||||
* List books in the library.
|
||||
*
|
||||
* The books list will be available in public vector member `bookIdList`.
|
||||
*
|
||||
* @param mode The mode of listing :
|
||||
* - LASTOPEN sort by last opened book.
|
||||
* - LOCAL list only local file.
|
||||
* - REMOTE list only remote file.
|
||||
* @param sortBy Attribute to sort by the book list.
|
||||
* @param maxSize Do not list book bigger than maxSize MiB.
|
||||
* Set to 0 to cancel this filter.
|
||||
* @param language List only books in this language.
|
||||
* @param creator List only books of this creator.
|
||||
* @param publisher List only books of this publisher.
|
||||
* @param search List only books with search in the title, description or
|
||||
* language.
|
||||
* @return True
|
||||
*/
|
||||
bool listBooks(const supportedListMode mode,
|
||||
const supportedListSortBy sortBy,
|
||||
const unsigned int maxSize,
|
||||
const std::string& language,
|
||||
const std::string& creator,
|
||||
const std::string& publisher,
|
||||
const std::string& search);
|
||||
|
||||
std::string writableLibraryPath;
|
||||
|
||||
bool m_hasSearchResult = false;
|
||||
@@ -225,8 +173,6 @@ class Manager
|
||||
bool parseOpdsDom(const pugi::xml_document& doc,
|
||||
const std::string& urlHost);
|
||||
|
||||
private:
|
||||
void checkAndCleanBookPaths(Book& book, const std::string& libraryPath);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ headers = [
|
||||
'downloader.h',
|
||||
'reader.h',
|
||||
'entry.h',
|
||||
'searcher.h'
|
||||
'searcher.h',
|
||||
'kiwixserve.h'
|
||||
]
|
||||
|
||||
install_headers(headers, subdir:'kiwix')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
project('kiwix-lib', 'cpp',
|
||||
version : '5.0.0',
|
||||
version : '5.2.0',
|
||||
license : 'GPL',
|
||||
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
|
||||
|
||||
@@ -14,7 +14,7 @@ endif
|
||||
|
||||
thread_dep = dependency('threads')
|
||||
libicu_dep = dependency('icu-i18n', static:static_deps)
|
||||
libzim_dep = dependency('libzim', version : '>=4.0.0', static:static_deps)
|
||||
libzim_dep = dependency('libzim', version : '>=5.0.0', static:static_deps)
|
||||
pugixml_dep = dependency('pugixml', static:static_deps)
|
||||
libcurl_dep = dependency('libcurl', static:static_deps)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include "org_kiwix_kiwixlib_JNIKiwix.h"
|
||||
#include "org_kiwix_kiwixlib_JNIICU.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
pthread_mutex_t globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_setDataDirectory(
|
||||
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIICU_setDataDirectory(
|
||||
JNIEnv* env, jobject obj, jstring dirStr)
|
||||
{
|
||||
std::string cPath = jni2c(dirStr, env);
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
kiwix_jni = custom_target('jni',
|
||||
input: ['org/kiwix/kiwixlib/JNIKiwix.java',
|
||||
input: ['org/kiwix/kiwixlib/JNIICU.java',
|
||||
'org/kiwix/kiwixlib/JNIKiwixReader.java',
|
||||
'org/kiwix/kiwixlib/JNIKiwixSearcher.java',
|
||||
'org/kiwix/kiwixlib/JNIKiwixInt.java',
|
||||
@@ -16,7 +16,7 @@ kiwix_jni = custom_target('jni',
|
||||
)
|
||||
|
||||
kiwix_sources += [
|
||||
'android/kiwix.cpp',
|
||||
'android/kiwixicu.cpp',
|
||||
'android/kiwixreader.cpp',
|
||||
'android/kiwixsearcher.cpp',
|
||||
kiwix_jni]
|
||||
|
||||
26
src/android/org/kiwix/kiwixlib/JNIICU.java
Normal file
26
src/android/org/kiwix/kiwixlib/JNIICU.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixlib;
|
||||
|
||||
public class JNIICU
|
||||
{
|
||||
static public native void setDataDirectory(String icuDataDir);
|
||||
}
|
||||
@@ -20,12 +20,17 @@
|
||||
|
||||
package org.kiwix.kiwixlib;
|
||||
|
||||
import org.kiwix.kiwixlib.JNIKiwixReader;
|
||||
import org.kiwix.kiwixlib.JNIKiwixString;
|
||||
import android.content.Context;
|
||||
import com.getkeepsafe.relinker.ReLinker;
|
||||
import org.kiwix.kiwixlib.JNIICU;
|
||||
|
||||
public class JNIKiwix
|
||||
{
|
||||
static { System.loadLibrary("kiwix"); }
|
||||
public JNIKiwix(final Context context){
|
||||
ReLinker.loadLibrary(context, "kiwix");
|
||||
}
|
||||
|
||||
public native void setDataDirectory(String icuDataDir);
|
||||
public void setDataDirectory(String icuDataDir) {
|
||||
JNIICU.setDataDirectory(icuDataDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ Aria2::Aria2():
|
||||
|
||||
int watchdog = 50;
|
||||
while(--watchdog) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10000));
|
||||
sleep(10);
|
||||
auto res = curl_easy_perform(mp_curl);
|
||||
if (res == CURLE_OK) {
|
||||
break;
|
||||
@@ -171,7 +171,6 @@ std::vector<std::string> Aria2::tellActive()
|
||||
MethodCall methodCall("aria2.tellActive", m_secret);
|
||||
auto statusArray = methodCall.newParamValue().getArray();
|
||||
statusArray.addValue().set(std::string("gid"));
|
||||
statusArray.addValue().set(std::string("following"));
|
||||
auto responseContent = doRequest(methodCall);
|
||||
MethodResponse response(responseContent);
|
||||
std::vector<std::string> activeGID;
|
||||
@@ -186,6 +185,27 @@ std::vector<std::string> Aria2::tellActive()
|
||||
return activeGID;
|
||||
}
|
||||
|
||||
std::vector<std::string> Aria2::tellWaiting()
|
||||
{
|
||||
MethodCall methodCall("aria2.tellWaiting", m_secret);
|
||||
methodCall.newParamValue().set(0);
|
||||
methodCall.newParamValue().set(99); // max number of downloads to be returned, don't know how to set this properly assumed that there will not be more than 99 paused downloads.
|
||||
auto statusArray = methodCall.newParamValue().getArray();
|
||||
statusArray.addValue().set(std::string("gid"));
|
||||
auto responseContent = doRequest(methodCall);
|
||||
MethodResponse response(responseContent);
|
||||
std::vector<std::string> waitingGID;
|
||||
int index = 0;
|
||||
while(true) {
|
||||
try {
|
||||
auto structNode = response.getParamValue(0).getArray().getValue(index++).getStruct();
|
||||
auto gidNode = structNode.getMember("gid");
|
||||
waitingGID.push_back(gidNode.getValue().getAsS());
|
||||
} catch (InvalidRPCNode& e) { break; }
|
||||
}
|
||||
return waitingGID;
|
||||
}
|
||||
|
||||
void Aria2::saveSession()
|
||||
{
|
||||
MethodCall methodCall("aria2.saveSession", m_secret);
|
||||
@@ -199,5 +219,25 @@ void Aria2::shutdown()
|
||||
doRequest(methodCall);
|
||||
}
|
||||
|
||||
void Aria2::pause(const std::string& gid)
|
||||
{
|
||||
MethodCall methodCall("aria2.pause", m_secret);
|
||||
methodCall.newParamValue().set(gid);
|
||||
doRequest(methodCall);
|
||||
}
|
||||
|
||||
void Aria2::unpause(const std::string& gid)
|
||||
{
|
||||
MethodCall methodCall("aria2.unpause", m_secret);
|
||||
methodCall.newParamValue().set(gid);
|
||||
doRequest(methodCall);
|
||||
}
|
||||
|
||||
void Aria2::remove(const std::string& gid)
|
||||
{
|
||||
MethodCall methodCall("aria2.remove", m_secret);
|
||||
methodCall.newParamValue().set(gid);
|
||||
doRequest(methodCall);
|
||||
}
|
||||
|
||||
} // end namespace kiwix
|
||||
|
||||
@@ -37,8 +37,12 @@ class Aria2
|
||||
std::string addUri(const std::vector<std::string>& uri);
|
||||
std::string tellStatus(const std::string& gid, const std::vector<std::string>& statusKey);
|
||||
std::vector<std::string> tellActive();
|
||||
std::vector<std::string> tellWaiting();
|
||||
void saveSession();
|
||||
void shutdown();
|
||||
void pause(const std::string& gid);
|
||||
void unpause(const std::string& gid);
|
||||
void remove(const std::string& gid);
|
||||
};
|
||||
|
||||
}; //end namespace kiwix
|
||||
|
||||
@@ -138,6 +138,7 @@ void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost
|
||||
m_language = VALUE("language");
|
||||
m_date = fromOpdsDate(VALUE("updated"));
|
||||
m_creator = node.child("author").child("name").child_value();
|
||||
m_tags = VALUE("tags");
|
||||
for(auto linkNode = node.child("link"); linkNode;
|
||||
linkNode = linkNode.next_sibling("link")) {
|
||||
std::string rel = linkNode.attribute("rel").value();
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace kiwix
|
||||
|
||||
void Download::updateStatus(bool follow)
|
||||
{
|
||||
if (m_status == Download::K_REMOVED)
|
||||
return;
|
||||
static std::vector<std::string> statusKey = {"status", "files", "totalLength",
|
||||
"completedLength", "followedBy",
|
||||
"downloadSpeed", "verifiedLength"};
|
||||
@@ -93,6 +95,33 @@ void Download::updateStatus(bool follow)
|
||||
}
|
||||
}
|
||||
|
||||
void Download::resumeDownload()
|
||||
{
|
||||
if (!m_followedBy.empty())
|
||||
mp_aria->unpause(m_followedBy);
|
||||
else
|
||||
mp_aria->unpause(m_did);
|
||||
updateStatus(true);
|
||||
}
|
||||
|
||||
void Download::pauseDownload()
|
||||
{
|
||||
if (!m_followedBy.empty())
|
||||
mp_aria->pause(m_followedBy);
|
||||
else
|
||||
mp_aria->pause(m_did);
|
||||
updateStatus(true);
|
||||
}
|
||||
|
||||
void Download::cancelDownload()
|
||||
{
|
||||
if (!m_followedBy.empty())
|
||||
mp_aria->remove(m_followedBy);
|
||||
else
|
||||
mp_aria->remove(m_did);
|
||||
m_status = Download::K_REMOVED;
|
||||
}
|
||||
|
||||
/* Constructor */
|
||||
Downloader::Downloader() :
|
||||
mp_aria(new Aria2())
|
||||
@@ -101,6 +130,10 @@ Downloader::Downloader() :
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads[gid]->updateStatus();
|
||||
}
|
||||
for (auto gid : mp_aria->tellWaiting()) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads[gid]->updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,14 +172,23 @@ Download* Downloader::startDownload(const std::string& uri)
|
||||
Download* Downloader::getDownload(const std::string& did)
|
||||
{
|
||||
try {
|
||||
m_knownDownloads.at(did).get()->updateStatus(true);
|
||||
return m_knownDownloads.at(did).get();
|
||||
} catch(exception& e) {
|
||||
for (auto gid : mp_aria->tellActive()) {
|
||||
if (gid == did) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads.at(gid).get()->updateStatus(true);
|
||||
return m_knownDownloads[gid].get();
|
||||
}
|
||||
}
|
||||
for (auto gid : mp_aria->tellWaiting()) {
|
||||
if (gid == did) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads.at(gid).get()->updateStatus(true);
|
||||
return m_knownDownloads[gid].get();
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
69
src/kiwixserve.cpp
Normal file
69
src/kiwixserve.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "kiwixserve.h"
|
||||
#include "subprocess.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define KIWIXSERVE_CMD "kiwix-serve.exe"
|
||||
# include <windows.h>
|
||||
#else
|
||||
# define KIWIXSERVE_CMD "kiwix-serve"
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "tools/pathTools.h"
|
||||
|
||||
namespace kiwix {
|
||||
|
||||
KiwixServe::KiwixServe(int port) : m_port(port)
|
||||
{
|
||||
}
|
||||
|
||||
KiwixServe::~KiwixServe()
|
||||
{
|
||||
shutDown();
|
||||
}
|
||||
|
||||
void KiwixServe::run()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int pid = GetCurrentProcessId();
|
||||
#else
|
||||
pid_t pid = getpid();
|
||||
|
||||
#endif
|
||||
|
||||
std::vector<const char*> callCmd;
|
||||
std::string kiwixServeCmd = appendToDirectory(
|
||||
removeLastPathElement(getExecutablePath(), true, true),
|
||||
KIWIXSERVE_CMD);
|
||||
if (fileExists(kiwixServeCmd)) {
|
||||
// A local kiwix-serve exe exists (packaged with kiwix-desktop), use it.
|
||||
callCmd.push_back(kiwixServeCmd.c_str());
|
||||
} else {
|
||||
// Try to use a potential installed kiwix-serve.
|
||||
callCmd.push_back(KIWIXSERVE_CMD);
|
||||
}
|
||||
std::string libraryPath = getDataDirectory() + "/library.xml";
|
||||
std::string attachProcessOpt = "-a" + to_string(pid);
|
||||
std::string portOpt = "-p" + to_string(m_port);
|
||||
callCmd.push_back(attachProcessOpt.c_str());
|
||||
callCmd.push_back(portOpt.c_str());
|
||||
callCmd.push_back("-l");
|
||||
callCmd.push_back(libraryPath.c_str());
|
||||
mp_kiwixServe = Subprocess::run(callCmd);
|
||||
}
|
||||
|
||||
void KiwixServe::shutDown()
|
||||
{
|
||||
if (mp_kiwixServe)
|
||||
mp_kiwixServe->kill();
|
||||
}
|
||||
|
||||
bool KiwixServe::isRunning()
|
||||
{
|
||||
if (mp_kiwixServe) {
|
||||
return (mp_kiwixServe->isRunning());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
364
src/library.cpp
364
src/library.cpp
@@ -96,14 +96,16 @@ unsigned int Library::getBookCount(const bool localBooks,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Library::writeToFile(const std::string& path) {
|
||||
bool Library::writeToFile(const std::string& path)
|
||||
{
|
||||
auto baseDir = removeLastPathElement(path, true, false);
|
||||
LibXMLDumper dumper(this);
|
||||
dumper.setBaseDir(baseDir);
|
||||
return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds()));
|
||||
}
|
||||
|
||||
bool Library::writeBookmarksToFile(const std::string& path) {
|
||||
bool Library::writeBookmarksToFile(const std::string& path)
|
||||
{
|
||||
LibXMLDumper dumper(this);
|
||||
return writeTextFile(path, dumper.dumpLibXMLBookmark());
|
||||
}
|
||||
@@ -182,67 +184,104 @@ std::vector<std::string> Library::filter(const std::string& search)
|
||||
return getBooksIds();
|
||||
}
|
||||
|
||||
return filter(Filter().query(search));
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> Library::filter(const Filter& filter)
|
||||
{
|
||||
std::vector<std::string> bookIds;
|
||||
for(auto& pair:m_books) {
|
||||
auto& book = pair.second;
|
||||
if (matchRegex(book.getTitle(), "\\Q" + search + "\\E")
|
||||
|| matchRegex(book.getDescription(), "\\Q" + search + "\\E")) {
|
||||
bookIds.push_back(pair.first);
|
||||
}
|
||||
auto book = pair.second;
|
||||
if(filter.accept(book)) {
|
||||
bookIds.push_back(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
return bookIds;
|
||||
}
|
||||
|
||||
template<supportedListSortBy sort>
|
||||
struct Comparator {
|
||||
Library* lib;
|
||||
Comparator(Library* lib) : lib(lib) {}
|
||||
|
||||
bool operator() (const std::string& id1, const std::string& id2) {
|
||||
return get_keys(id1) < get_keys(id2);
|
||||
}
|
||||
|
||||
std::string get_keys(const std::string& id);
|
||||
unsigned int get_keyi(const std::string& id);
|
||||
template<supportedListSortBy SORT>
|
||||
struct KEY_TYPE {
|
||||
typedef std::string TYPE;
|
||||
};
|
||||
|
||||
template<>
|
||||
std::string Comparator<TITLE>::get_keys(const std::string& id)
|
||||
struct KEY_TYPE<SIZE> {
|
||||
typedef size_t TYPE;
|
||||
};
|
||||
|
||||
template<supportedListSortBy sort>
|
||||
class Comparator {
|
||||
private:
|
||||
Library* lib;
|
||||
bool ascending;
|
||||
|
||||
inline typename KEY_TYPE<sort>::TYPE get_key(const std::string& id);
|
||||
|
||||
public:
|
||||
Comparator(Library* lib, bool ascending) : lib(lib), ascending(ascending) {}
|
||||
inline bool operator() (const std::string& id1, const std::string& id2) {
|
||||
if (ascending) {
|
||||
return get_key(id1) < get_key(id2);
|
||||
} else {
|
||||
return get_key(id2) < get_key(id1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
std::string Comparator<TITLE>::get_key(const std::string& id)
|
||||
{
|
||||
return lib->getBookById(id).getTitle();
|
||||
}
|
||||
|
||||
template<>
|
||||
unsigned int Comparator<SIZE>::get_keyi(const std::string& id)
|
||||
size_t Comparator<SIZE>::get_key(const std::string& id)
|
||||
{
|
||||
return lib->getBookById(id).getSize();
|
||||
}
|
||||
|
||||
template<>
|
||||
bool Comparator<SIZE>::operator() (const std::string& id1, const std::string& id2)
|
||||
{
|
||||
return get_keyi(id1) < get_keyi(id2);
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string Comparator<DATE>::get_keys(const std::string& id)
|
||||
std::string Comparator<DATE>::get_key(const std::string& id)
|
||||
{
|
||||
return lib->getBookById(id).getDate();
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string Comparator<CREATOR>::get_keys(const std::string& id)
|
||||
std::string Comparator<CREATOR>::get_key(const std::string& id)
|
||||
{
|
||||
return lib->getBookById(id).getCreator();
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string Comparator<PUBLISHER>::get_keys(const std::string& id)
|
||||
std::string Comparator<PUBLISHER>::get_key(const std::string& id)
|
||||
{
|
||||
return lib->getBookById(id).getPublisher();
|
||||
}
|
||||
|
||||
void Library::sort(std::vector<std::string>& bookIds, supportedListSortBy sort, bool ascending)
|
||||
{
|
||||
switch(sort) {
|
||||
case TITLE:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<TITLE>(this, ascending));
|
||||
break;
|
||||
case SIZE:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<SIZE>(this, ascending));
|
||||
break;
|
||||
case DATE:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<DATE>(this, ascending));
|
||||
break;
|
||||
case CREATOR:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<CREATOR>(this, ascending));
|
||||
break;
|
||||
case PUBLISHER:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<PUBLISHER>(this, ascending));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> Library::listBooksIds(
|
||||
int mode,
|
||||
@@ -254,74 +293,205 @@ std::vector<std::string> Library::listBooksIds(
|
||||
const std::vector<std::string>& tags,
|
||||
size_t maxSize) {
|
||||
|
||||
std::vector<std::string> bookIds;
|
||||
for(auto& pair:m_books) {
|
||||
auto& book = pair.second;
|
||||
auto local = !book.getPath().empty();
|
||||
if (mode & LOCAL && !local)
|
||||
continue;
|
||||
if (mode & NOLOCAL && local)
|
||||
continue;
|
||||
auto valid = book.isPathValid();
|
||||
if (mode & VALID && !valid)
|
||||
continue;
|
||||
if (mode & NOVALID && valid)
|
||||
continue;
|
||||
auto remote = !book.getUrl().empty();
|
||||
if (mode & REMOTE && !remote)
|
||||
continue;
|
||||
if (mode & NOREMOTE && remote)
|
||||
continue;
|
||||
if (!tags.empty()) {
|
||||
auto vBookTags = split(book.getTags(), ";");
|
||||
std::set<std::string> sBookTags(vBookTags.begin(), vBookTags.end());
|
||||
bool ok = true;
|
||||
for (auto& t: tags) {
|
||||
if (sBookTags.find(t) == sBookTags.end()) {
|
||||
// A "filter" tag is not in the book tag.
|
||||
// No need to loop for all "filter" tags.
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! ok ) {
|
||||
// Skip the book
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (maxSize != 0 && book.getSize() > maxSize)
|
||||
continue;
|
||||
if (!language.empty() && book.getLanguage() != language)
|
||||
continue;
|
||||
if (!publisher.empty() && book.getPublisher() != publisher)
|
||||
continue;
|
||||
if (!creator.empty() && book.getCreator() != creator)
|
||||
continue;
|
||||
if (!search.empty() && !(matchRegex(book.getTitle(), "\\Q" + search + "\\E")
|
||||
|| matchRegex(book.getDescription(), "\\Q" + search + "\\E")))
|
||||
continue;
|
||||
bookIds.push_back(pair.first);
|
||||
}
|
||||
Filter _filter;
|
||||
if (mode & LOCAL)
|
||||
_filter.local(true);
|
||||
if (mode & NOLOCAL)
|
||||
_filter.local(false);
|
||||
if (mode & VALID)
|
||||
_filter.valid(true);
|
||||
if (mode & NOVALID)
|
||||
_filter.valid(false);
|
||||
if (mode & REMOTE)
|
||||
_filter.remote(true);
|
||||
if (mode & NOREMOTE)
|
||||
_filter.remote(false);
|
||||
if (!tags.empty())
|
||||
_filter.acceptTags(tags);
|
||||
if (maxSize != 0)
|
||||
_filter.maxSize(maxSize);
|
||||
if (!language.empty())
|
||||
_filter.lang(language);
|
||||
if (!publisher.empty())
|
||||
_filter.publisher(publisher);
|
||||
if (!creator.empty())
|
||||
_filter.creator(creator);
|
||||
if (!search.empty())
|
||||
_filter.query(search);
|
||||
|
||||
switch(sortBy) {
|
||||
case TITLE:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<TITLE>(this));
|
||||
break;
|
||||
case SIZE:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<SIZE>(this));
|
||||
break;
|
||||
case DATE:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<DATE>(this));
|
||||
break;
|
||||
case CREATOR:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<CREATOR>(this));
|
||||
break;
|
||||
case PUBLISHER:
|
||||
std::sort(bookIds.begin(), bookIds.end(), Comparator<PUBLISHER>(this));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
auto bookIds = filter(_filter);
|
||||
|
||||
sort(bookIds, sortBy, true);
|
||||
return bookIds;
|
||||
}
|
||||
|
||||
Filter::Filter()
|
||||
: activeFilters(0),
|
||||
_maxSize(0)
|
||||
{};
|
||||
|
||||
#define FLAG(x) (1 << x)
|
||||
enum filterTypes {
|
||||
NONE = 0,
|
||||
_LOCAL = FLAG(0),
|
||||
_REMOTE = FLAG(1),
|
||||
_NOLOCAL = FLAG(2),
|
||||
_NOREMOTE = FLAG(3),
|
||||
_VALID = FLAG(4),
|
||||
_NOVALID = FLAG(5),
|
||||
ACCEPTTAGS = FLAG(6),
|
||||
REJECTTAGS = FLAG(7),
|
||||
LANG = FLAG(8),
|
||||
_PUBLISHER = FLAG(9),
|
||||
_CREATOR = FLAG(10),
|
||||
MAXSIZE = FLAG(11),
|
||||
QUERY = FLAG(12),
|
||||
};
|
||||
|
||||
Filter& Filter::local(bool accept)
|
||||
{
|
||||
if (accept) {
|
||||
activeFilters |= _LOCAL;
|
||||
activeFilters &= ~_NOLOCAL;
|
||||
} else {
|
||||
activeFilters |= _NOLOCAL;
|
||||
activeFilters &= ~_LOCAL;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::remote(bool accept)
|
||||
{
|
||||
if (accept) {
|
||||
activeFilters |= _REMOTE;
|
||||
activeFilters &= ~_NOREMOTE;
|
||||
} else {
|
||||
activeFilters |= _NOREMOTE;
|
||||
activeFilters &= ~_REMOTE;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::valid(bool accept)
|
||||
{
|
||||
if (accept) {
|
||||
activeFilters |= _VALID;
|
||||
activeFilters &= ~_NOVALID;
|
||||
} else {
|
||||
activeFilters |= _NOVALID;
|
||||
activeFilters &= ~_VALID;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::acceptTags(std::vector<std::string> tags)
|
||||
{
|
||||
_acceptTags = tags;
|
||||
activeFilters |= ACCEPTTAGS;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::rejectTags(std::vector<std::string> tags)
|
||||
{
|
||||
_rejectTags = tags;
|
||||
activeFilters |= REJECTTAGS;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::lang(std::string lang)
|
||||
{
|
||||
_lang = lang;
|
||||
activeFilters |= LANG;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::publisher(std::string publisher)
|
||||
{
|
||||
_publisher = publisher;
|
||||
activeFilters |= _PUBLISHER;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::creator(std::string creator)
|
||||
{
|
||||
_creator = creator;
|
||||
activeFilters |= _CREATOR;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::maxSize(size_t maxSize)
|
||||
{
|
||||
_maxSize = maxSize;
|
||||
activeFilters |= MAXSIZE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter& Filter::query(std::string query)
|
||||
{
|
||||
_query = query;
|
||||
activeFilters |= QUERY;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#define ACTIVE(X) (activeFilters & (X))
|
||||
bool Filter::accept(const Book& book) const
|
||||
{
|
||||
auto local = !book.getPath().empty();
|
||||
if (ACTIVE(_LOCAL) && !local)
|
||||
return false;
|
||||
if (ACTIVE(_NOLOCAL) && local)
|
||||
return false;
|
||||
auto valid = book.isPathValid();
|
||||
if (ACTIVE(_VALID) && !valid)
|
||||
return false;
|
||||
if (ACTIVE(_NOVALID) && valid)
|
||||
return false;
|
||||
auto remote = !book.getUrl().empty();
|
||||
if (ACTIVE(_REMOTE) && !remote)
|
||||
return false;
|
||||
if (ACTIVE(_NOREMOTE) && remote)
|
||||
return false;
|
||||
if (ACTIVE(ACCEPTTAGS)) {
|
||||
if (!_acceptTags.empty()) {
|
||||
auto vBookTags = split(book.getTags(), ";");
|
||||
std::set<std::string> sBookTags(vBookTags.begin(), vBookTags.end());
|
||||
for (auto& t: _acceptTags) {
|
||||
if (sBookTags.find(t) == sBookTags.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ACTIVE(REJECTTAGS)) {
|
||||
if (!_rejectTags.empty()) {
|
||||
auto vBookTags = split(book.getTags(), ";");
|
||||
std::set<std::string> sBookTags(vBookTags.begin(), vBookTags.end());
|
||||
for (auto& t: _rejectTags) {
|
||||
if (sBookTags.find(t) != sBookTags.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ACTIVE(MAXSIZE) && book.getSize() > _maxSize)
|
||||
return false;
|
||||
|
||||
if (ACTIVE(LANG) && book.getLanguage() != _lang)
|
||||
return false;
|
||||
|
||||
if (ACTIVE(_PUBLISHER) && book.getPublisher() != _publisher)
|
||||
return false;
|
||||
|
||||
if (ACTIVE(_CREATOR) && book.getCreator() != _creator)
|
||||
return false;
|
||||
|
||||
if ( ACTIVE(QUERY)
|
||||
&& !(matchRegex(book.getTitle(), "\\Q" + _query + "\\E")
|
||||
|| matchRegex(book.getDescription(), "\\Q" + _query + "\\E")))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ kiwix_sources = [
|
||||
'tools/stringTools.cpp',
|
||||
'tools/networkTools.cpp',
|
||||
'tools/otherTools.cpp',
|
||||
'kiwixserve.cpp',
|
||||
]
|
||||
kiwix_sources += lib_resources
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ pugi::xml_node OPDSDumper::handleBook(Book book, pugi::xml_node root_node) {
|
||||
ADD_TEXT_ENTRY(entry_node, "icon", rootLocation + "/meta?name=favicon&content=" + book.getHumanReadableIdFromPath());
|
||||
ADD_TEXT_ENTRY(entry_node, "updated", gen_date_from_yyyy_mm_dd(book.getDate()));
|
||||
ADD_TEXT_ENTRY(entry_node, "summary", book.getDescription());
|
||||
ADD_TEXT_ENTRY(entry_node, "tags", book.getTags());
|
||||
|
||||
auto content_node = entry_node.append_child("link");
|
||||
content_node.append_attribute("type") = "text/html";
|
||||
|
||||
@@ -765,13 +765,16 @@ bool Reader::searchSuggestionsSmart(const string& prefix,
|
||||
this->suggestions.clear();
|
||||
this->suggestionsOffset = this->suggestions.begin();
|
||||
/* Try to search in the title using fulltext search database */
|
||||
const zim::Search* suggestionSearch
|
||||
const auto suggestionSearch
|
||||
= this->getZimFileHandler()->suggestions(prefix, 0, suggestionsCount);
|
||||
|
||||
if (suggestionSearch->get_matches_estimated()) {
|
||||
for (auto current = suggestionSearch->begin();
|
||||
current != suggestionSearch->end();
|
||||
current++) {
|
||||
if (!current->good()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<std::string> suggestion;
|
||||
suggestion.push_back(current->getTitle());
|
||||
suggestion.push_back("/A/" + current->getUrl());
|
||||
|
||||
@@ -136,6 +136,7 @@ void Searcher::search(std::string& search,
|
||||
}
|
||||
}
|
||||
zim::Search* search = new zim::Search(zims);
|
||||
search->set_verbose(verbose);
|
||||
search->set_query(unaccentedSearch);
|
||||
search->set_range(resultStart, resultEnd);
|
||||
internal->_search = search;
|
||||
@@ -190,6 +191,7 @@ void Searcher::geo_search(float latitude, float longitude, float distance,
|
||||
zims.push_back((*current)->getZimFileHandler());
|
||||
}
|
||||
zim::Search* search = new zim::Search(zims);
|
||||
search->set_verbose(verbose);
|
||||
search->set_query("");
|
||||
search->set_georange(latitude, longitude, distance);
|
||||
search->set_range(resultStart, resultEnd);
|
||||
@@ -244,6 +246,7 @@ void Searcher::suggestions(std::string& searchPattern, const bool verbose)
|
||||
zims.push_back((*current)->getZimFileHandler());
|
||||
}
|
||||
zim::Search* search = new zim::Search(zims);
|
||||
search->set_verbose(verbose);
|
||||
search->set_query(unaccentedSearch);
|
||||
search->set_range(resultStart, resultEnd);
|
||||
search->set_suggestion_mode(true);
|
||||
|
||||
@@ -46,7 +46,7 @@ void* UnixImpl::waitForPID(void* _self)
|
||||
#endif
|
||||
|
||||
UnixImpl* self = static_cast<UnixImpl*>(_self);
|
||||
waitpid(self->m_pid, NULL, WEXITED);
|
||||
waitpid(self->m_pid, NULL, 0);
|
||||
|
||||
pthread_mutex_lock(&self->m_mutex);
|
||||
self->m_running = false;
|
||||
@@ -67,7 +67,7 @@ void UnixImpl::run(commandLine_t& commandLine)
|
||||
commandLine.push_back(NULL);
|
||||
if (execvp(binary, const_cast<char* const*>(commandLine.data()))) {
|
||||
perror("Cannot launch\n");
|
||||
exit(-1);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -29,11 +29,6 @@
|
||||
#define getcwd _getcwd // stupid MSFT "deprecation" warning
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
const std::string SEPARATOR("\\");
|
||||
#else
|
||||
|
||||
241
test/library.cpp
Normal file
241
test/library.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Tommi Maekitalo
|
||||
*
|
||||
* 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
|
||||
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
|
||||
* NON-INFRINGEMENT. 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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
const char * sampleOpdsStream = R"(
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:opds="http://opds-spec.org/2010/catalog">
|
||||
<id>00000000-0000-0000-0000-000000000000</id>
|
||||
<entry>
|
||||
<title>Encyclopédie de la Tunisie</title>
|
||||
<id>urn:uuid:0c45160e-f917-760a-9159-dfe3c53cdcdd</id>
|
||||
<icon>/meta?name=favicon&content=wikipedia_fr_tunisie_novid_2018-10</icon>
|
||||
<updated>2018-10-08T00:00::00:Z</updated>
|
||||
<language>fra</language>
|
||||
<summary>Le meilleur de Wikipédia sur la Tunisie</summary>
|
||||
<tags>wikipedia;novid;_ftindex</tags>
|
||||
<link type="text/html" href="/wikipedia_fr_tunisie_novid_2018-10" />
|
||||
<author>
|
||||
<name>Wikipedia</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikipedia/wikipedia_fr_tunisie_novid_2018-10.zim.meta4" length="90030080" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=wikipedia_fr_tunisie_novid_2018-10" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Tania Louis</title>
|
||||
<id>urn:uuid:0d0bcd57-d3f6-cb22-44cc-a723ccb4e1b2</id>
|
||||
<icon>/meta?name=favicon&content=biologie-tout-compris_fr_all_2018-06</icon>
|
||||
<updated>2018-06-23T00:00::00:Z</updated>
|
||||
<language>fra</language>
|
||||
<summary>Tania Louis videos</summary>
|
||||
<tags>youtube</tags>
|
||||
<link type="text/html" href="/biologie-tout-compris_fr_all_2018-06" />
|
||||
<author>
|
||||
<name>Tania Louis</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/other/biologie-tout-compris_fr_all_2018-06.zim.meta4" length="2172639232" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=biologie-tout-compris_fr_all_2018-06" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Wikiquote</title>
|
||||
<id>urn:uuid:0ea1cde6-441d-6c58-f2c7-21c2838e659f</id>
|
||||
<icon>/meta?name=favicon&content=wikiquote_fr_all_nopic_2019-06</icon>
|
||||
<updated>2019-06-05T00:00::00:Z</updated>
|
||||
<language>fra</language>
|
||||
<summary>Une page de Wikiquote, le recueil des citations libres.</summary>
|
||||
<tags>wikiquote;nopic</tags>
|
||||
<link type="text/html" href="/wikiquote_fr_all_nopic_2019-06" />
|
||||
<author>
|
||||
<name>Wikiquote</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikiquote/wikiquote_fr_all_nopic_2019-06.zim.meta4" length="21368832" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=wikiquote_fr_all_nopic_2019-06" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Géographie par Wikipédia</title>
|
||||
<id>urn:uuid:1123e574-6eef-6d54-28fc-13e4caeae474</id>
|
||||
<icon>/meta?name=favicon&content=wikipedia_fr_geography_nopic_2019-06</icon>
|
||||
<updated>2019-06-02T00:00::00:Z</updated>
|
||||
<summary>Une sélection d'articles de Wikipédia sur la géographie</summary>
|
||||
<language>fra</language>
|
||||
<tags>wikipedia;nopic</tags>
|
||||
<link type="text/html" href="/wikipedia_fr_geography_nopic_2019-06" />
|
||||
<author>
|
||||
<name>Wikipedia</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikipedia/wikipedia_fr_geography_nopic_2019-06.zim.meta4" length="157586432" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=wikipedia_fr_geography_nopic_2019-06" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Mathématiques</title>
|
||||
<id>urn:uuid:14829621-c490-c376-0792-9de558b57efa</id>
|
||||
<icon>/meta?name=favicon&content=wikipedia_fr_mathematics_nopic_2019-05</icon>
|
||||
<updated>2019-05-13T00:00::00:Z</updated>
|
||||
<language>fra</language>
|
||||
<summary>Une</summary>
|
||||
<tags>wikipedia;nopic</tags>
|
||||
<link type="text/html" href="/wikipedia_fr_mathematics_nopic_2019-05" />
|
||||
<author>
|
||||
<name>Wikipedia</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikipedia/wikipedia_fr_mathematics_nopic_2019-05.zim.meta4" length="223368192" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=wikipedia_fr_mathematics_nopic_2019-05" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Granblue Fantasy Wiki</title>
|
||||
<id>urn:uuid:006cbd1b-16d8-b00d-a584-c1ae110a94ed</id>
|
||||
<icon>/meta?name=favicon&content=granbluefantasy_en_all_all_nopic_2018-10</icon>
|
||||
<updated>2018-10-14T00:00::00:Z</updated>
|
||||
<language>eng</language>
|
||||
<summary>Granblue Fantasy Wiki</summary>
|
||||
<tags>gbf;nopic;_ftindex</tags>
|
||||
<link type="text/html" href="/granbluefantasy_en_all_all_nopic_2018-10" />
|
||||
<author>
|
||||
<name>Wiki</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/other/granbluefantasy_en_all_all_nopic_2018-10.zim.meta4" length="23197696" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=granbluefantasy_en_all_all_nopic_2018-10" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Movies & TV Stack Exchange</title>
|
||||
<id>urn:uuid:00f37b00-f4da-0675-995a-770f9c72903e</id>
|
||||
<icon>/meta?name=favicon&content=movies.stackexchange.com_en_all_2019-02</icon>
|
||||
<updated>2019-02-03T00:00::00:Z</updated>
|
||||
<language>eng</language>
|
||||
<summary>Q&A for movie and tv enthusiasts</summary>
|
||||
<tags>stackexchange;_ftindex</tags>
|
||||
<link type="text/html" href="/movies.stackexchange.com_en_all_2019-02" />
|
||||
<author>
|
||||
<name>Movies & TV Stack Exchange</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/stack_exchange/movies.stackexchange.com_en_all_2019-02.zim.meta4" length="859463680" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=movies.stackexchange.com_en_all_2019-02" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>TED talks - Business</title>
|
||||
<id>urn:uuid:0189d9be-2fd0-b4b6-7300-20fab0b5cdc8</id>
|
||||
<icon>/meta?name=favicon&content=ted_en_business_2018-07</icon>
|
||||
<updated>2018-07-23T00:00::00:Z</updated>
|
||||
<language>eng</language>
|
||||
<summary>Ideas worth spreading</summary>
|
||||
<tags></tags>
|
||||
<link type="text/html" href="/ted_en_business_2018-07" />
|
||||
<author>
|
||||
<name>TED</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/ted/ted_en_business_2018-07.zim.meta4" length="8855827456" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=ted_en_business_2018-07" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Mythology & Folklore Stack Exchange</title>
|
||||
<id>urn:uuid:028055ac-4acc-1d54-65e0-a96de45e1b22</id>
|
||||
<icon>/meta?name=favicon&content=mythology.stackexchange.com_en_all_2019-02</icon>
|
||||
<updated>2019-02-03T00:00::00:Z</updated>
|
||||
<language>eng</language>
|
||||
<summary>Q&A for enthusiasts and scholars of mythology and folklore</summary>
|
||||
<tags>stackexchange;_ftindex</tags>
|
||||
<link type="text/html" href="/mythology.stackexchange.com_en_all_2019-02" />
|
||||
<author>
|
||||
<name>Mythology & Folklore Stack Exchange</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/stack_exchange/mythology.stackexchange.com_en_all_2019-02.zim.meta4" length="47005696" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=mythology.stackexchange.com_en_all_2019-02" />
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Islam Stack Exchange</title>
|
||||
<id>urn:uuid:02e9c7ff-36fc-9c6e-6ac7-cd7085989029</id>
|
||||
<icon>/meta?name=favicon&content=islam.stackexchange.com_en_all_2019-01</icon>
|
||||
<updated>2019-01-31T00:00::00:Z</updated>
|
||||
<language>eng</language>
|
||||
<summary>Q&A for Muslims, experts in Islam, and those interested in learning more about Islam</summary>
|
||||
<tags>stackexchange;_ftindex</tags>
|
||||
<link type="text/html" href="/islam.stackexchange.com_en_all_2019-01" />
|
||||
<author>
|
||||
<name>Islam Stack Exchange</name>
|
||||
</author>
|
||||
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/stack_exchange/islam.stackexchange.com_en_all_2019-01.zim.meta4" length="135346176" />
|
||||
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&content=islam.stackexchange.com_en_all_2019-01" />
|
||||
</entry>
|
||||
</feed>
|
||||
|
||||
)";
|
||||
|
||||
#include "../include/library.h"
|
||||
#include "../include/manager.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class LibraryTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
kiwix::Manager manager(&lib);
|
||||
manager.readOpds(sampleOpdsStream, "foo.urlHost");
|
||||
}
|
||||
|
||||
kiwix::Library lib;
|
||||
};
|
||||
|
||||
TEST_F(LibraryTest, sanityCheck)
|
||||
{
|
||||
EXPECT_EQ(lib.getBookCount(true, true), 10U);
|
||||
EXPECT_EQ(lib.getBooksLanguages().size(), 2U);
|
||||
EXPECT_EQ(lib.getBooksCreators().size(), 8U);
|
||||
EXPECT_EQ(lib.getBooksPublishers().size(), 1U);
|
||||
}
|
||||
|
||||
TEST_F(LibraryTest, filterCheck)
|
||||
{
|
||||
auto bookIds = lib.filter(kiwix::Filter());
|
||||
EXPECT_EQ(bookIds, lib.getBooksIds());
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().lang("eng"));
|
||||
EXPECT_EQ(bookIds.size(), 5U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().acceptTags({"stackexchange"}));
|
||||
EXPECT_EQ(bookIds.size(), 3U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().acceptTags({"wikipedia"}));
|
||||
EXPECT_EQ(bookIds.size(), 3U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().acceptTags({"wikipedia", "nopic"}));
|
||||
EXPECT_EQ(bookIds.size(), 2U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().acceptTags({"wikipedia"}).rejectTags({"nopic"}));
|
||||
EXPECT_EQ(bookIds.size(), 1U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().query("folklore"));
|
||||
EXPECT_EQ(bookIds.size(), 1U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().query("Wiki"));
|
||||
EXPECT_EQ(bookIds.size(), 3U);
|
||||
|
||||
bookIds = lib.filter(kiwix::Filter().query("Wiki").creator("Wiki"));
|
||||
EXPECT_EQ(bookIds.size(), 1U);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
|
||||
|
||||
tests = [
|
||||
'parseUrl'
|
||||
'parseUrl',
|
||||
'library'
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
set -e
|
||||
|
||||
REPO_NAME=${TRAVIS_REPO_SLUG#*/}
|
||||
ARCHIVE_NAME=deps_${TRAVIS_OS_NAME}_${PLATFORM}_${REPO_NAME}.tar.xz
|
||||
|
||||
# Ninja
|
||||
cd $HOME
|
||||
@@ -13,6 +12,7 @@ then
|
||||
|
||||
wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip
|
||||
unzip ninja-mac.zip ninja
|
||||
ARCHIVE_NAME=deps_osx_${PLATFORM}_${REPO_NAME}.tar.xz
|
||||
else
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
python3.5 get-pip.py --user
|
||||
@@ -22,6 +22,7 @@ else
|
||||
|
||||
wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
|
||||
unzip ninja-linux.zip ninja
|
||||
ARCHIVE_NAME=deps_linux_xenial_${PLATFORM}_${REPO_NAME}.tar.xz
|
||||
fi
|
||||
|
||||
mkdir -p $HOME/bin
|
||||
@@ -31,3 +32,4 @@ cp ninja $HOME/bin
|
||||
cd ${HOME}
|
||||
wget http://tmp.kiwix.org/ci/${ARCHIVE_NAME}
|
||||
tar xf ${HOME}/${ARCHIVE_NAME}
|
||||
sudo ln -s travis ../ci_builder
|
||||
|
||||
Reference in New Issue
Block a user