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
8e5e97bf
Commit
8e5e97bf
authored
Apr 05, 2019
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
command: add command "getfingerprint"
A first use case for our libchromaprint integration added by commit
30e22b75
parent
17dd334b
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
420 additions
and
0 deletions
+420
-0
protocol.rst
doc/protocol.rst
+11
-0
meson.build
meson.build
+8
-0
AllCommands.cxx
src/command/AllCommands.cxx
+4
-0
FingerprintCommands.cxx
src/command/FingerprintCommands.cxx
+358
-0
FingerprintCommands.hxx
src/command/FingerprintCommands.hxx
+32
-0
DecoderClient.hxx
src/lib/chromaprint/DecoderClient.hxx
+7
-0
No files found.
doc/protocol.rst
View file @
8e5e97bf
...
...
@@ -824,6 +824,17 @@ The music database
don't this group tag. It exists only if at least one such song is
found.
:command:`getfingerprint {URI}`
Calculate the song's audio fingerprint. Example (abbreviated fingerprint)::
getfingerprint "foo/bar.ogg"
chromaprint: AQACcEmSREmWJJmkIT_6CCf64...
OK
This command is only available if MPD was built with
:file:`libchromaprint` (``-Dchromaprint=enabled``).
.. _command_find:
:command:`find {FILTER} [sort {TYPE}] [window {START:END}]`
...
...
meson.build
View file @
8e5e97bf
...
...
@@ -356,6 +356,13 @@ if sqlite_dep.found()
]
endif
if chromaprint_dep.found()
sources += [
'src/command/FingerprintCommands.cxx',
'src/lib/chromaprint/DecoderClient.cxx',
]
endif
basic = static_library(
'basic',
'src/ReplayGainInfo.cxx',
...
...
@@ -444,6 +451,7 @@ mpd = build_target(
sqlite_dep,
zeroconf_dep,
more_deps,
chromaprint_dep,
],
link_args: link_args,
install: not is_android and not is_haiku,
...
...
src/command/AllCommands.cxx
View file @
8e5e97bf
...
...
@@ -33,6 +33,7 @@
#include "NeighborCommands.hxx"
#include "ClientCommands.hxx"
#include "PartitionCommands.hxx"
#include "FingerprintCommands.hxx"
#include "OtherCommands.hxx"
#include "Permission.hxx"
#include "tag/Type.h"
...
...
@@ -107,6 +108,9 @@ static constexpr struct command commands[] = {
{
"find"
,
PERMISSION_READ
,
1
,
-
1
,
handle_find
},
{
"findadd"
,
PERMISSION_ADD
,
1
,
-
1
,
handle_findadd
},
#endif
#ifdef ENABLE_CHROMAPRINT
{
"getfingerprint"
,
PERMISSION_READ
,
1
,
1
,
handle_getfingerprint
},
#endif
{
"idle"
,
PERMISSION_READ
,
0
,
-
1
,
handle_idle
},
{
"kill"
,
PERMISSION_ADMIN
,
-
1
,
-
1
,
handle_kill
},
#ifdef ENABLE_DATABASE
...
...
src/command/FingerprintCommands.cxx
0 → 100644
View file @
8e5e97bf
/*
* Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 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.
*/
#include "config.h"
#include "FingerprintCommands.hxx"
#include "Request.hxx"
#include "LocateUri.hxx"
#include "lib/chromaprint/DecoderClient.hxx"
#include "decoder/DecoderAPI.hxx"
#include "decoder/DecoderList.hxx"
#include "storage/StorageInterface.hxx"
#include "client/Client.hxx"
#include "client/Response.hxx"
#include "client/ThreadBackgroundCommand.hxx"
#include "input/InputStream.hxx"
#include "input/LocalOpen.hxx"
#include "input/Handler.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "system/Error.hxx"
#include "util/MimeType.hxx"
#include "util/UriUtil.hxx"
class
GetChromaprintCommand
final
:
public
ThreadBackgroundCommand
,
ChromaprintDecoderClient
,
InputStreamHandler
{
Mutex
mutex
;
Cond
cond
;
const
std
::
string
uri
;
const
AllocatedPath
path
;
bool
cancel
=
false
;
public
:
GetChromaprintCommand
(
Client
&
_client
,
std
::
string
&&
_uri
,
AllocatedPath
&&
_path
)
noexcept
:
ThreadBackgroundCommand
(
_client
),
uri
(
std
::
move
(
_uri
)),
path
(
std
::
move
(
_path
))
{
}
protected
:
void
Run
()
override
;
void
SendResponse
(
Response
&
r
)
noexcept
override
{
r
.
Format
(
"chromaprint: %s
\n
"
,
GetFingerprint
().
c_str
());
}
void
CancelThread
()
noexcept
override
{
const
std
::
lock_guard
<
Mutex
>
lock
(
mutex
);
cancel
=
true
;
cond
.
signal
();
}
private
:
void
DecodeStream
(
InputStream
&
is
,
const
DecoderPlugin
&
plugin
);
bool
DecodeStream
(
InputStream
&
is
,
const
char
*
suffix
,
const
DecoderPlugin
&
plugin
);
void
DecodeStream
(
InputStream
&
is
);
bool
DecodeContainer
(
const
char
*
suffix
,
const
DecoderPlugin
&
plugin
);
bool
DecodeContainer
(
const
char
*
suffix
);
bool
DecodeFile
(
const
char
*
suffix
,
InputStream
&
is
,
const
DecoderPlugin
&
plugin
);
void
DecodeFile
();
/* virtual methods from class DecoderClient */
InputStreamPtr
OpenUri
(
const
char
*
uri
)
override
;
size_t
Read
(
InputStream
&
is
,
void
*
buffer
,
size_t
length
)
override
;
/* virtual methods from class InputStreamHandler */
void
OnInputStreamReady
()
noexcept
override
{
cond
.
signal
();
}
void
OnInputStreamAvailable
()
noexcept
override
{
cond
.
signal
();
}
};
inline
void
GetChromaprintCommand
::
DecodeStream
(
InputStream
&
input_stream
,
const
DecoderPlugin
&
plugin
)
{
assert
(
plugin
.
stream_decode
!=
nullptr
);
assert
(
input_stream
.
IsReady
());
if
(
cancel
)
throw
StopDecoder
();
/* rewind the stream, so each plugin gets a fresh start */
try
{
input_stream
.
Rewind
();
}
catch
(...)
{
}
const
ScopeUnlock
unlock
(
mutex
);
plugin
.
StreamDecode
(
*
this
,
input_stream
);
}
gcc_pure
static
bool
decoder_check_plugin_mime
(
const
DecoderPlugin
&
plugin
,
const
InputStream
&
is
)
noexcept
{
assert
(
plugin
.
stream_decode
!=
nullptr
);
const
char
*
mime_type
=
is
.
GetMimeType
();
return
mime_type
!=
nullptr
&&
plugin
.
SupportsMimeType
(
GetMimeTypeBase
(
mime_type
).
c_str
());
}
gcc_pure
static
bool
decoder_check_plugin_suffix
(
const
DecoderPlugin
&
plugin
,
const
char
*
suffix
)
noexcept
{
assert
(
plugin
.
stream_decode
!=
nullptr
);
return
suffix
!=
nullptr
&&
plugin
.
SupportsSuffix
(
suffix
);
}
gcc_pure
static
bool
decoder_check_plugin
(
const
DecoderPlugin
&
plugin
,
const
InputStream
&
is
,
const
char
*
suffix
)
noexcept
{
return
plugin
.
stream_decode
!=
nullptr
&&
(
decoder_check_plugin_mime
(
plugin
,
is
)
||
decoder_check_plugin_suffix
(
plugin
,
suffix
));
}
inline
bool
GetChromaprintCommand
::
DecodeStream
(
InputStream
&
is
,
const
char
*
suffix
,
const
DecoderPlugin
&
plugin
)
{
if
(
!
decoder_check_plugin
(
plugin
,
is
,
suffix
))
return
false
;
ChromaprintDecoderClient
::
Reset
();
DecodeStream
(
is
,
plugin
);
return
true
;
}
inline
void
GetChromaprintCommand
::
DecodeStream
(
InputStream
&
is
)
{
UriSuffixBuffer
suffix_buffer
;
const
char
*
const
suffix
=
uri_get_suffix
(
uri
.
c_str
(),
suffix_buffer
);
decoder_plugins_try
([
this
,
&
is
,
suffix
](
const
DecoderPlugin
&
plugin
){
return
DecodeStream
(
is
,
suffix
,
plugin
);
});
}
inline
bool
GetChromaprintCommand
::
DecodeContainer
(
const
char
*
suffix
,
const
DecoderPlugin
&
plugin
)
{
if
(
plugin
.
container_scan
==
nullptr
||
plugin
.
file_decode
==
nullptr
||
!
plugin
.
SupportsSuffix
(
suffix
))
return
false
;
ChromaprintDecoderClient
::
Reset
();
plugin
.
FileDecode
(
*
this
,
path
);
return
IsReady
();
}
inline
bool
GetChromaprintCommand
::
DecodeContainer
(
const
char
*
suffix
)
{
return
decoder_plugins_try
([
this
,
suffix
](
const
DecoderPlugin
&
plugin
){
return
DecodeContainer
(
suffix
,
plugin
);
});
}
inline
bool
GetChromaprintCommand
::
DecodeFile
(
const
char
*
suffix
,
InputStream
&
is
,
const
DecoderPlugin
&
plugin
)
{
if
(
!
plugin
.
SupportsSuffix
(
suffix
))
return
false
;
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
if
(
cancel
)
throw
StopDecoder
();
}
ChromaprintDecoderClient
::
Reset
();
if
(
plugin
.
file_decode
!=
nullptr
)
{
plugin
.
FileDecode
(
*
this
,
path
);
return
IsReady
();
}
else
if
(
plugin
.
stream_decode
!=
nullptr
)
{
plugin
.
StreamDecode
(
*
this
,
is
);
return
IsReady
();
}
else
return
false
;
}
inline
void
GetChromaprintCommand
::
DecodeFile
()
{
const
char
*
suffix
=
uri_get_suffix
(
uri
.
c_str
());
if
(
suffix
==
nullptr
)
return
;
InputStreamPtr
input_stream
;
try
{
input_stream
=
OpenLocalInputStream
(
path
,
mutex
);
}
catch
(
const
std
::
system_error
&
e
)
{
if
(
IsPathNotFound
(
e
)
&&
/* ENOTDIR means this may be a path inside a
"container" file */
DecodeContainer
(
suffix
))
return
;
throw
;
}
assert
(
input_stream
);
auto
&
is
=
*
input_stream
;
decoder_plugins_try
([
this
,
suffix
,
&
is
](
const
DecoderPlugin
&
plugin
){
return
DecodeFile
(
suffix
,
is
,
plugin
);
});
}
void
GetChromaprintCommand
::
Run
()
try
{
if
(
!
path
.
IsNull
())
DecodeFile
();
else
DecodeStream
(
*
OpenUri
(
uri
.
c_str
()));
ChromaprintDecoderClient
::
Finish
();
}
catch
(
StopDecoder
)
{
}
InputStreamPtr
GetChromaprintCommand
::
OpenUri
(
const
char
*
uri2
)
{
if
(
cancel
)
throw
StopDecoder
();
auto
is
=
InputStream
::
Open
(
uri2
,
mutex
);
is
->
SetHandler
(
this
);
const
std
::
lock_guard
<
Mutex
>
lock
(
mutex
);
while
(
true
)
{
if
(
cancel
)
throw
StopDecoder
();
is
->
Update
();
if
(
is
->
IsReady
())
{
is
->
Check
();
return
is
;
}
cond
.
wait
(
mutex
);
}
}
size_t
GetChromaprintCommand
::
Read
(
InputStream
&
is
,
void
*
buffer
,
size_t
length
)
{
/* overriding ChromaprintDecoderClient's implementation to
make it cancellable */
if
(
length
==
0
)
return
0
;
std
::
lock_guard
<
Mutex
>
lock
(
mutex
);
while
(
true
)
{
if
(
cancel
)
return
0
;
if
(
is
.
IsAvailable
())
break
;
cond
.
wait
(
mutex
);
}
return
is
.
Read
(
buffer
,
length
);
}
CommandResult
handle_getfingerprint
(
Client
&
client
,
Request
args
,
Response
&
)
{
const
char
*
_uri
=
args
.
front
();
auto
lu
=
LocateUri
(
_uri
,
&
client
#ifdef ENABLE_DATABASE
,
nullptr
#endif
);
std
::
string
uri
=
lu
.
canonical_uri
;
switch
(
lu
.
type
)
{
case
LocatedUri
:
:
Type
::
ABSOLUTE
:
break
;
case
LocatedUri
:
:
Type
::
PATH
:
break
;
case
LocatedUri
:
:
Type
::
RELATIVE
:
#ifdef ENABLE_DATABASE
{
const
auto
*
storage
=
client
.
GetStorage
();
if
(
storage
==
nullptr
)
throw
ProtocolError
(
ACK_ERROR_NO_EXIST
,
"No database"
);
lu
.
path
=
storage
->
MapFS
(
lu
.
canonical_uri
);
if
(
lu
.
path
.
IsNull
())
{
uri
=
storage
->
MapUTF8
(
lu
.
canonical_uri
);
if
(
uri_has_scheme
(
uri
.
c_str
()))
throw
ProtocolError
(
ACK_ERROR_NO_EXIST
,
"No such song"
);
}
}
#else
throw
ProtocolError
(
ACK_ERROR_NO_EXIST
,
"No database"
);
#endif
}
auto
cmd
=
std
::
make_unique
<
GetChromaprintCommand
>
(
client
,
std
::
move
(
uri
),
std
::
move
(
lu
.
path
));
cmd
->
Start
();
client
.
SetBackgroundCommand
(
std
::
move
(
cmd
));
return
CommandResult
::
BACKGROUND
;
}
src/command/FingerprintCommands.hxx
0 → 100644
View file @
8e5e97bf
/*
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 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.
*/
#ifndef MPD_FINGERPRINT_COMMANDS_HXX
#define MPD_FINGERPRINT_COMMANDS_HXX
#include "CommandResult.hxx"
class
Client
;
class
Request
;
class
Response
;
CommandResult
handle_getfingerprint
(
Client
&
client
,
Request
request
,
Response
&
response
);
#endif
src/lib/chromaprint/DecoderClient.hxx
View file @
8e5e97bf
...
...
@@ -45,6 +45,13 @@ public:
ChromaprintDecoderClient
();
~
ChromaprintDecoderClient
()
noexcept
;
bool
IsReady
()
const
noexcept
{
return
ready
;
}
void
Reset
()
noexcept
{
}
void
Finish
();
std
::
string
GetFingerprint
()
const
{
...
...
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