SongFilter: new filter "modified-since"
This commit is contained in:
parent
14908b7f28
commit
5d2506e697
1
NEWS
1
NEWS
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue