This commit is contained in:
Max Kellermann 2024-02-15 11:28:11 +01:00
commit 9eb26ab90c
8 changed files with 130 additions and 1 deletions

2
NEWS
View File

@ -10,7 +10,7 @@ ver 0.24 (not yet released)
- show PCRE support in "config" response
- apply Unicode normalization to case-insensitive filter expressions
- stickers on playlists and some tag types
- new command "stickernames"
- new commands "stickernames" and "playlistlength"
- new "search"/"find" filter "added-since"
- allow range in listplaylist and listplaylistinfo
* database

View File

@ -1003,6 +1003,19 @@ remote playlists (absolute URI with a supported scheme).
The second parameter can be a range. [#since_0_23_3]_
.. _command_playlistlength:
:command:`playlistlength {NAME}`
Count the number of songs and their total playtime (seconds) in the
playlist.
Example::
playlistlength example
songs: 10
playtime: 8192
OK
.. _command_playlistmove:
:command:`playlistmove {NAME} [{FROM} | {START:END}] {TO}`

View File

@ -387,6 +387,7 @@ sources = [
'src/PlaylistError.cxx',
'src/PlaylistPrint.cxx',
'src/PlaylistSave.cxx',
'src/playlist/Length.cxx',
'src/playlist/PlaylistStream.cxx',
'src/playlist/PlaylistMapper.cxx',
'src/playlist/PlaylistAny.cxx',

View File

@ -148,6 +148,7 @@ static constexpr struct command commands[] = {
{ "playlistfind", PERMISSION_READ, 1, -1, handle_playlistfind },
{ "playlistid", PERMISSION_READ, 0, 1, handle_playlistid },
{ "playlistinfo", PERMISSION_READ, 0, 1, handle_playlistinfo },
{ "playlistlength", PERMISSION_READ, 1, 1, handle_playlistlength },
{ "playlistmove", PERMISSION_CONTROL, 3, 3, handle_playlistmove },
{ "playlistsearch", PERMISSION_READ, 1, -1, handle_playlistsearch },
{ "plchanges", PERMISSION_READ, 1, 2, handle_plchanges },

View File

@ -17,6 +17,7 @@
#include "SongLoader.hxx"
#include "song/DetachedSong.hxx"
#include "BulkEdit.hxx"
#include "playlist/Length.hxx"
#include "playlist/PlaylistQueue.hxx"
#include "playlist/Print.hxx"
#include "TimePrint.hxx"
@ -153,6 +154,23 @@ handle_listplaylistinfo(Client &client, Request args, Response &r)
throw PlaylistError::NoSuchList();
}
CommandResult
handle_playlistlength(Client &client, Request args, Response &r)
{
const auto name = LocateUri(UriPluginKind::PLAYLIST, args.front(),
&client
#ifdef ENABLE_DATABASE
, nullptr
#endif
);
if (playlist_file_length(r, client.GetPartition(), SongLoader(client),
name))
return CommandResult::OK;
throw PlaylistError::NoSuchList();
}
CommandResult
handle_rm([[maybe_unused]] Client &client, Request args, [[maybe_unused]] Response &r)
{

View File

@ -26,6 +26,9 @@ handle_listplaylist(Client &client, Request request, Response &response);
CommandResult
handle_listplaylistinfo(Client &client, Request request, Response &response);
CommandResult
handle_playlistlength(Client &client, Request request, Response &response);
CommandResult
handle_rm(Client &client, Request request, Response &response);

69
src/playlist/Length.cxx Normal file
View File

@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#include "config.h"
#include "LocateUri.hxx"
#include "Length.hxx"
#include "PlaylistAny.hxx"
#include "PlaylistSong.hxx"
#include "SongEnumerator.hxx"
#include "SongPrint.hxx"
#include "song/DetachedSong.hxx"
#include "song/LightSong.hxx"
#include "fs/Traits.hxx"
#include "thread/Mutex.hxx"
#include "Partition.hxx"
#include "Instance.hxx"
#include <fmt/format.h>
static SignedSongTime get_duration(const DetachedSong &song) {
const auto duration = song.GetDuration();
return duration.IsNegative() ? (SignedSongTime)0 : song.GetDuration();
}
static void
playlist_provider_length(Response &r,
const SongLoader &loader,
const char *uri,
SongEnumerator &e) noexcept
{
const auto base_uri = uri != nullptr
? PathTraitsUTF8::GetParent(uri)
: ".";
std::unique_ptr<DetachedSong> song;
unsigned i = 0;
SignedSongTime playtime = (SignedSongTime)0;
while ((song = e.NextSong()) != nullptr) {
if (playlist_check_translate_song(*song, base_uri,
loader))
playtime += get_duration(*song);
i++;
}
r.Fmt(FMT_STRING("songs: {}\n"), i);
r.Fmt(FMT_STRING("playtime: {}\n"), playtime.RoundS());
}
bool
playlist_file_length(Response &r, Partition &partition,
const SongLoader &loader,
const LocatedUri &uri)
{
Mutex mutex;
#ifndef ENABLE_DATABASE
(void)partition;
#endif
auto playlist = playlist_open_any(uri,
#ifdef ENABLE_DATABASE
partition.instance.storage,
#endif
mutex);
if (playlist == nullptr)
return false;
playlist_provider_length(r, loader, uri.canonical_uri, *playlist);
return true;
}

24
src/playlist/Length.hxx Normal file
View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#ifndef MPD_PLAYLIST__LENGTH_HXX
#define MPD_PLAYLIST__LENGTH_HXX
#include "client/Response.hxx"
class SongLoader;
struct Partition;
/**
* Count the number of songs and their total playtime (seconds) in the
* playlist.
*
* @param uri the URI of the playlist file in UTF-8 encoding
* @return true on success, false if the playlist does not exist
*/
bool
playlist_file_length(Response &r, Partition &partition,
const SongLoader &loader,
const LocatedUri &uri);
#endif