Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
2cfccc1c
Commit
2cfccc1c
authored
Jul 25, 2018
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SongFilter: make Item an interface
Prepare to allow more complex expressions.
parent
438366ef
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
296 additions
and
186 deletions
+296
-186
SongFilter.cxx
src/SongFilter.cxx
+130
-79
SongFilter.hxx
src/SongFilter.hxx
+123
-62
DatabaseCommands.cxx
src/command/DatabaseCommands.cxx
+1
-1
ProxyDatabasePlugin.cxx
src/db/plugins/ProxyDatabasePlugin.cxx
+31
-27
UpnpDatabasePlugin.cxx
src/db/plugins/upnp/UpnpDatabasePlugin.cxx
+11
-17
No files found.
src/SongFilter.cxx
View file @
2cfccc1c
This diff is collapsed.
Click to expand it.
src/SongFilter.hxx
View file @
2cfccc1c
/*
/*
* Copyright 2003-201
7
The Music Player Daemon Project
* Copyright 2003-201
8
The Music Player Daemon Project
* http://www.musicpd.org
* http://www.musicpd.org
*
*
* This program is free software; you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
...
@@ -23,10 +23,13 @@
...
@@ -23,10 +23,13 @@
#include "lib/icu/Compare.hxx"
#include "lib/icu/Compare.hxx"
#include "Compiler.h"
#include "Compiler.h"
#include <memory>
#include <string>
#include <string>
#include <list>
#include <list>
#include <chrono>
#include <chrono>
#include <stdint.h>
/**
/**
* Limit the search to files within the given directory.
* Limit the search to files within the given directory.
*/
*/
...
@@ -42,9 +45,28 @@
...
@@ -42,9 +45,28 @@
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
template
<
typename
T
>
struct
ConstBuffer
;
template
<
typename
T
>
struct
ConstBuffer
;
enum
TagType
:
uint8_t
;
struct
Tag
;
struct
Tag
;
struct
TagItem
;
struct
TagItem
;
struct
LightSong
;
struct
LightSong
;
class
ISongFilter
;
using
ISongFilterPtr
=
std
::
unique_ptr
<
ISongFilter
>
;
class
ISongFilter
{
public
:
virtual
~
ISongFilter
()
noexcept
{}
virtual
ISongFilterPtr
Clone
()
const
noexcept
=
0
;
/**
* Convert this object into an "expression". This is
* only useful for debugging.
*/
virtual
std
::
string
ToExpression
()
const
noexcept
=
0
;
gcc_pure
virtual
bool
Match
(
const
LightSong
&
song
)
const
noexcept
=
0
;
};
class
StringFilter
{
class
StringFilter
{
std
::
string
value
;
std
::
string
value
;
...
@@ -55,8 +77,6 @@ class StringFilter {
...
@@ -55,8 +77,6 @@ class StringFilter {
IcuCompare
fold_case
;
IcuCompare
fold_case
;
public
:
public
:
StringFilter
()
=
default
;
template
<
typename
V
>
template
<
typename
V
>
StringFilter
(
V
&&
_value
,
bool
_fold_case
)
StringFilter
(
V
&&
_value
,
bool
_fold_case
)
:
value
(
std
::
forward
<
V
>
(
_value
)),
:
value
(
std
::
forward
<
V
>
(
_value
)),
...
@@ -80,82 +100,129 @@ public:
...
@@ -80,82 +100,129 @@ public:
bool
Match
(
const
char
*
s
)
const
noexcept
;
bool
Match
(
const
char
*
s
)
const
noexcept
;
};
};
class
SongFilter
{
class
UriSongFilter
final
:
public
ISongFilter
{
StringFilter
filter
;
bool
negated
;
public
:
public
:
class
Item
{
template
<
typename
V
>
unsigned
tag
;
UriSongFilter
(
V
&&
_value
,
bool
fold_case
,
bool
_negated
)
:
filter
(
std
::
forward
<
V
>
(
_value
),
fold_case
),
negated
(
_negated
)
{}
const
auto
&
GetValue
()
const
noexcept
{
return
filter
.
GetValue
();
}
bool
GetFoldCase
()
const
{
return
filter
.
GetFoldCase
();
}
bool
IsNegated
()
const
noexcept
{
return
negated
;
}
ISongFilterPtr
Clone
()
const
noexcept
override
{
return
std
::
make_unique
<
UriSongFilter
>
(
*
this
);
}
std
::
string
ToExpression
()
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
};
bool
negated
=
false
;
class
BaseSongFilter
final
:
public
ISongFilter
{
std
::
string
value
;
StringFilter
string_filter
;
public
:
BaseSongFilter
(
const
BaseSongFilter
&
)
=
default
;
/**
template
<
typename
V
>
* For #LOCATE_TAG_MODIFIED_SINCE
explicit
BaseSongFilter
(
V
&&
_value
)
*/
:
value
(
std
::
forward
<
V
>
(
_value
))
{}
std
::
chrono
::
system_clock
::
time_point
time
;
public
:
const
char
*
GetValue
()
const
noexcept
{
Item
(
unsigned
tag
,
std
::
string
&&
_value
,
bool
fold_case
=
false
);
return
value
.
c_str
(
);
Item
(
unsigned
tag
,
std
::
chrono
::
system_clock
::
time_point
time
);
}
/**
ISongFilterPtr
Clone
()
const
noexcept
override
{
* Convert this object into an "expression". This is
return
std
::
make_unique
<
BaseSongFilter
>
(
*
this
);
* only useful for debugging.
}
*/
std
::
string
ToExpression
()
const
noexcept
;
std
::
string
ToExpression
()
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
};
unsigned
GetTag
()
const
{
class
TagSongFilter
final
:
public
ISongFilter
{
return
tag
;
TagType
type
;
}
bool
IsNegated
()
const
noexcept
{
bool
negated
;
return
negated
;
}
void
SetNegated
(
bool
_negated
=
true
)
noexcept
{
StringFilter
filter
;
negated
=
_negated
;
}
bool
GetFoldCase
()
const
{
public
:
return
string_filter
.
GetFoldCase
();
template
<
typename
V
>
}
TagSongFilter
(
TagType
_type
,
V
&&
_value
,
bool
fold_case
,
bool
_negated
)
:
type
(
_type
),
negated
(
_negated
),
filter
(
std
::
forward
<
V
>
(
_value
),
fold_case
)
{}
const
char
*
GetValu
e
()
const
{
TagType
GetTagTyp
e
()
const
{
return
string_filter
.
GetValue
().
c_str
()
;
return
type
;
}
}
private
:
const
auto
&
GetValue
()
const
noexcept
{
/* note: the "NN" suffix means "no negation", i.e. the
return
filter
.
GetValue
();
method pretends negation is unset, and the caller
}
is responsibly for considering it */
gcc_pure
bool
GetFoldCase
()
const
{
bool
MatchNN
(
const
TagItem
&
tag_item
)
const
noexcept
;
return
filter
.
GetFoldCase
();
}
gcc_pure
bool
IsNegated
()
const
noexcept
{
bool
MatchNN
(
const
Tag
&
tag
)
const
noexcept
;
return
negated
;
}
gcc_pure
ISongFilterPtr
Clone
()
const
noexcept
override
{
bool
MatchNN
(
const
LightSong
&
song
)
const
noexcept
;
return
std
::
make_unique
<
TagSongFilter
>
(
*
this
);
}
public
:
std
::
string
ToExpression
()
const
noexcept
override
;
gcc_pure
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
{
return
MatchNN
(
song
)
!=
IsNegated
();
}
};
private
:
private
:
std
::
list
<
Item
>
items
;
bool
MatchNN
(
const
Tag
&
tag
)
const
noexcept
;
bool
MatchNN
(
const
TagItem
&
tag
)
const
noexcept
;
};
class
ModifiedSinceSongFilter
final
:
public
ISongFilter
{
std
::
chrono
::
system_clock
::
time_point
value
;
public
:
explicit
ModifiedSinceSongFilter
(
std
::
chrono
::
system_clock
::
time_point
_value
)
noexcept
:
value
(
_value
)
{}
ISongFilterPtr
Clone
()
const
noexcept
override
{
return
std
::
make_unique
<
ModifiedSinceSongFilter
>
(
*
this
);
}
std
::
string
ToExpression
()
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
};
class
SongFilter
{
std
::
list
<
ISongFilterPtr
>
items
;
public
:
public
:
SongFilter
()
=
default
;
SongFilter
()
=
default
;
gcc_nonnull
(
3
)
gcc_nonnull
(
3
)
SongFilter
(
unsigned
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
SongFilter
(
TagType
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
~
SongFilter
();
~
SongFilter
();
SongFilter
(
SongFilter
&&
)
=
default
;
SongFilter
&
operator
=
(
SongFilter
&&
)
=
default
;
/**
/**
* Convert this object into an "expression". This is
* Convert this object into an "expression". This is
* only useful for debugging.
* only useful for debugging.
...
@@ -163,7 +230,7 @@ public:
...
@@ -163,7 +230,7 @@ public:
std
::
string
ToExpression
()
const
noexcept
;
std
::
string
ToExpression
()
const
noexcept
;
private
:
private
:
const
char
*
ParseExpression
(
const
char
*
s
,
bool
fold_case
=
false
);
ISongFilterPtr
ParseExpression
(
const
char
*&
s
,
bool
fold_case
=
false
);
gcc_nonnull
(
2
,
3
)
gcc_nonnull
(
2
,
3
)
void
Parse
(
const
char
*
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
void
Parse
(
const
char
*
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
...
@@ -177,7 +244,7 @@ public:
...
@@ -177,7 +244,7 @@ public:
gcc_pure
gcc_pure
bool
Match
(
const
LightSong
&
song
)
const
noexcept
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
;
const
std
::
list
<
Item
>
&
GetItems
()
const
noexcept
{
const
auto
&
GetItems
()
const
noexcept
{
return
items
;
return
items
;
}
}
...
@@ -190,13 +257,7 @@ public:
...
@@ -190,13 +257,7 @@ public:
* Is there at least one item with "fold case" enabled?
* Is there at least one item with "fold case" enabled?
*/
*/
gcc_pure
gcc_pure
bool
HasFoldCase
()
const
noexcept
{
bool
HasFoldCase
()
const
noexcept
;
for
(
const
auto
&
i
:
items
)
if
(
i
.
GetFoldCase
())
return
true
;
return
false
;
}
/**
/**
* Does this filter contain constraints other than "base"?
* Does this filter contain constraints other than "base"?
...
...
src/command/DatabaseCommands.cxx
View file @
2cfccc1c
...
@@ -248,7 +248,7 @@ handle_list(Client &client, Request args, Response &r)
...
@@ -248,7 +248,7 @@ handle_list(Client &client, Request args, Response &r)
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
}
}
filter
.
reset
(
new
SongFilter
(
(
unsigned
)
TAG_ARTIST
,
filter
.
reset
(
new
SongFilter
(
TAG_ARTIST
,
args
.
shift
()));
args
.
shift
()));
}
}
...
...
src/db/plugins/ProxyDatabasePlugin.cxx
View file @
2cfccc1c
...
@@ -268,49 +268,53 @@ CheckError(struct mpd_connection *connection)
...
@@ -268,49 +268,53 @@ CheckError(struct mpd_connection *connection)
}
}
static
bool
static
bool
SendConstraints
(
mpd_connection
*
connection
,
const
SongFilter
::
Item
&
item
)
SendConstraints
(
mpd_connection
*
connection
,
const
ISongFilter
&
f
)
{
{
switch
(
item
.
GetTag
())
{
if
(
auto
t
=
dynamic_cast
<
const
TagSongFilter
*>
(
&
f
))
{
mpd_tag_type
tag
;
if
(
t
->
IsNegated
())
// TODO implement
#if LIBMPDCLIENT_CHECK_VERSION(2,9,0)
case
LOCATE_TAG_BASE_TYPE
:
if
(
mpd_connection_cmp_server_version
(
connection
,
0
,
18
,
0
)
<
0
)
/* requires MPD 0.18 */
return
true
;
return
true
;
return
mpd_search_add_base_constraint
(
connection
,
if
(
t
->
GetTagType
()
==
TAG_NUM_OF_ITEM_TYPES
)
MPD_OPERATOR_DEFAULT
,
return
mpd_search_add_any_tag_constraint
(
connection
,
item
.
GetValue
());
MPD_OPERATOR_DEFAULT
,
#endif
t
->
GetValue
().
c_str
());
case
LOCATE_TAG_FILE_TYPE
:
return
mpd_search_add_uri_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
item
.
GetValue
());
case
LOCATE_TAG_ANY_TYPE
:
return
mpd_search_add_any_tag_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
item
.
GetValue
());
default
:
const
auto
tag
=
Convert
(
t
->
GetTagType
());
tag
=
Convert
(
TagType
(
item
.
GetTag
()));
if
(
tag
==
MPD_TAG_COUNT
)
if
(
tag
==
MPD_TAG_COUNT
)
return
true
;
return
true
;
return
mpd_search_add_tag_constraint
(
connection
,
return
mpd_search_add_tag_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
MPD_OPERATOR_DEFAULT
,
tag
,
tag
,
item
.
GetValue
());
t
->
GetValue
().
c_str
());
}
}
else
if
(
auto
u
=
dynamic_cast
<
const
UriSongFilter
*>
(
&
f
))
{
if
(
u
->
IsNegated
())
// TODO implement
return
true
;
return
mpd_search_add_uri_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
u
->
GetValue
().
c_str
());
#if LIBMPDCLIENT_CHECK_VERSION(2,9,0)
}
else
if
(
auto
b
=
dynamic_cast
<
const
BaseSongFilter
*>
(
&
f
))
{
if
(
mpd_connection_cmp_server_version
(
connection
,
0
,
18
,
0
)
<
0
)
/* requires MPD 0.18 */
return
true
;
return
mpd_search_add_base_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
b
->
GetValue
());
#endif
}
else
return
true
;
}
}
static
bool
static
bool
SendConstraints
(
mpd_connection
*
connection
,
const
SongFilter
&
filter
)
SendConstraints
(
mpd_connection
*
connection
,
const
SongFilter
&
filter
)
{
{
for
(
const
auto
&
i
:
filter
.
GetItems
())
for
(
const
auto
&
i
:
filter
.
GetItems
())
if
(
!
SendConstraints
(
connection
,
i
))
if
(
!
SendConstraints
(
connection
,
*
i
))
return
false
;
return
false
;
return
true
;
return
true
;
...
...
src/db/plugins/upnp/UpnpDatabasePlugin.cxx
View file @
2cfccc1c
...
@@ -254,9 +254,9 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
...
@@ -254,9 +254,9 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
std
::
string
cond
;
std
::
string
cond
;
for
(
const
auto
&
item
:
filter
->
GetItems
())
{
for
(
const
auto
&
item
:
filter
->
GetItems
())
{
switch
(
auto
tag
=
item
.
GetTag
(
))
{
if
(
auto
t
=
dynamic_cast
<
const
TagSongFilter
*>
(
item
.
get
()
))
{
case
LOCATE_TAG_ANY_TYPE
:
auto
tag
=
t
->
GetTagType
();
{
if
(
tag
==
TAG_NUM_OF_ITEM_TYPES
)
{
if
(
!
cond
.
empty
())
{
if
(
!
cond
.
empty
())
{
cond
+=
" and "
;
cond
+=
" and "
;
}
}
...
@@ -268,29 +268,21 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
...
@@ -268,29 +268,21 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
else
else
cond
+=
" or "
;
cond
+=
" or "
;
cond
+=
cap
;
cond
+=
cap
;
if
(
item
.
GetFoldCase
())
{
if
(
t
->
GetFoldCase
())
{
cond
+=
" contains "
;
cond
+=
" contains "
;
}
else
{
}
else
{
cond
+=
" = "
;
cond
+=
" = "
;
}
}
dquote
(
cond
,
item
.
GetValue
());
dquote
(
cond
,
t
->
GetValue
().
c_str
());
}
}
cond
+=
')'
;
cond
+=
')'
;
continue
;
}
}
break
;
default
:
/* Unhandled conditions like
LOCATE_TAG_BASE_TYPE or
LOCATE_TAG_FILE_TYPE won't have a
corresponding upnp prop, so they will be
skipped */
if
(
tag
==
TAG_ALBUM_ARTIST
)
if
(
tag
==
TAG_ALBUM_ARTIST
)
tag
=
TAG_ARTIST
;
tag
=
TAG_ARTIST
;
// TODO: support LOCATE_TAG_ANY_TYPE etc.
const
char
*
name
=
tag_table_lookup
(
upnp_tags
,
tag
);
const
char
*
name
=
tag_table_lookup
(
upnp_tags
,
TagType
(
tag
));
if
(
name
==
nullptr
)
if
(
name
==
nullptr
)
continue
;
continue
;
...
@@ -304,13 +296,15 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
...
@@ -304,13 +296,15 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
case-insensitive, but at least some servers
case-insensitive, but at least some servers
have the same convention as mpd (e.g.:
have the same convention as mpd (e.g.:
minidlna) */
minidlna) */
if
(
item
.
GetFoldCase
())
{
if
(
t
->
GetFoldCase
())
{
cond
+=
" contains "
;
cond
+=
" contains "
;
}
else
{
}
else
{
cond
+=
" = "
;
cond
+=
" = "
;
}
}
dquote
(
cond
,
item
.
GetValue
());
dquote
(
cond
,
t
->
GetValue
().
c_str
());
}
}
// TODO: support other ISongFilter implementations
}
}
return
server
.
search
(
handle
,
objid
,
cond
.
c_str
());
return
server
.
search
(
handle
,
objid
,
cond
.
c_str
());
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment