Commit Graph

10965 Commits

Author SHA1 Message Date
Max Kellermann
9187a08106 lib/curl: remove .netrc support on Android
Not needed on Android, and the implementation uses getpwuid_r() which
is unavailable on old Android versions.
2018-02-09 23:14:29 +01:00
Max Kellermann
3859a50466 python/build/libs.py: convert CURL edit to quilt patch 2018-02-09 23:14:27 +01:00
Max Kellermann
6ba918b203 input/file: don't use posix_fadvise() on Android
Requires Android API 21, but we want to support older versions as
well.
2018-02-09 22:54:22 +01:00
Max Kellermann
e8b70dbca4 SongSave, queue/PlaylistState, tag/ReplayGain: use portable atof() wrappers
For Android pre-5.0 compatibility (#213).
2018-02-09 22:54:22 +01:00
Max Kellermann
0f8d223c7f protocol/ArgParser: move strtof()/strtod() switch to util/NumberParser.hxx 2018-02-09 22:54:22 +01:00
Max Kellermann
19a2885fd5 protocol/ArgParser: use strtod() instead of strtof() on Android
For Android pre-5.0 compatibility (#213).
2018-02-09 22:54:22 +01:00
Max Kellermann
e46fbd0780 filter/convert: set the PcmConvert instance only if it was initialized
Fixes valgrind warning.
2018-02-09 19:05:45 +01:00
Max Kellermann
56b74ad990 filter/convert: add method IsActive() 2018-02-09 19:04:45 +01:00
Max Kellermann
6de92bb42b pcm/Order: fix size calculation with 8 channels
This was a buffer overflow bug which could cause MPD crahes when
playing back 8 channels with the ALSA output plugin.

Closes #216
2018-02-09 19:01:12 +01:00
Max Kellermann
c801936e53 db/update/Service: set the update thread name 2018-02-09 18:48:14 +01:00
Max Kellermann
817656504d thread/Util: implement system call wrapper for sched_setscheduler()
There is a POSIX definition for sched_setscheduler(), but Linux does
not implement that; instead of changing the process's scheduler, it
only affects one thread.  This has caused some confusion among
application developers and C library developers.

While glibc implements Linux semantics, Musl has made their
sched_setscheduler() function an always-failing no-op, causing the
error message "sched_setscheduler failed: Function not implemented".

 http://git.musl-libc.org/cgit/musl/commit/src/sched/sched_setscheduler.c?id=1e21e78bf7a5c24c217446d8760be7b7188711c2

Instead of relying on the C library which may be unreliable here, we
now roll our own system call wrapper.

Closes #218
2018-02-09 18:43:45 +01:00
Max Kellermann
6f00f97b66 thread/Util: rename ioprio_set() to linux_ioprio_set()
Juse in cas glibc gets a wrapper for the system call which would then
conflict with ours.
2018-02-09 18:43:45 +01:00
Max Kellermann
5147654f6c SongFilter: fix "modified-since" filter
Error message sent to client was "basic_string::_M_construct null not
valid" due to passing nullptr to the std::string constructor.

Regression caused by commit 386688b87a
2018-02-09 13:19:26 +01:00
Max Kellermann
250b6a3d52 release v0.20.16
-----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAlp2BjEQHG1heEBtdXNp
 Y3BkLm9yZwAKCRAjbopYxttFEl0HEACEOGoX3a8slWwuPsqdOu+thc73SIMVzqF9
 xzxFbGLyp0MzXTAgU+SLzfbD4/gerglC7TRbGzsvVu7iwxXeKhEL/SJ5RhrCQM3E
 U8jaOeavMWeqX60pdbTz2zw4maKAD7U3fWcYxoh09EBaZa7Mm/j7URPLqrlWT7VD
 iWqdLcC4HZarOxBbq0CY7rQPVsAVZZ5l43MDV6hiInSn0wWo783G6BtYFgvgH8qk
 pZIL0yScQrjyj9D0fgfDC9DBVfHqMqpWW5G2rNhsXTUtpdb9V5tB2B8ZbIUWJfD7
 aVFztCwwmbiBle4UBHfyyDLfZkBZmU/qKc9+I3WUxT4DLbA8gDXZFjb7zU80NMge
 xzbXeO7XvxvSm7groqRh3Vy0nbkBMB9wTwzre3ULhYyhY2vVISb9lSyRtUE9xuR8
 SL2zpdgVcFdTUSKat63ikeU4sbYvssra3ZDRnAyoFF2iVpT2J2B95ZVNfaIYbRSQ
 lidlz8q3Px5GUDe3D0ao3hFk5t1Hg2jXx3nbz/Kk+SwZpsaiCewl6zjptye7R7zD
 e9OMxYrU+/opsynRE5NFuTvkRKUG1kC47/rWgGZWI9Cbv8+lse1NO0c7VzM7o1hH
 Bfk5AhEkv8tLNM+39evXdNQVB0cna5ndSDOkYBcPI78/PUNd+TnrPdfI/W3NCnRl
 I+CWQE3uSQ==
 =+tJ2
 -----END PGP SIGNATURE-----

Merge tag 'v0.20.16'

release v0.20.16
2018-02-03 19:59:29 +01:00
Max Kellermann
12fd1cad0c archive/iso9660: libcdio 2.0 compatibility
Closes #173
2018-02-03 19:32:31 +01:00
Max Kellermann
73ddbcc876 player/Thread: wake up the main thread after failed seek
Fixes deadlock bug.  Closes #210
2018-02-03 19:28:39 +01:00
Max Kellermann
e573cbf032 db/update/Queue: work around GCC7 -Wuninitialized 2018-02-01 19:53:42 +01:00
Max Kellermann
dead461542 lib/upnp/Init: enable IPv6 2018-01-31 18:15:46 +01:00
Max Kellermann
3d5da1ac73 lib/upnp/Init: use nullptr instead of 0 2018-01-31 18:14:26 +01:00
Max Kellermann
ec408ca6a6 output/pulse: fix crash during auto-detection
The PulseOutput needs to be "enabled" before WaitConnection() may be
called.

Closes #207
2018-01-30 10:06:36 +01:00
Max Kellermann
ce2b6dc84d RemoteTagCache: new glue class for integrating RemoteTagScanner
This commit also puts an instance of RemoteTagScanner into the
Instance class, and hooks it into the "add" and "addid" commands.
2018-01-30 00:05:57 +01:00
Max Kellermann
7d16d8c887 Listen: move ClientListener pointer to struct Partition 2018-01-29 23:53:52 +01:00
Max Kellermann
1df5c5a76e Listen: move class ClientListener to src/client/Listener.hxx 2018-01-29 23:48:16 +01:00
Max Kellermann
52da387a1d Main: move part of the shutdown code to Instance methods 2018-01-29 23:45:48 +01:00
Max Kellermann
c1221c5c87 Main: cancel the update on shutdown even if inotify is unavailable
Fixup for commit 681e012fb5
2018-01-29 23:37:56 +01:00
Max Kellermann
befd669075 Instance: rename Shutdown() to Break() 2018-01-29 23:31:41 +01:00
Max Kellermann
b6013a92e0 lib/curl/Request: merge two "try" blocks 2018-01-29 23:25:03 +01:00
Max Kellermann
19af364400 event/Thread: add "noexcept" 2018-01-29 23:11:15 +01:00
Max Kellermann
3fd9a86f3c event/Call: invoke function directly if the EventLoop is dead
Works around a deadlock bug which happens when waiting for an
EventLoop which isn't running anymore.
2018-01-29 23:06:43 +01:00
Max Kellermann
ce68701c0c event/Loop: add flag "dead" 2018-01-29 22:52:13 +01:00
Max Kellermann
6ea2cb3644 event/Thread: remove unused method StopAsync() 2018-01-29 22:50:21 +01:00
Max Kellermann
199c8aaa25 event/Loop: move code to HandleTimers() 2018-01-29 22:40:12 +01:00
Max Kellermann
9ce6828d72 filter/convert: call PcmConvert::Flush() only if initialized
Fixes another part of #208, the one which caused the bogus exception.
2018-01-29 22:35:28 +01:00
Max Kellermann
7ff5cf8372 filter/convert: add method IsActive() 2018-01-29 22:33:54 +01:00
Max Kellermann
eb771eaf0d output/Thread: re-lock the mutex after flush error
Fixes deadlock due to mutex double lock.
2018-01-29 22:30:56 +01:00
Max Kellermann
6908555ed3 filter/Observer: drop bogus "noexcept"
Closes #208.
2018-01-29 22:14:27 +01:00
Max Kellermann
3890bc5a96 event/Loop: add "noexcept" 2018-01-29 22:05:58 +01:00
Max Kellermann
2c65f986d6 event/EPoll: use C++11 initializer 2018-01-29 22:05:13 +01:00
Max Kellermann
ea402b765c event/WinSelect: pass const reference to copy constructor 2018-01-29 22:05:13 +01:00
Max Kellermann
497e0669ff event/PollResult: use size_t for size and index 2018-01-29 21:56:12 +01:00
Max Kellermann
13ac74e0a2 system/EPollFD: update API documentation 2018-01-29 21:49:27 +01:00
Max Kellermann
c2f23d92dc system/EPollFD: add "noexcept" 2018-01-29 21:48:39 +01:00
Max Kellermann
7027da3cd3 Instance: un-inline the destructor
Allows more forward declarations.
2018-01-29 18:43:10 +01:00
Max Kellermann
005bb59797 test/run_input: move code from Scan() to input/ScanTags.cxx 2018-01-29 16:57:44 +01:00
Max Kellermann
88bc3a9271 input/qobuz: implement InputPlugin::scan_tags() 2018-01-26 20:08:01 +01:00
Max Kellermann
2e32cf7b87 input/tidal: implement InputPlugin::scan_tags() 2018-01-26 20:08:01 +01:00
Max Kellermann
3e9c3c8ae8 input/Plugin: add method scan() with asynchronous callback class
To be used when a URL gets added.
2018-01-26 19:29:38 +01:00
Max Kellermann
ec31e8df1d input/qobuz: remove obsolete prototypes 2018-01-26 19:29:38 +01:00
Max Kellermann
b56bfbae26 input/tidal: add setting "audioquality" 2018-01-26 19:18:13 +01:00
Max Kellermann
f55b1415d4 input/qobuz: move code to ExtractQobuzTrackId() 2018-01-26 18:48:15 +01:00
Max Kellermann
67693c6ad0 input/qobuz: add setting "format_id" 2018-01-26 17:30:28 +01:00
Max Kellermann
fce061b544 input/tidal: move code to ExtractTidalTrackId() 2018-01-26 17:29:58 +01:00
Max Kellermann
93765fcb30 input/{tidal,qobuz}: remove unused list_base_hook 2018-01-26 16:56:56 +01:00
Max Kellermann
b221bd7472 input/qobuz: remove bogus "noexcept"
This change was missing in 47fa8c4cf6
2018-01-25 15:19:33 +01:00
Max Kellermann
fd0a5a1116 decoder/{dsdiff,dsf,mpg123,wavpack}: avoid exceptions in scan methods
The scan methods must be "noexcept".
2018-01-25 13:01:39 +01:00
Max Kellermann
47fa8c4cf6 input/{tidal,qobus}: add bogus "noexcept"
The CurlRequest constructor can throw exceptions, therefore we must
not use "noexcept".
2018-01-25 12:58:58 +01:00
Max Kellermann
7640609b41 input/tidal: retry if session is expired 2018-01-24 15:35:02 +01:00
Max Kellermann
e8e6357b73 input/tidal/Error: add method IsInvalidSession() 2018-01-24 15:34:59 +01:00
Max Kellermann
4398101706 input/tidal: parse subStatus in error responses 2018-01-24 15:34:57 +01:00
Max Kellermann
2e1481f49d input/tidal: add exception class TidalError
Allows catchers to inspect the HTTP status.
2018-01-24 15:34:54 +01:00
Max Kellermann
c7c087a0d2 input/tidal: add some debug logging 2018-01-24 15:29:30 +01:00
Max Kellermann
97f670658f util/StringFormat: new utility library 2018-01-24 13:28:28 +01:00
Max Kellermann
4324fb2fbe input/Offset: add macro PRIoffset 2018-01-24 13:26:43 +01:00
Max Kellermann
e4cb74cf7b input/curl: remove obsolete Windows sprintf() fallback
See commit be137a191e
2018-01-24 13:26:18 +01:00
Max Kellermann
be137a191e util/FormatString: remove obsolete Windows fallback
Since 7d353bbe2a, _GNU_SOURCE is always
defined, which implies __USE_MINGW_ANSI_STDIO and thus switches to
the mingw implementations of the printf() family.  That's
standards-compliant, unlike Microsoft's CRT implementations.
2018-01-24 13:22:43 +01:00
Max Kellermann
fe1f3df36e util/StringBuffer: add "noexcept" 2018-01-24 12:52:05 +01:00
Max Kellermann
4a330a4c33 input/qobuz: use class DelegateCurlResponseHandler to eliminate duplicate code 2018-01-24 11:33:01 +01:00
Max Kellermann
75e60669a7 input/tidal: include cleanup 2018-01-24 11:33:01 +01:00
Max Kellermann
db379c6172 player/Thread: remove bogus "noexcept" from play_chunk()
Closes #203
2018-01-24 11:21:28 +01:00
Max Kellermann
1da64fb79c lib/curl/Delegate: helper class for response body parsers
Eliminate duplicate code.
2018-01-23 16:48:43 +01:00
Max Kellermann
2c401f1102 decoder/{adplug,sidplay}: add missing "noexcept"
Closes #202
2018-01-23 10:02:36 +01:00
Max Kellermann
bb1e369f30 playlist/SoundCloud: fix -Wunused-lambda-capture 2018-01-23 09:57:52 +01:00
Max Kellermann
d084950b1a input/qobuz: dispose the TidalLoginRequest earlier 2018-01-21 20:17:50 +01:00
Max Kellermann
2ce10f2fed input/qobuz: dispose the QobuzTrackRequest 2018-01-21 19:59:17 +01:00
Max Kellermann
036094799b input/qobuz: pass std::string by value to OnQobuzTrackSuccess() 2018-01-21 19:58:21 +01:00
Max Kellermann
2da6ab2a45 input/qobuz: dispose the QobuzLoginRequest earlier 2018-01-21 19:45:03 +01:00
Max Kellermann
91a512606b input/qobuz: remove unused variable 2018-01-21 15:01:31 +01:00
Max Kellermann
b6d858f4d6 lib/expat/Parser: make the "is_final" argument optional 2018-01-21 14:37:10 +01:00
Max Kellermann
dedede3caa lib/expat/Parser: add method CompleteParse() 2018-01-21 14:35:47 +01:00
Max Kellermann
b14fc907f9 lib/expat/Parser: forward Parse() arguments 2018-01-21 14:34:53 +01:00
Max Kellermann
542bbb0889 input/qobuz: parse and report message from error responses 2018-01-21 12:52:54 +01:00
Max Kellermann
714e69e708 input/qobuz: create JSON parser on demand 2018-01-21 12:49:10 +01:00
Max Kellermann
cb87ed29d8 input/tidal: invoke login handlers on second request
Fixes a freeze bug.
2018-01-21 12:41:19 +01:00
Max Kellermann
570c6765b0 input/tidal: parse and report userMessage from error responses 2018-01-21 12:30:46 +01:00
Max Kellermann
12a2285ba5 input/tidal: create JSON parser on demand 2018-01-21 12:29:52 +01:00
Max Kellermann
1c023eac70 db/update/Walk: add "noexcept" 2018-01-21 11:53:59 +01:00
Max Kellermann
1e3f0650df SongUpdate: add "noexcept" 2018-01-21 11:53:42 +01:00
Max Kellermann
1e9da09f62 TagFile: add "noexcept" 2018-01-21 11:53:42 +01:00
Max Kellermann
5caf351c44 TagArchive: add "noexcept" 2018-01-21 11:53:42 +01:00
Max Kellermann
7d789a984a decoder/Plugin: add "noexcept" 2018-01-21 11:47:17 +01:00
Max Kellermann
d921114832 db/Helpers: add "noexcept" 2018-01-21 11:37:13 +01:00
Max Kellermann
39a1f03d5c db/PlaylistVector: add "noexcept" 2018-01-21 11:36:16 +01:00
Max Kellermann
c6a4a4edf1 playlist/Print: add "noexcept" 2018-01-21 11:35:31 +01:00
Max Kellermann
45748a2bb0 playlist/Song: add "noexcept" 2018-01-21 11:35:28 +01:00
Max Kellermann
9b13d862c1 playlist/Registry: add "noexcept" 2018-01-21 11:35:27 +01:00
Max Kellermann
590edc648a playlist/Plugin: add "noexcept" 2018-01-21 11:35:25 +01:00
Max Kellermann
3326c87a88 db/Count: add "noexcept" 2018-01-21 11:33:53 +01:00
Max Kellermann
0ec1863fcb db/Print: add "noexcept" 2018-01-21 11:32:22 +01:00
Max Kellermann
2b67a14155 SongPrint: add "noexcept" 2018-01-21 11:32:10 +01:00
Max Kellermann
eac8caa4de TagPrint: add "noexcept" 2018-01-21 11:30:47 +01:00
Max Kellermann
61eb2aa328 storage/Interface: wrap StorageDirectoryReader in std::unique_ptr 2018-01-21 11:04:30 +01:00
Max Kellermann
a9847ebf54 playlist/Queue: use "auto" 2018-01-20 20:14:19 +01:00
Max Kellermann
4ca1089f60 input/TextInputStream: ReadLine() throws exception on error 2018-01-20 20:13:08 +01:00
Max Kellermann
926d4150c5 playlist/SongEnumerator: allow NextSong() to throw exceptions 2018-01-20 20:11:15 +01:00
Max Kellermann
6f267adc14 playlist/SongEnumerator: add "noexcept" 2018-01-20 20:10:46 +01:00
Max Kellermann
0095e2d7d8 playlist/Plugin: use std::unique_ptr<SongEnumerator> 2018-01-20 19:59:42 +01:00
Max Kellermann
147f64e1fc playlist/Plugin: remove useless wrapper functions 2018-01-20 19:58:11 +01:00
Max Kellermann
b8e5932506 tag/{Tag,Builder}: use std::lock_guard 2018-01-20 19:53:38 +01:00
Max Kellermann
1ea04cd8cc tag/Pool: add "noexcept" 2018-01-20 19:53:38 +01:00
Max Kellermann
b334643b68 tag/Pool: use C++11 initializers 2018-01-20 19:53:38 +01:00
Max Kellermann
6e6a0275b5 tag/Pool: use uint8_t instead of unsigned char 2018-01-20 19:53:38 +01:00
Max Kellermann
f5590806e0 tag/Handler: add "noexcept" 2018-01-20 19:53:38 +01:00
Max Kellermann
f686e838fe tag/Builder: add "noexcept" 2018-01-20 19:53:38 +01:00
Max Kellermann
ea8642dec9 tag/Builder: use C++11 initializers 2018-01-20 19:53:38 +01:00
Max Kellermann
eef5b58211 tag/Tag: add "noexcept" 2018-01-20 19:39:46 +01:00
Max Kellermann
1de68b72b9 tag/Tag: use C++11 initializers 2018-01-20 19:39:06 +01:00
Max Kellermann
720df085e5 input/TextInputStream: add "noexcept" 2018-01-20 19:35:09 +01:00
Max Kellermann
f90f17227b input/Domain: remove obsolete variable 2018-01-20 19:19:29 +01:00
Max Kellermann
36f7eaadef input/LocalOpen: make assert() more strict 2018-01-20 19:15:51 +01:00
Max Kellermann
1eb9300658 input/LocalOpen: include cleanup 2018-01-20 19:14:42 +01:00
Max Kellermann
27e4656515 input/{file,archive}: remove the dummy InputPlugin instances
These have no use, exception for creating an artificial dependency
from input_plugins[] to all archive plugins.
2018-01-20 19:07:34 +01:00
Max Kellermann
e0a09dbd69 input/thread: add "noexcept" 2018-01-20 18:54:29 +01:00
Max Kellermann
30832ab33a input/InputStream: add "noexcept" to constructor 2018-01-20 18:53:37 +01:00
Max Kellermann
d20b545a94 input/InputStream: remove methods Lock() and Unlock()
These methods are not exception-safe; std::lock_guard should better be
used.
2018-01-20 18:51:23 +01:00
Max Kellermann
20d01be1ad input/mms: use class ScopeUnlock 2018-01-20 18:51:00 +01:00
Max Kellermann
d6b62d7512 input/tidal: delete request objects upon completion 2018-01-20 00:34:44 +01:00
Max Kellermann
ab9d9541a8 input/tidal: pass std::string by value
Don't keep any references to the owner, as the handler method may
delete the owner.
2018-01-20 00:33:01 +01:00
Max Kellermann
c3b8568560 lib/curl/Handler: add API documentation 2018-01-20 00:28:54 +01:00
Max Kellermann
8bd95a4eb1 db/update/Container: include cleanup 2018-01-20 00:25:57 +01:00
Max Kellermann
2dac3ef58e client: eliminate client_printf() 2018-01-20 00:24:18 +01:00
Max Kellermann
86c7ab29f4 client/Idle: pass Response instead of Client to WriteIdleResponse() 2018-01-20 00:22:47 +01:00
Max Kellermann
db89e5ec28 client/Idle: move code to WriteIdleResponse() 2018-01-20 00:22:30 +01:00
Max Kellermann
d096f75fb8 client/Idle: apply idle_subscriptions mask once 2018-01-20 00:22:09 +01:00
Max Kellermann
854424a758 client/Idle: use std::exchange() 2018-01-20 00:21:26 +01:00
Max Kellermann
fe5d0ce827 Merge branch 'v0.20.x' 2018-01-20 00:04:35 +01:00
Max Kellermann
8376578921 db/simple/Mount: drop mount point prefix from LOCATE_TAG_BASE_TYPE
Fixes search within mount points, resulting in error "No such
directory".

Closes #190
2018-01-19 23:52:57 +01:00
Max Kellermann
ed2354cd9d SongFilter: allow copying items 2018-01-19 23:52:03 +01:00
Max Kellermann
386688b87a SongFilter: use std::string instead of AllocatedString 2018-01-19 23:51:42 +01:00
Max Kellermann
38d56dddf1 lib/icu/Compare: allow copying 2018-01-19 23:49:50 +01:00
Max Kellermann
3ca80a7336 util/RefCount, db/simple/Mount: remove obsolete libc++ workarounds
No longer a problem with NDK r16.
2018-01-19 23:19:46 +01:00
Max Kellermann
2fed3b5e5b input/Tidal*Request: add Start() method
The caller should be able to safely store the instance pointer before
his callback is invoked.
2018-01-19 16:03:05 +01:00
Max Kellermann
1ecad094bd input/tidal: add API documentation 2018-01-19 16:03:03 +01:00
Stefan Roellin
187cc573a7 db/upnp/Directory: fix parsing of durations with fractions of seconds
The duration of a song can have fractions of seconds
(quote from http://www.upnp.org/schemas/av/didl-lite-v2.xsd):

    The format of the duration string is:
    H+:MM:SS[.F+], or H+:MM:SS[.F0/F1]
    Where:
    +H              one or more digits to indicate elapsed hours,
    MM              exactly 2 digits to indicate minutes (00 to 59),
    SS              exactly 2 digits to indicate seconds (00 to 59),
    F+              any number of digits (including no digits) to indicate fractions of seconds,
    F0/F1   a fraction, with F0 and F1 at least one digit long,
                    and F0 < F1.
    The string may be preceded by an optional + or - sign, and the
    decimal point itself may be omitted if there are no fractional  seconds digits.

Until now, a duration with fractions of seconds could not be parsed and
resulted in an unknown duration. Only durations in the format "H+:MM:SS"
were feasible. This commit enables to read durations in the first format,
i.e. "H+:MM:SS[.F+]"
2018-01-19 15:53:18 +01:00
Max Kellermann
0ead72a404 lib/upnp/ContentDirectoryService: work around GCC 5 bug
Closes #193
2018-01-18 21:55:58 +01:00
Max Kellermann
bbc5212436 fs/AllocatedPath: make the nullptr_t constructor public 2018-01-17 12:17:41 +01:00
Max Kellermann
c4f7740b80 fs/Path: replace method Null() with nullptr_t constructor 2018-01-17 12:16:21 +01:00
Max Kellermann
3e2e0d062b util/OptionParser: support option values 2018-01-17 11:14:57 +01:00
Max Kellermann
c45fe3517c util/OptionParser: add struct Result
Prepare for option values.
2018-01-17 11:08:05 +01:00
Max Kellermann
d588da69e5 CommandLine: convert pointer to reference 2018-01-17 11:08:03 +01:00
Max Kellermann
ec54a5c72c CommandLine: use C++11 initializers 2018-01-17 11:08:02 +01:00