SongFilter: new filter "modified-since"

This commit is contained in:
Max Kellermann 2014-08-11 22:08:26 +02:00
parent 14908b7f28
commit 5d2506e697
4 changed files with 93 additions and 1 deletions

1
NEWS
View File

@ -7,6 +7,7 @@ ver 0.19 (not yet released)
- "idle" with unrecognized event name fails
- "list" on album artist falls back to the artist tag
- "list" and "count" allow grouping
- new "search"/"find" filter "modified-since"
* database
- proxy: forward "idle" events
- proxy: forward the "update" command

View File

@ -1553,7 +1553,7 @@ OK
<para>
Finds songs in the db that are exactly
<varname>WHAT</varname>. <varname>TYPE</varname> can
be any tag supported by MPD, or one of the three special
be any tag supported by MPD, or one of the special
parameters:
</para>
@ -1578,6 +1578,14 @@ OK
music directory)
</para>
</listitem>
<listitem>
<para>
<parameter>modified-since</parameter> compares the
file's time stamp with the given value (ISO 8601 or
UNIX time stamp)
</para>
</listitem>
</itemizedlist>
<para>

View File

@ -48,6 +48,9 @@ locate_parse_type(const char *str)
if (strcmp(str, "base") == 0)
return LOCATE_TAG_BASE_TYPE;
if (strcmp(str, "modified-since") == 0)
return LOCATE_TAG_MODIFIED_SINCE;
return tag_name_parse_i(str);
}
@ -66,6 +69,11 @@ SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
{
}
SongFilter::Item::Item(unsigned _tag, time_t _time)
:tag(_tag), time(_time)
{
}
bool
SongFilter::Item::StringMatch(const char *s) const
{
@ -128,6 +136,9 @@ SongFilter::Item::Match(const DetachedSong &song) const
if (tag == LOCATE_TAG_BASE_TYPE)
return uri_is_child_or_same(value.c_str(), song.GetURI());
if (tag == LOCATE_TAG_MODIFIED_SINCE)
return song.GetLastModified() >= time;
if (tag == LOCATE_TAG_FILE_TYPE)
return StringMatch(song.GetURI());
@ -142,6 +153,9 @@ SongFilter::Item::Match(const LightSong &song) const
return uri_is_child_or_same(value.c_str(), uri.c_str());
}
if (tag == LOCATE_TAG_MODIFIED_SINCE)
return song.mtime >= time;
if (tag == LOCATE_TAG_FILE_TYPE) {
const auto uri = song.GetURI();
return StringMatch(uri.c_str());
@ -160,6 +174,58 @@ SongFilter::~SongFilter()
/* this destructor exists here just so it won't get inlined */
}
#if !defined(__GLIBC__) && !defined(WIN32)
/**
* Determine the time zone offset in a portable way.
*/
gcc_const
static time_t
GetTimeZoneOffset()
{
time_t t = 1234567890;
struct tm tm;
tm.tm_isdst = 0;
gmtime_r(&t, &tm);
return t - mktime(&tm);
}
#endif
gcc_pure
static time_t
ParseTimeStamp(const char *s)
{
assert(s != nullptr);
char *endptr;
unsigned long long value = strtoull(s, &endptr, 10);
if (*endptr == 0 && endptr > s)
/* it's an integral UNIX time stamp */
return (time_t)value;
#ifdef WIN32
/* TODO: emulate strptime()? */
return 0;
#else
/* try ISO 8601 */
struct tm tm;
const char *end = strptime(s, "%FT%TZ", &tm);
if (end == nullptr || *end != 0)
return 0;
#ifdef __GLIBC__
/* timegm() is a GNU extension */
return timegm(&tm);
#else
tm.tm_isdst = 0;
return mktime(&tm) + GetTimeZoneOffset();
#endif /* !__GLIBC__ */
#endif /* !WIN32 */
}
bool
SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
{
@ -175,6 +241,15 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
fold_case = false;
}
if (tag == LOCATE_TAG_MODIFIED_SINCE) {
time_t t = ParseTimeStamp(value);
if (t == 0)
return false;
items.push_back(Item(tag, t));
return true;
}
items.push_back(Item(tag, value, fold_case));
return true;
}

View File

@ -26,11 +26,13 @@
#include <string>
#include <stdint.h>
#include <time.h>
/**
* Limit the search to files within the given directory.
*/
#define LOCATE_TAG_BASE_TYPE (TAG_NUM_OF_ITEM_TYPES + 1)
#define LOCATE_TAG_MODIFIED_SINCE (TAG_NUM_OF_ITEM_TYPES + 2)
#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
@ -51,9 +53,15 @@ public:
std::string value;
/**
* For #LOCATE_TAG_MODIFIED_SINCE
*/
time_t time;
public:
gcc_nonnull(3)
Item(unsigned tag, const char *value, bool fold_case=false);
Item(unsigned tag, time_t time);
Item(const Item &other) = delete;
Item(Item &&) = default;