Playlist: convert functions to methods
This commit is contained in:
parent
400ff1c812
commit
52638c68f5
@ -113,7 +113,6 @@ mpd_headers = \
|
||||
src/page.h \
|
||||
src/Playlist.hxx \
|
||||
src/playlist_error.h \
|
||||
src/PlaylistInternal.hxx \
|
||||
src/playlist_plugin.h \
|
||||
src/playlist_list.h \
|
||||
src/playlist/extm3u_playlist_plugin.h \
|
||||
|
@ -30,8 +30,7 @@ static bool
|
||||
AddToQueue(Partition &partition, song &song, GError **error_r)
|
||||
{
|
||||
enum playlist_result result =
|
||||
playlist_append_song(&partition.playlist, &partition.pc,
|
||||
&song, NULL);
|
||||
partition.playlist.AppendSong(partition.pc, &song, NULL);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS) {
|
||||
g_set_error(error_r, playlist_quark(), result,
|
||||
"Playlist error");
|
||||
|
@ -38,6 +38,128 @@ struct Partition {
|
||||
:playlist(max_length),
|
||||
pc(buffer_chunks, buffered_before_play) {
|
||||
}
|
||||
|
||||
void ClearQueue() {
|
||||
playlist.Clear(pc);
|
||||
}
|
||||
|
||||
enum playlist_result AppendFile(const char *path_fs,
|
||||
unsigned *added_id=nullptr) {
|
||||
return playlist.AppendFile(pc, path_fs, added_id);
|
||||
}
|
||||
|
||||
enum playlist_result AppendURI(const char *uri_utf8,
|
||||
unsigned *added_id=nullptr) {
|
||||
return playlist.AppendURI(pc, uri_utf8, added_id);
|
||||
}
|
||||
|
||||
enum playlist_result DeletePosition(unsigned position) {
|
||||
return playlist.DeletePosition(pc, position);
|
||||
}
|
||||
|
||||
enum playlist_result DeleteId(unsigned id) {
|
||||
return playlist.DeleteId(pc, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a range of songs from the playlist.
|
||||
*
|
||||
* @param start the position of the first song to delete
|
||||
* @param end the position after the last song to delete
|
||||
*/
|
||||
enum playlist_result DeleteRange(unsigned start, unsigned end) {
|
||||
return playlist.DeleteRange(pc, start, end);
|
||||
}
|
||||
|
||||
void DeleteSong(const song &song) {
|
||||
playlist.DeleteSong(pc, song);
|
||||
}
|
||||
|
||||
void Shuffle(unsigned start, unsigned end) {
|
||||
playlist.Shuffle(pc, start, end);
|
||||
}
|
||||
|
||||
enum playlist_result MoveRange(unsigned start, unsigned end, int to) {
|
||||
return playlist.MoveRange(pc, start, end, to);
|
||||
}
|
||||
|
||||
enum playlist_result MoveId(unsigned id, int to) {
|
||||
return playlist.MoveId(pc, id, to);
|
||||
}
|
||||
|
||||
enum playlist_result SwapPositions(unsigned song1, unsigned song2) {
|
||||
return playlist.SwapPositions(pc, song1, song2);
|
||||
}
|
||||
|
||||
enum playlist_result SwapIds(unsigned id1, unsigned id2) {
|
||||
return playlist.SwapIds(pc, id1, id2);
|
||||
}
|
||||
|
||||
enum playlist_result SetPriorityRange(unsigned start_position,
|
||||
unsigned end_position,
|
||||
uint8_t priority) {
|
||||
return playlist.SetPriorityRange(pc,
|
||||
start_position, end_position,
|
||||
priority);
|
||||
}
|
||||
|
||||
enum playlist_result SetPriorityId(unsigned song_id,
|
||||
uint8_t priority) {
|
||||
return playlist.SetPriorityId(pc, song_id, priority);
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
playlist.Stop(pc);
|
||||
}
|
||||
|
||||
enum playlist_result PlayPosition(int position) {
|
||||
return playlist.PlayPosition(pc, position);
|
||||
}
|
||||
|
||||
enum playlist_result PlayId(int id) {
|
||||
return playlist.PlayId(pc, id);
|
||||
}
|
||||
|
||||
void PlayNext() {
|
||||
return playlist.PlayNext(pc);
|
||||
}
|
||||
|
||||
void PlayPrevious() {
|
||||
return playlist.PlayPrevious(pc);
|
||||
}
|
||||
|
||||
enum playlist_result SeekSongPosition(unsigned song_position,
|
||||
float seek_time) {
|
||||
return playlist.SeekSongPosition(pc, song_position, seek_time);
|
||||
}
|
||||
|
||||
enum playlist_result SeekSongId(unsigned song_id, float seek_time) {
|
||||
return playlist.SeekSongId(pc, song_id, seek_time);
|
||||
}
|
||||
|
||||
enum playlist_result SeekCurrent(float seek_time, bool relative) {
|
||||
return playlist.SeekCurrent(pc, seek_time, relative);
|
||||
}
|
||||
|
||||
void SetRepeat(bool new_value) {
|
||||
playlist.SetRepeat(pc, new_value);
|
||||
}
|
||||
|
||||
bool GetRandom() const {
|
||||
return playlist.GetRandom();
|
||||
}
|
||||
|
||||
void SetRandom(bool new_value) {
|
||||
playlist.SetRandom(pc, new_value);
|
||||
}
|
||||
|
||||
void SetSingle(bool new_value) {
|
||||
playlist.SetSingle(pc, new_value);
|
||||
}
|
||||
|
||||
void SetConsume(bool new_value) {
|
||||
playlist.SetConsume(new_value);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "ClientInternal.hxx"
|
||||
#include "Volume.hxx"
|
||||
#include "OutputAll.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "protocol/Result.hxx"
|
||||
#include "protocol/ArgParser.hxx"
|
||||
|
||||
@ -34,7 +35,6 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include "replay_gain_config.h"
|
||||
#include "PlayerControl.hxx"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
@ -62,12 +62,10 @@ enum command_return
|
||||
handle_play(Client *client, int argc, char *argv[])
|
||||
{
|
||||
int song = -1;
|
||||
enum playlist_result result;
|
||||
|
||||
if (argc == 2 && !check_int(client, &song, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
result = playlist_play(&client->playlist, client->player_control,
|
||||
song);
|
||||
enum playlist_result result = client->partition.PlayPosition(song);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -75,13 +73,11 @@ enum command_return
|
||||
handle_playid(Client *client, int argc, char *argv[])
|
||||
{
|
||||
int id = -1;
|
||||
enum playlist_result result;
|
||||
|
||||
if (argc == 2 && !check_int(client, &id, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
result = playlist_play_id(&client->playlist, client->player_control,
|
||||
id);
|
||||
enum playlist_result result = client->partition.PlayId(id);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -89,7 +85,7 @@ enum command_return
|
||||
handle_stop(Client *client,
|
||||
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
||||
{
|
||||
playlist_stop(&client->playlist, client->player_control);
|
||||
client->partition.Stop();
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -155,23 +151,23 @@ handle_status(Client *client,
|
||||
COMMAND_STATUS_MIXRAMPDELAY ": %f\n"
|
||||
COMMAND_STATUS_STATE ": %s\n",
|
||||
volume_level_get(),
|
||||
playlist_get_repeat(&playlist),
|
||||
playlist_get_random(&playlist),
|
||||
playlist_get_single(&playlist),
|
||||
playlist_get_consume(&playlist),
|
||||
playlist_get_version(&playlist),
|
||||
playlist_get_length(&playlist),
|
||||
playlist.GetRepeat(),
|
||||
playlist.GetRandom(),
|
||||
playlist.GetSingle(),
|
||||
playlist.GetConsume(),
|
||||
(unsigned long)playlist.GetVersion(),
|
||||
playlist.GetLength(),
|
||||
(int)(pc_get_cross_fade(client->player_control) + 0.5),
|
||||
pc_get_mixramp_db(client->player_control),
|
||||
pc_get_mixramp_delay(client->player_control),
|
||||
state);
|
||||
|
||||
song = playlist_get_current_song(&playlist);
|
||||
song = playlist.GetCurrentPosition();
|
||||
if (song >= 0) {
|
||||
client_printf(client,
|
||||
COMMAND_STATUS_SONG ": %i\n"
|
||||
COMMAND_STATUS_SONGID ": %u\n",
|
||||
song, playlist_get_song_id(&playlist, song));
|
||||
song, playlist.PositionToId(song));
|
||||
}
|
||||
|
||||
if (player_status.state != PLAYER_STATE_STOP) {
|
||||
@ -204,12 +200,12 @@ handle_status(Client *client,
|
||||
g_free(error);
|
||||
}
|
||||
|
||||
song = playlist_get_next_song(&playlist);
|
||||
song = playlist.GetNextPosition();
|
||||
if (song >= 0) {
|
||||
client_printf(client,
|
||||
COMMAND_STATUS_NEXTSONG ": %i\n"
|
||||
COMMAND_STATUS_NEXTSONGID ": %u\n",
|
||||
song, playlist_get_song_id(&playlist, song));
|
||||
song, playlist.PositionToId(song));
|
||||
}
|
||||
|
||||
return COMMAND_RETURN_OK;
|
||||
@ -226,7 +222,7 @@ handle_next(Client *client,
|
||||
const bool single = playlist.queue.single;
|
||||
playlist.queue.single = false;
|
||||
|
||||
playlist_next(&playlist, client->player_control);
|
||||
client->partition.PlayNext();
|
||||
|
||||
playlist.queue.single = single;
|
||||
return COMMAND_RETURN_OK;
|
||||
@ -236,7 +232,7 @@ enum command_return
|
||||
handle_previous(Client *client,
|
||||
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
||||
{
|
||||
playlist_previous(&client->playlist, client->player_control);
|
||||
client->partition.PlayPrevious();
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -247,7 +243,7 @@ handle_repeat(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!check_bool(client, &status, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
playlist_set_repeat(&client->playlist, client->player_control, status);
|
||||
client->partition.SetRepeat(status);
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -258,7 +254,7 @@ handle_single(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!check_bool(client, &status, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
playlist_set_single(&client->playlist, client->player_control, status);
|
||||
client->partition.SetSingle(status);
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -269,7 +265,7 @@ handle_consume(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!check_bool(client, &status, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
playlist_set_consume(&client->playlist, status);
|
||||
client->partition.SetConsume(status);
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -280,8 +276,8 @@ handle_random(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!check_bool(client, &status, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
playlist_set_random(&client->playlist, client->player_control, status);
|
||||
audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->playlist.queue.random));
|
||||
client->partition.SetRandom(status);
|
||||
audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->partition.GetRandom()));
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -297,15 +293,14 @@ enum command_return
|
||||
handle_seek(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned song, seek_time;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_unsigned(client, &song, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (!check_unsigned(client, &seek_time, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
result = playlist_seek_song(&client->playlist, client->player_control,
|
||||
song, seek_time);
|
||||
enum playlist_result result =
|
||||
client->partition.SeekSongPosition(song, seek_time);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -313,16 +308,14 @@ enum command_return
|
||||
handle_seekid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned id, seek_time;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_unsigned(client, &id, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (!check_unsigned(client, &seek_time, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
result = playlist_seek_song_id(&client->playlist,
|
||||
client->player_control,
|
||||
id, seek_time);
|
||||
enum playlist_result result =
|
||||
client->partition.SeekSongId(id, seek_time);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -336,9 +329,7 @@ handle_seekcur(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
enum playlist_result result =
|
||||
playlist_seek_current(&client->playlist,
|
||||
client->player_control,
|
||||
seek_time, relative);
|
||||
client->partition.SeekCurrent(seek_time, relative);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
|
264
src/Playlist.cxx
264
src/Playlist.cxx
@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "PlaylistInternal.hxx"
|
||||
#include "Playlist.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "song.h"
|
||||
|
||||
@ -41,14 +41,14 @@ playlist_increment_version_all(struct playlist *playlist)
|
||||
}
|
||||
|
||||
void
|
||||
playlist_tag_changed(struct playlist *playlist)
|
||||
playlist::TagChanged()
|
||||
{
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
return;
|
||||
|
||||
assert(playlist->current >= 0);
|
||||
assert(current >= 0);
|
||||
|
||||
playlist->queue.ModifyAtOrder(playlist->current);
|
||||
queue.ModifyAtOrder(current);
|
||||
idle_add(IDLE_PLAYLIST);
|
||||
}
|
||||
|
||||
@ -92,135 +92,117 @@ playlist_song_started(struct playlist *playlist, struct player_control *pc)
|
||||
playlist->queued = -1;
|
||||
|
||||
if(playlist->queue.consume)
|
||||
playlist_delete(playlist, pc,
|
||||
playlist->queue.OrderToPosition(current));
|
||||
playlist->DeleteOrder(*pc, current);
|
||||
|
||||
idle_add(IDLE_PLAYER);
|
||||
}
|
||||
|
||||
const struct song *
|
||||
playlist_get_queued_song(struct playlist *playlist)
|
||||
playlist::GetQueuedSong() const
|
||||
{
|
||||
if (!playlist->playing || playlist->queued < 0)
|
||||
return NULL;
|
||||
|
||||
return playlist->queue.GetOrder(playlist->queued);
|
||||
return playing && queued >= 0
|
||||
? queue.GetOrder(queued)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
playlist_update_queued_song(struct playlist *playlist,
|
||||
struct player_control *pc,
|
||||
const struct song *prev)
|
||||
playlist::UpdateQueuedSong(player_control &pc, const song *prev)
|
||||
{
|
||||
int next_order;
|
||||
const struct song *next_song;
|
||||
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
return;
|
||||
|
||||
assert(!playlist->queue.IsEmpty());
|
||||
assert((playlist->queued < 0) == (prev == NULL));
|
||||
assert(!queue.IsEmpty());
|
||||
assert((queued < 0) == (prev == NULL));
|
||||
|
||||
next_order = playlist->current >= 0
|
||||
? playlist->queue.GetNextOrder(playlist->current)
|
||||
const int next_order = current >= 0
|
||||
? queue.GetNextOrder(current)
|
||||
: 0;
|
||||
|
||||
if (next_order == 0 && playlist->queue.random &&
|
||||
!playlist->queue.single) {
|
||||
if (next_order == 0 && queue.random && !queue.single) {
|
||||
/* shuffle the song order again, so we get a different
|
||||
order each time the playlist is played
|
||||
completely */
|
||||
unsigned current_position =
|
||||
playlist->queue.OrderToPosition(playlist->current);
|
||||
const unsigned current_position =
|
||||
queue.OrderToPosition(current);
|
||||
|
||||
playlist->queue.ShuffleOrder();
|
||||
queue.ShuffleOrder();
|
||||
|
||||
/* make sure that the playlist->current still points to
|
||||
/* make sure that the current still points to
|
||||
the current song, after the song order has been
|
||||
shuffled */
|
||||
playlist->current =
|
||||
playlist->queue.PositionToOrder(current_position);
|
||||
current = queue.PositionToOrder(current_position);
|
||||
}
|
||||
|
||||
if (next_order >= 0)
|
||||
next_song = playlist->queue.GetOrder(next_order);
|
||||
else
|
||||
next_song = NULL;
|
||||
const struct song *const next_song = next_order >= 0
|
||||
? queue.GetOrder(next_order)
|
||||
: nullptr;
|
||||
|
||||
if (prev != NULL && next_song != prev) {
|
||||
/* clear the currently queued song */
|
||||
pc_cancel(pc);
|
||||
playlist->queued = -1;
|
||||
pc_cancel(&pc);
|
||||
queued = -1;
|
||||
}
|
||||
|
||||
if (next_order >= 0) {
|
||||
if (next_song != prev)
|
||||
playlist_queue_song_order(playlist, pc, next_order);
|
||||
playlist_queue_song_order(this, &pc, next_order);
|
||||
else
|
||||
playlist->queued = next_order;
|
||||
queued = next_order;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
playlist_play_order(struct playlist *playlist, struct player_control *pc,
|
||||
int orderNum)
|
||||
playlist::PlayOrder(player_control &pc, int order)
|
||||
{
|
||||
char *uri;
|
||||
playing = true;
|
||||
queued = -1;
|
||||
|
||||
playlist->playing = true;
|
||||
playlist->queued = -1;
|
||||
struct song *song = song_dup_detached(queue.GetOrder(order));
|
||||
|
||||
struct song *song =
|
||||
song_dup_detached(playlist->queue.GetOrder(orderNum));
|
||||
|
||||
uri = song_get_uri(song);
|
||||
g_debug("play %i:\"%s\"", orderNum, uri);
|
||||
char *uri = song_get_uri(song);
|
||||
g_debug("play %i:\"%s\"", order, uri);
|
||||
g_free(uri);
|
||||
|
||||
pc_play(pc, song);
|
||||
playlist->current = orderNum;
|
||||
pc_play(&pc, song);
|
||||
current = order;
|
||||
}
|
||||
|
||||
static void
|
||||
playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
|
||||
|
||||
/**
|
||||
* This is the "PLAYLIST" event handler. It is invoked by the player
|
||||
* thread whenever it requests a new queued song, or when it exits.
|
||||
*/
|
||||
void
|
||||
playlist_sync(struct playlist *playlist, struct player_control *pc)
|
||||
playlist::SyncWithPlayer(player_control &pc)
|
||||
{
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
/* this event has reached us out of sync: we aren't
|
||||
playing anymore; ignore the event */
|
||||
return;
|
||||
|
||||
player_lock(pc);
|
||||
enum player_state pc_state = pc_get_state(pc);
|
||||
const struct song *pc_next_song = pc->next_song;
|
||||
player_unlock(pc);
|
||||
player_lock(&pc);
|
||||
const enum player_state pc_state = pc_get_state(&pc);
|
||||
const song *pc_next_song = pc.next_song;
|
||||
player_unlock(&pc);
|
||||
|
||||
if (pc_state == PLAYER_STATE_STOP)
|
||||
/* the player thread has stopped: check if playback
|
||||
should be restarted with the next song. That can
|
||||
happen if the playlist isn't filling the queue fast
|
||||
enough */
|
||||
playlist_resume_playback(playlist, pc);
|
||||
playlist_resume_playback(this, &pc);
|
||||
else {
|
||||
/* check if the player thread has already started
|
||||
playing the queued song */
|
||||
if (pc_next_song == NULL && playlist->queued != -1)
|
||||
playlist_song_started(playlist, pc);
|
||||
if (pc_next_song == nullptr && queued != -1)
|
||||
playlist_song_started(this, &pc);
|
||||
|
||||
player_lock(pc);
|
||||
pc_next_song = pc->next_song;
|
||||
player_unlock(pc);
|
||||
player_lock(&pc);
|
||||
pc_next_song = pc.next_song;
|
||||
player_unlock(&pc);
|
||||
|
||||
/* make sure the queued song is always set (if
|
||||
possible) */
|
||||
if (pc_next_song == NULL && playlist->queued < 0)
|
||||
playlist_update_queued_song(playlist, pc, NULL);
|
||||
if (pc_next_song == nullptr && queued < 0)
|
||||
UpdateQueuedSong(pc, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,53 +229,25 @@ playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
|
||||
playlist->error_count >= playlist->queue.GetLength())
|
||||
/* too many errors, or critical error: stop
|
||||
playback */
|
||||
playlist_stop(playlist, pc);
|
||||
playlist->Stop(*pc);
|
||||
else
|
||||
/* continue playback at the next song */
|
||||
playlist_next(playlist, pc);
|
||||
}
|
||||
|
||||
bool
|
||||
playlist_get_repeat(const struct playlist *playlist)
|
||||
{
|
||||
return playlist->queue.repeat;
|
||||
}
|
||||
|
||||
bool
|
||||
playlist_get_random(const struct playlist *playlist)
|
||||
{
|
||||
return playlist->queue.random;
|
||||
}
|
||||
|
||||
bool
|
||||
playlist_get_single(const struct playlist *playlist)
|
||||
{
|
||||
return playlist->queue.single;
|
||||
}
|
||||
|
||||
bool
|
||||
playlist_get_consume(const struct playlist *playlist)
|
||||
{
|
||||
return playlist->queue.consume;
|
||||
playlist->PlayNext(*pc);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
|
||||
bool status)
|
||||
playlist::SetRepeat(player_control &pc, bool status)
|
||||
{
|
||||
if (status == playlist->queue.repeat)
|
||||
if (status == queue.repeat)
|
||||
return;
|
||||
|
||||
struct queue *queue = &playlist->queue;
|
||||
queue.repeat = status;
|
||||
|
||||
queue->repeat = status;
|
||||
|
||||
pc_set_border_pause(pc, queue->single && !queue->repeat);
|
||||
pc_set_border_pause(&pc, queue.single && !queue.repeat);
|
||||
|
||||
/* if the last song is currently being played, the "next song"
|
||||
might change when repeat mode is toggled */
|
||||
playlist_update_queued_song(playlist, pc,
|
||||
playlist_get_queued_song(playlist));
|
||||
UpdateQueuedSong(pc, GetQueuedSong());
|
||||
|
||||
idle_add(IDLE_OPTIONS);
|
||||
}
|
||||
@ -309,117 +263,87 @@ playlist_order(struct playlist *playlist)
|
||||
}
|
||||
|
||||
void
|
||||
playlist_set_single(struct playlist *playlist, struct player_control *pc,
|
||||
bool status)
|
||||
playlist::SetSingle(player_control &pc, bool status)
|
||||
{
|
||||
if (status == playlist->queue.single)
|
||||
if (status == queue.single)
|
||||
return;
|
||||
|
||||
struct queue *queue = &playlist->queue;
|
||||
queue.single = status;
|
||||
|
||||
queue->single = status;
|
||||
|
||||
pc_set_border_pause(pc, queue->single && !queue->repeat);
|
||||
pc_set_border_pause(&pc, queue.single && !queue.repeat);
|
||||
|
||||
/* if the last song is currently being played, the "next song"
|
||||
might change when single mode is toggled */
|
||||
playlist_update_queued_song(playlist, pc,
|
||||
playlist_get_queued_song(playlist));
|
||||
UpdateQueuedSong(pc, GetQueuedSong());
|
||||
|
||||
idle_add(IDLE_OPTIONS);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_set_consume(struct playlist *playlist, bool status)
|
||||
playlist::SetConsume(bool status)
|
||||
{
|
||||
if (status == playlist->queue.consume)
|
||||
if (status == queue.consume)
|
||||
return;
|
||||
|
||||
playlist->queue.consume = status;
|
||||
queue.consume = status;
|
||||
idle_add(IDLE_OPTIONS);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_set_random(struct playlist *playlist, struct player_control *pc,
|
||||
bool status)
|
||||
playlist::SetRandom(player_control &pc, bool status)
|
||||
{
|
||||
const struct song *queued;
|
||||
|
||||
if (status == playlist->queue.random)
|
||||
if (status == queue.random)
|
||||
return;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *const queued_song = GetQueuedSong();
|
||||
|
||||
playlist->queue.random = status;
|
||||
queue.random = status;
|
||||
|
||||
if (playlist->queue.random) {
|
||||
/* shuffle the queue order, but preserve
|
||||
playlist->current */
|
||||
if (queue.random) {
|
||||
/* shuffle the queue order, but preserve current */
|
||||
|
||||
int current_position =
|
||||
playlist->playing && playlist->current >= 0
|
||||
? (int)playlist->queue.OrderToPosition(playlist->current)
|
||||
: -1;
|
||||
const int current_position = GetCurrentPosition();
|
||||
|
||||
playlist->queue.ShuffleOrder();
|
||||
queue.ShuffleOrder();
|
||||
|
||||
if (current_position >= 0) {
|
||||
/* make sure the current song is the first in
|
||||
the order list, so the whole rest of the
|
||||
playlist is played after that */
|
||||
unsigned current_order =
|
||||
playlist->queue.PositionToOrder(current_position);
|
||||
playlist->queue.SwapOrders(0, current_order);
|
||||
playlist->current = 0;
|
||||
queue.PositionToOrder(current_position);
|
||||
queue.SwapOrders(0, current_order);
|
||||
current = 0;
|
||||
} else
|
||||
playlist->current = -1;
|
||||
current = -1;
|
||||
} else
|
||||
playlist_order(playlist);
|
||||
playlist_order(this);
|
||||
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
|
||||
idle_add(IDLE_OPTIONS);
|
||||
}
|
||||
|
||||
int
|
||||
playlist_get_current_song(const struct playlist *playlist)
|
||||
playlist::GetCurrentPosition() const
|
||||
{
|
||||
if (playlist->current >= 0)
|
||||
return playlist->queue.OrderToPosition(playlist->current);
|
||||
|
||||
return -1;
|
||||
return current >= 0
|
||||
? queue.OrderToPosition(current)
|
||||
: -1;
|
||||
}
|
||||
|
||||
int
|
||||
playlist_get_next_song(const struct playlist *playlist)
|
||||
playlist::GetNextPosition() const
|
||||
{
|
||||
if (playlist->current >= 0)
|
||||
{
|
||||
if (playlist->queue.single == 1 && playlist->queue.repeat == 1)
|
||||
return playlist->queue.OrderToPosition(playlist->current);
|
||||
else if (playlist->current + 1 < (int)playlist->queue.GetLength())
|
||||
return playlist->queue.OrderToPosition(playlist->current + 1);
|
||||
else if (playlist->queue.repeat == 1)
|
||||
return playlist->queue.OrderToPosition(0);
|
||||
}
|
||||
if (current < 0)
|
||||
return -1;
|
||||
|
||||
if (queue.single && queue.repeat)
|
||||
return queue.OrderToPosition(current);
|
||||
else if (queue.IsValidOrder(current + 1))
|
||||
return queue.OrderToPosition(current + 1);
|
||||
else if (queue.repeat)
|
||||
return queue.OrderToPosition(0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
playlist_get_version(const struct playlist *playlist)
|
||||
{
|
||||
return playlist->queue.version;
|
||||
}
|
||||
|
||||
int
|
||||
playlist_get_length(const struct playlist *playlist)
|
||||
{
|
||||
return playlist->queue.GetLength();
|
||||
}
|
||||
|
||||
unsigned
|
||||
playlist_get_song_id(const struct playlist *playlist, unsigned song)
|
||||
{
|
||||
return playlist->queue.PositionToId(song);
|
||||
}
|
||||
|
339
src/Playlist.hxx
339
src/Playlist.hxx
@ -75,176 +75,185 @@ struct playlist {
|
||||
|
||||
~playlist() {
|
||||
}
|
||||
|
||||
uint32_t GetVersion() const {
|
||||
return queue.version;
|
||||
}
|
||||
|
||||
unsigned GetLength() const {
|
||||
return queue.GetLength();
|
||||
}
|
||||
|
||||
unsigned PositionToId(unsigned position) const {
|
||||
return queue.PositionToId(position);
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
int GetCurrentPosition() const;
|
||||
|
||||
gcc_pure
|
||||
int GetNextPosition() const;
|
||||
|
||||
/**
|
||||
* Returns the song object which is currently queued. Returns
|
||||
* none if there is none (yet?) or if MPD isn't playing.
|
||||
*/
|
||||
gcc_pure
|
||||
const struct song *GetQueuedSong() const;
|
||||
|
||||
/**
|
||||
* This is the "PLAYLIST" event handler. It is invoked by the
|
||||
* player thread whenever it requests a new queued song, or
|
||||
* when it exits.
|
||||
*/
|
||||
void SyncWithPlayer(player_control &pc);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called by all editing methods after a modification.
|
||||
* Updates the queue version and emits #IDLE_PLAYLIST.
|
||||
*/
|
||||
void OnModified();
|
||||
|
||||
/**
|
||||
* Updates the "queued song". Calculates the next song
|
||||
* according to the current one (if MPD isn't playing, it
|
||||
* takes the first song), and queues this song. Clears the
|
||||
* old queued song if there was one.
|
||||
*
|
||||
* @param prev the song which was previously queued, as
|
||||
* determined by playlist_get_queued_song()
|
||||
*/
|
||||
void UpdateQueuedSong(player_control &pc, const song *prev);
|
||||
|
||||
public:
|
||||
void Clear(player_control &pc);
|
||||
|
||||
void TagChanged();
|
||||
|
||||
enum playlist_result AppendSong(player_control &pc,
|
||||
struct song *song,
|
||||
unsigned *added_id=nullptr);
|
||||
|
||||
/**
|
||||
* Appends a local file (outside the music database) to the
|
||||
* playlist.
|
||||
*
|
||||
* Note: the caller is responsible for checking permissions.
|
||||
*/
|
||||
enum playlist_result AppendFile(player_control &pc,
|
||||
const char *path_fs,
|
||||
unsigned *added_id=nullptr);
|
||||
|
||||
enum playlist_result AppendURI(player_control &pc,
|
||||
const char *uri_utf8,
|
||||
unsigned *added_id=nullptr);
|
||||
|
||||
protected:
|
||||
void DeleteInternal(player_control &pc,
|
||||
unsigned song, const struct song **queued_p);
|
||||
|
||||
public:
|
||||
enum playlist_result DeletePosition(player_control &pc,
|
||||
unsigned position);
|
||||
|
||||
enum playlist_result DeleteOrder(player_control &pc,
|
||||
unsigned order) {
|
||||
return DeletePosition(pc, queue.OrderToPosition(order));
|
||||
}
|
||||
|
||||
enum playlist_result DeleteId(player_control &pc, unsigned id);
|
||||
|
||||
/**
|
||||
* Deletes a range of songs from the playlist.
|
||||
*
|
||||
* @param start the position of the first song to delete
|
||||
* @param end the position after the last song to delete
|
||||
*/
|
||||
enum playlist_result DeleteRange(player_control &pc,
|
||||
unsigned start, unsigned end);
|
||||
|
||||
void DeleteSong(player_control &pc, const song &song);
|
||||
|
||||
void Shuffle(player_control &pc, unsigned start, unsigned end);
|
||||
|
||||
enum playlist_result MoveRange(player_control &pc,
|
||||
unsigned start, unsigned end, int to);
|
||||
|
||||
enum playlist_result MoveId(player_control &pc, unsigned id, int to);
|
||||
|
||||
enum playlist_result SwapPositions(player_control &pc,
|
||||
unsigned song1, unsigned song2);
|
||||
|
||||
enum playlist_result SwapIds(player_control &pc,
|
||||
unsigned id1, unsigned id2);
|
||||
|
||||
enum playlist_result SetPriorityRange(player_control &pc,
|
||||
unsigned start_position,
|
||||
unsigned end_position,
|
||||
uint8_t priority);
|
||||
|
||||
enum playlist_result SetPriorityId(player_control &pc,
|
||||
unsigned song_id, uint8_t priority);
|
||||
|
||||
void Stop(player_control &pc);
|
||||
|
||||
enum playlist_result PlayPosition(player_control &pc, int position);
|
||||
|
||||
void PlayOrder(player_control &pc, int order);
|
||||
|
||||
enum playlist_result PlayId(player_control &pc, int id);
|
||||
|
||||
void PlayNext(player_control &pc);
|
||||
|
||||
void PlayPrevious(player_control &pc);
|
||||
|
||||
enum playlist_result SeekSongPosition(player_control &pc,
|
||||
unsigned song_position,
|
||||
float seek_time);
|
||||
|
||||
enum playlist_result SeekSongId(player_control &pc,
|
||||
unsigned song_id, float seek_time);
|
||||
|
||||
/**
|
||||
* Seek within the current song. Fails if MPD is not currently
|
||||
* playing.
|
||||
*
|
||||
* @param time the time in seconds
|
||||
* @param relative if true, then the specified time is relative to the
|
||||
* current position
|
||||
*/
|
||||
enum playlist_result SeekCurrent(player_control &pc,
|
||||
float seek_time, bool relative);
|
||||
|
||||
bool GetRepeat() const {
|
||||
return queue.repeat;
|
||||
}
|
||||
|
||||
void SetRepeat(player_control &pc, bool new_value);
|
||||
|
||||
bool GetRandom() const {
|
||||
return queue.random;
|
||||
}
|
||||
|
||||
void SetRandom(player_control &pc, bool new_value);
|
||||
|
||||
bool GetSingle() const {
|
||||
return queue.single;
|
||||
}
|
||||
|
||||
void SetSingle(player_control &pc, bool new_value);
|
||||
|
||||
bool GetConsume() const {
|
||||
return queue.consume;
|
||||
}
|
||||
|
||||
void SetConsume(bool new_value);
|
||||
};
|
||||
|
||||
void
|
||||
playlist_global_init();
|
||||
|
||||
void
|
||||
playlist_tag_changed(struct playlist *playlist);
|
||||
|
||||
/**
|
||||
* Returns the "queue" object of the global playlist instance.
|
||||
*/
|
||||
static inline const struct queue *
|
||||
playlist_get_queue(const struct playlist *playlist)
|
||||
{
|
||||
return &playlist->queue;
|
||||
}
|
||||
|
||||
void
|
||||
playlist_clear(struct playlist *playlist, struct player_control *pc);
|
||||
|
||||
/**
|
||||
* Appends a local file (outside the music database) to the playlist.
|
||||
*
|
||||
* Note: the caller is responsible for checking permissions.
|
||||
*/
|
||||
enum playlist_result
|
||||
playlist_append_file(struct playlist *playlist, struct player_control *pc,
|
||||
const char *path_fs, unsigned *added_id);
|
||||
|
||||
enum playlist_result
|
||||
playlist_append_uri(struct playlist *playlist, struct player_control *pc,
|
||||
const char *file, unsigned *added_id);
|
||||
|
||||
enum playlist_result
|
||||
playlist_append_song(struct playlist *playlist, struct player_control *pc,
|
||||
struct song *song, unsigned *added_id);
|
||||
|
||||
enum playlist_result
|
||||
playlist_delete(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song);
|
||||
|
||||
/**
|
||||
* Deletes a range of songs from the playlist.
|
||||
*
|
||||
* @param start the position of the first song to delete
|
||||
* @param end the position after the last song to delete
|
||||
*/
|
||||
enum playlist_result
|
||||
playlist_delete_range(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end);
|
||||
|
||||
enum playlist_result
|
||||
playlist_delete_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song);
|
||||
|
||||
void
|
||||
playlist_stop(struct playlist *playlist, struct player_control *pc);
|
||||
|
||||
enum playlist_result
|
||||
playlist_play(struct playlist *playlist, struct player_control *pc,
|
||||
int song);
|
||||
|
||||
enum playlist_result
|
||||
playlist_play_id(struct playlist *playlist, struct player_control *pc,
|
||||
int song);
|
||||
|
||||
void
|
||||
playlist_next(struct playlist *playlist, struct player_control *pc);
|
||||
|
||||
void
|
||||
playlist_sync(struct playlist *playlist, struct player_control *pc);
|
||||
|
||||
void
|
||||
playlist_previous(struct playlist *playlist, struct player_control *pc);
|
||||
|
||||
void
|
||||
playlist_shuffle(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end);
|
||||
|
||||
void
|
||||
playlist_delete_song(struct playlist *playlist, struct player_control *pc,
|
||||
const struct song *song);
|
||||
|
||||
enum playlist_result
|
||||
playlist_move_range(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end, int to);
|
||||
|
||||
enum playlist_result
|
||||
playlist_move_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id, int to);
|
||||
|
||||
enum playlist_result
|
||||
playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song1, unsigned song2);
|
||||
|
||||
enum playlist_result
|
||||
playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id1, unsigned id2);
|
||||
|
||||
enum playlist_result
|
||||
playlist_set_priority(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start_position, unsigned end_position,
|
||||
uint8_t priority);
|
||||
|
||||
enum playlist_result
|
||||
playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song_id, uint8_t priority);
|
||||
|
||||
bool
|
||||
playlist_get_repeat(const struct playlist *playlist);
|
||||
|
||||
void
|
||||
playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
|
||||
bool status);
|
||||
|
||||
bool
|
||||
playlist_get_random(const struct playlist *playlist);
|
||||
|
||||
void
|
||||
playlist_set_random(struct playlist *playlist, struct player_control *pc,
|
||||
bool status);
|
||||
|
||||
bool
|
||||
playlist_get_single(const struct playlist *playlist);
|
||||
|
||||
void
|
||||
playlist_set_single(struct playlist *playlist, struct player_control *pc,
|
||||
bool status);
|
||||
|
||||
bool
|
||||
playlist_get_consume(const struct playlist *playlist);
|
||||
|
||||
void
|
||||
playlist_set_consume(struct playlist *playlist, bool status);
|
||||
|
||||
int
|
||||
playlist_get_current_song(const struct playlist *playlist);
|
||||
|
||||
int
|
||||
playlist_get_next_song(const struct playlist *playlist);
|
||||
|
||||
unsigned
|
||||
playlist_get_song_id(const struct playlist *playlist, unsigned song);
|
||||
|
||||
int
|
||||
playlist_get_length(const struct playlist *playlist);
|
||||
|
||||
unsigned long
|
||||
playlist_get_version(const struct playlist *playlist);
|
||||
|
||||
enum playlist_result
|
||||
playlist_seek_song(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song, float seek_time);
|
||||
|
||||
enum playlist_result
|
||||
playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id, float seek_time);
|
||||
|
||||
/**
|
||||
* Seek within the current song. Fails if MPD is not currently
|
||||
* playing.
|
||||
*
|
||||
* @param time the time in seconds
|
||||
* @param relative if true, then the specified time is relative to the
|
||||
* current position
|
||||
*/
|
||||
enum playlist_result
|
||||
playlist_seek_current(struct playlist *playlist, struct player_control *pc,
|
||||
float seek_time, bool relative);
|
||||
|
||||
void
|
||||
playlist_increment_version_all(struct playlist *playlist);
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "PlaylistInternal.hxx"
|
||||
#include "Playlist.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "song.h"
|
||||
|
||||
@ -33,244 +33,223 @@
|
||||
#define G_LOG_DOMAIN "playlist"
|
||||
|
||||
void
|
||||
playlist_stop(struct playlist *playlist, struct player_control *pc)
|
||||
playlist::Stop(player_control &pc)
|
||||
{
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
return;
|
||||
|
||||
assert(playlist->current >= 0);
|
||||
assert(current >= 0);
|
||||
|
||||
g_debug("stop");
|
||||
pc_stop(pc);
|
||||
playlist->queued = -1;
|
||||
playlist->playing = false;
|
||||
pc_stop(&pc);
|
||||
queued = -1;
|
||||
playing = false;
|
||||
|
||||
if (playlist->queue.random) {
|
||||
if (queue.random) {
|
||||
/* shuffle the playlist, so the next playback will
|
||||
result in a new random order */
|
||||
|
||||
unsigned current_position =
|
||||
playlist->queue.OrderToPosition(playlist->current);
|
||||
unsigned current_position = queue.OrderToPosition(current);
|
||||
|
||||
playlist->queue.ShuffleOrder();
|
||||
queue.ShuffleOrder();
|
||||
|
||||
/* make sure that "current" stays valid, and the next
|
||||
"play" command plays the same song again */
|
||||
playlist->current =
|
||||
playlist->queue.PositionToOrder(current_position);
|
||||
current = queue.PositionToOrder(current_position);
|
||||
}
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_play(struct playlist *playlist, struct player_control *pc,
|
||||
int song)
|
||||
playlist::PlayPosition(player_control &pc, int song)
|
||||
{
|
||||
pc_clear_error(&pc);
|
||||
|
||||
unsigned i = song;
|
||||
|
||||
pc_clear_error(pc);
|
||||
|
||||
if (song == -1) {
|
||||
/* play any song ("current" song, or the first song */
|
||||
|
||||
if (playlist->queue.IsEmpty())
|
||||
if (queue.IsEmpty())
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
|
||||
if (playlist->playing) {
|
||||
if (playing) {
|
||||
/* already playing: unpause playback, just in
|
||||
case it was paused, and return */
|
||||
pc_set_pause(pc, false);
|
||||
pc_set_pause(&pc, false);
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/* select a song: "current" song, or the first one */
|
||||
i = playlist->current >= 0
|
||||
? playlist->current
|
||||
i = current >= 0
|
||||
? current
|
||||
: 0;
|
||||
} else if (!playlist->queue.IsValidPosition(song))
|
||||
} else if (!queue.IsValidPosition(song))
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
if (playlist->queue.random) {
|
||||
if (queue.random) {
|
||||
if (song >= 0)
|
||||
/* "i" is currently the song position (which
|
||||
would be equal to the order number in
|
||||
no-random mode); convert it to a order
|
||||
number, because random mode is enabled */
|
||||
i = playlist->queue.PositionToOrder(song);
|
||||
i = queue.PositionToOrder(song);
|
||||
|
||||
if (!playlist->playing)
|
||||
playlist->current = 0;
|
||||
if (!playing)
|
||||
current = 0;
|
||||
|
||||
/* swap the new song with the previous "current" one,
|
||||
so playback continues as planned */
|
||||
playlist->queue.SwapOrders(i, playlist->current);
|
||||
i = playlist->current;
|
||||
queue.SwapOrders(i, current);
|
||||
i = current;
|
||||
}
|
||||
|
||||
playlist->stop_on_error = false;
|
||||
playlist->error_count = 0;
|
||||
stop_on_error = false;
|
||||
error_count = 0;
|
||||
|
||||
playlist_play_order(playlist, pc, i);
|
||||
PlayOrder(pc, i);
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_play_id(struct playlist *playlist, struct player_control *pc,
|
||||
int id)
|
||||
playlist::PlayId(player_control &pc, int id)
|
||||
{
|
||||
int song;
|
||||
if (id == -1)
|
||||
return PlayPosition(pc, id);
|
||||
|
||||
if (id == -1) {
|
||||
return playlist_play(playlist, pc, id);
|
||||
}
|
||||
|
||||
song = playlist->queue.IdToPosition(id);
|
||||
int song = queue.IdToPosition(id);
|
||||
if (song < 0)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_play(playlist, pc, song);
|
||||
return PlayPosition(pc, song);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_next(struct playlist *playlist, struct player_control *pc)
|
||||
playlist::PlayNext(player_control &pc)
|
||||
{
|
||||
int next_order;
|
||||
int current;
|
||||
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
return;
|
||||
|
||||
assert(!playlist->queue.IsEmpty());
|
||||
assert(playlist->queue.IsValidOrder(playlist->current));
|
||||
assert(!queue.IsEmpty());
|
||||
assert(queue.IsValidOrder(current));
|
||||
|
||||
current = playlist->current;
|
||||
playlist->stop_on_error = false;
|
||||
const int old_current = current;
|
||||
stop_on_error = false;
|
||||
|
||||
/* determine the next song from the queue's order list */
|
||||
|
||||
next_order = playlist->queue.GetNextOrder(playlist->current);
|
||||
const int next_order = queue.GetNextOrder(current);
|
||||
if (next_order < 0) {
|
||||
/* no song after this one: stop playback */
|
||||
playlist_stop(playlist, pc);
|
||||
Stop(pc);
|
||||
|
||||
/* reset "current song" */
|
||||
playlist->current = -1;
|
||||
current = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (next_order == 0 && playlist->queue.random) {
|
||||
if (next_order == 0 && queue.random) {
|
||||
/* The queue told us that the next song is the first
|
||||
song. This means we are in repeat mode. Shuffle
|
||||
the queue order, so this time, the user hears the
|
||||
songs in a different than before */
|
||||
assert(playlist->queue.repeat);
|
||||
assert(queue.repeat);
|
||||
|
||||
playlist->queue.ShuffleOrder();
|
||||
queue.ShuffleOrder();
|
||||
|
||||
/* note that playlist->current and playlist->queued are
|
||||
/* note that current and queued are
|
||||
now invalid, but playlist_play_order() will
|
||||
discard them anyway */
|
||||
}
|
||||
|
||||
playlist_play_order(playlist, pc, next_order);
|
||||
PlayOrder(pc, next_order);
|
||||
}
|
||||
|
||||
/* Consume mode removes each played songs. */
|
||||
if(playlist->queue.consume)
|
||||
playlist_delete(playlist, pc,
|
||||
playlist->queue.OrderToPosition(current));
|
||||
if (queue.consume)
|
||||
DeleteOrder(pc, old_current);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_previous(struct playlist *playlist, struct player_control *pc)
|
||||
playlist::PlayPrevious(player_control &pc)
|
||||
{
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
return;
|
||||
|
||||
assert(playlist->queue.GetLength() > 0);
|
||||
assert(!queue.IsEmpty());
|
||||
|
||||
if (playlist->current > 0) {
|
||||
int order;
|
||||
if (current > 0) {
|
||||
/* play the preceding song */
|
||||
playlist_play_order(playlist, pc,
|
||||
playlist->current - 1);
|
||||
} else if (playlist->queue.repeat) {
|
||||
order = current - 1;
|
||||
} else if (queue.repeat) {
|
||||
/* play the last song in "repeat" mode */
|
||||
playlist_play_order(playlist, pc,
|
||||
playlist->queue.GetLength() - 1);
|
||||
order = queue.GetLength() - 1;
|
||||
} else {
|
||||
/* re-start playing the current song if it's
|
||||
the first one */
|
||||
playlist_play_order(playlist, pc, playlist->current);
|
||||
order = current;
|
||||
}
|
||||
|
||||
PlayOrder(pc, order);
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_seek_song(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song, float seek_time)
|
||||
playlist::SeekSongPosition(player_control &pc, unsigned song, float seek_time)
|
||||
{
|
||||
const struct song *queued;
|
||||
unsigned i;
|
||||
bool success;
|
||||
|
||||
if (!playlist->queue.IsValidPosition(song))
|
||||
if (!queue.IsValidPosition(song))
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *queued_song = GetQueuedSong();
|
||||
|
||||
if (playlist->queue.random)
|
||||
i = playlist->queue.PositionToOrder(song);
|
||||
else
|
||||
i = song;
|
||||
unsigned i = queue.random
|
||||
? queue.PositionToOrder(song)
|
||||
: song;
|
||||
|
||||
pc_clear_error(pc);
|
||||
playlist->stop_on_error = true;
|
||||
playlist->error_count = 0;
|
||||
pc_clear_error(&pc);
|
||||
stop_on_error = true;
|
||||
error_count = 0;
|
||||
|
||||
if (!playlist->playing || (unsigned)playlist->current != i) {
|
||||
if (!playing || (unsigned)current != i) {
|
||||
/* seeking is not within the current song - prepare
|
||||
song change */
|
||||
|
||||
playlist->playing = true;
|
||||
playlist->current = i;
|
||||
playing = true;
|
||||
current = i;
|
||||
|
||||
queued = NULL;
|
||||
queued_song = nullptr;
|
||||
}
|
||||
|
||||
struct song *the_song =
|
||||
song_dup_detached(playlist->queue.GetOrder(i));
|
||||
success = pc_seek(pc, the_song, seek_time);
|
||||
if (!success) {
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
struct song *the_song = song_dup_detached(queue.GetOrder(i));
|
||||
if (!pc_seek(&pc, the_song, seek_time)) {
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
|
||||
return PLAYLIST_RESULT_NOT_PLAYING;
|
||||
}
|
||||
|
||||
playlist->queued = -1;
|
||||
playlist_update_queued_song(playlist, pc, NULL);
|
||||
queued = -1;
|
||||
UpdateQueuedSong(pc, NULL);
|
||||
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id, float seek_time)
|
||||
playlist::SeekSongId(player_control &pc, unsigned id, float seek_time)
|
||||
{
|
||||
int song = playlist->queue.IdToPosition(id);
|
||||
int song = queue.IdToPosition(id);
|
||||
if (song < 0)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_seek_song(playlist, pc, song, seek_time);
|
||||
return SeekSongPosition(pc, song, seek_time);
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_seek_current(struct playlist *playlist, struct player_control *pc,
|
||||
float seek_time, bool relative)
|
||||
playlist::SeekCurrent(player_control &pc, float seek_time, bool relative)
|
||||
{
|
||||
if (!playlist->playing)
|
||||
if (!playing)
|
||||
return PLAYLIST_RESULT_NOT_PLAYING;
|
||||
|
||||
if (relative) {
|
||||
struct player_status status;
|
||||
pc_get_status(pc, &status);
|
||||
pc_get_status(&pc, &status);
|
||||
|
||||
if (status.state != PLAYER_STATE_PLAY &&
|
||||
status.state != PLAYER_STATE_PAUSE)
|
||||
@ -282,5 +261,5 @@ playlist_seek_current(struct playlist *playlist, struct player_control *pc,
|
||||
if (seek_time < 0)
|
||||
seek_time = 0;
|
||||
|
||||
return playlist_seek_song(playlist, pc, playlist->current, seek_time);
|
||||
return SeekSongPosition(pc, current, seek_time);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "PlaylistInternal.hxx"
|
||||
#include "Playlist.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
|
||||
extern "C" {
|
||||
@ -38,67 +38,64 @@ extern "C" {
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void playlist_increment_version(struct playlist *playlist)
|
||||
void
|
||||
playlist::OnModified()
|
||||
{
|
||||
playlist->queue.IncrementVersion();
|
||||
queue.IncrementVersion();
|
||||
|
||||
idle_add(IDLE_PLAYLIST);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_clear(struct playlist *playlist, struct player_control *pc)
|
||||
playlist::Clear(player_control &pc)
|
||||
{
|
||||
playlist_stop(playlist, pc);
|
||||
Stop(pc);
|
||||
|
||||
playlist->queue.Clear();
|
||||
queue.Clear();
|
||||
current = -1;
|
||||
|
||||
playlist->current = -1;
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
OnModified();
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_append_file(struct playlist *playlist, struct player_control *pc,
|
||||
playlist::AppendFile(struct player_control &pc,
|
||||
const char *path_fs, unsigned *added_id)
|
||||
{
|
||||
struct song *song = song_file_load(path_fs, NULL);
|
||||
if (song == NULL)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_append_song(playlist, pc, song, added_id);
|
||||
return AppendSong(pc, song, added_id);
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_append_song(struct playlist *playlist, struct player_control *pc,
|
||||
struct song *song, unsigned *added_id)
|
||||
playlist::AppendSong(struct player_control &pc,
|
||||
struct song *song, unsigned *added_id)
|
||||
{
|
||||
const struct song *queued;
|
||||
unsigned id;
|
||||
|
||||
if (playlist->queue.IsFull())
|
||||
if (queue.IsFull())
|
||||
return PLAYLIST_RESULT_TOO_LARGE;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *const queued_song = GetQueuedSong();
|
||||
|
||||
id = playlist->queue.Append(song, 0);
|
||||
id = queue.Append(song, 0);
|
||||
|
||||
if (playlist->queue.random) {
|
||||
if (queue.random) {
|
||||
/* shuffle the new song into the list of remaining
|
||||
songs to play */
|
||||
|
||||
unsigned start;
|
||||
if (playlist->queued >= 0)
|
||||
start = playlist->queued + 1;
|
||||
if (queued >= 0)
|
||||
start = queued + 1;
|
||||
else
|
||||
start = playlist->current + 1;
|
||||
if (start < playlist->queue.GetLength())
|
||||
playlist->queue.ShuffleOrderLast(start,
|
||||
playlist->queue.GetLength());
|
||||
start = current + 1;
|
||||
if (start < queue.GetLength())
|
||||
queue.ShuffleOrderLast(start, queue.GetLength());
|
||||
}
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
|
||||
if (added_id)
|
||||
*added_id = id;
|
||||
@ -107,7 +104,7 @@ playlist_append_song(struct playlist *playlist, struct player_control *pc,
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_append_uri(struct playlist *playlist, struct player_control *pc,
|
||||
playlist::AppendURI(struct player_control &pc,
|
||||
const char *uri, unsigned *added_id)
|
||||
{
|
||||
g_debug("add to playlist: %s", uri);
|
||||
@ -126,8 +123,7 @@ playlist_append_uri(struct playlist *playlist, struct player_control *pc,
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
}
|
||||
|
||||
enum playlist_result result =
|
||||
playlist_append_song(playlist, pc, song, added_id);
|
||||
enum playlist_result result = AppendSong(pc, song, added_id);
|
||||
if (db != nullptr)
|
||||
db->ReturnSong(song);
|
||||
|
||||
@ -135,335 +131,293 @@ playlist_append_uri(struct playlist *playlist, struct player_control *pc,
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song1, unsigned song2)
|
||||
playlist::SwapPositions(player_control &pc, unsigned song1, unsigned song2)
|
||||
{
|
||||
const struct song *queued;
|
||||
|
||||
if (!playlist->queue.IsValidPosition(song1) ||
|
||||
!playlist->queue.IsValidPosition(song2))
|
||||
if (!queue.IsValidPosition(song1) || !queue.IsValidPosition(song2))
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *const queued_song = GetQueuedSong();
|
||||
|
||||
playlist->queue.SwapPositions(song1, song2);
|
||||
queue.SwapPositions(song1, song2);
|
||||
|
||||
if (playlist->queue.random) {
|
||||
/* update the queue order, so that playlist->current
|
||||
if (queue.random) {
|
||||
/* update the queue order, so that current
|
||||
still points to the current song order */
|
||||
|
||||
playlist->queue.SwapOrders(playlist->queue.PositionToOrder(song1),
|
||||
playlist->queue.PositionToOrder(song2));
|
||||
queue.SwapOrders(queue.PositionToOrder(song1),
|
||||
queue.PositionToOrder(song2));
|
||||
} else {
|
||||
/* correct the "current" song order */
|
||||
|
||||
if (playlist->current == (int)song1)
|
||||
playlist->current = song2;
|
||||
else if (playlist->current == (int)song2)
|
||||
playlist->current = song1;
|
||||
if (current == (int)song1)
|
||||
current = song2;
|
||||
else if (current == (int)song2)
|
||||
current = song1;
|
||||
}
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id1, unsigned id2)
|
||||
playlist::SwapIds(player_control &pc, unsigned id1, unsigned id2)
|
||||
{
|
||||
int song1 = playlist->queue.IdToPosition(id1);
|
||||
int song2 = playlist->queue.IdToPosition(id2);
|
||||
int song1 = queue.IdToPosition(id1);
|
||||
int song2 = queue.IdToPosition(id2);
|
||||
|
||||
if (song1 < 0 || song2 < 0)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_swap_songs(playlist, pc, song1, song2);
|
||||
return SwapPositions(pc, song1, song2);
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_set_priority(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end,
|
||||
uint8_t priority)
|
||||
playlist::SetPriorityRange(player_control &pc,
|
||||
unsigned start, unsigned end,
|
||||
uint8_t priority)
|
||||
{
|
||||
if (start >= playlist->queue.GetLength())
|
||||
if (start >= GetLength())
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
if (end > playlist->queue.GetLength())
|
||||
end = playlist->queue.GetLength();
|
||||
if (end > GetLength())
|
||||
end = GetLength();
|
||||
|
||||
if (start >= end)
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
|
||||
/* remember "current" and "queued" */
|
||||
|
||||
int current_position = playlist->current >= 0
|
||||
? (int)playlist->queue.OrderToPosition(playlist->current)
|
||||
: -1;
|
||||
|
||||
const struct song *queued = playlist_get_queued_song(playlist);
|
||||
const int current_position = GetCurrentPosition();
|
||||
const struct song *const queued_song = GetQueuedSong();
|
||||
|
||||
/* apply the priority changes */
|
||||
|
||||
playlist->queue.SetPriorityRange(start, end, priority,
|
||||
playlist->current);
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
queue.SetPriorityRange(start, end, priority, current);
|
||||
|
||||
/* restore "current" and choose a new "queued" */
|
||||
|
||||
if (current_position >= 0)
|
||||
playlist->current = playlist->queue.PositionToOrder(current_position);
|
||||
current = queue.PositionToOrder(current_position);
|
||||
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song_id, uint8_t priority)
|
||||
playlist::SetPriorityId(struct player_control &pc,
|
||||
unsigned song_id, uint8_t priority)
|
||||
{
|
||||
int song_position = playlist->queue.IdToPosition(song_id);
|
||||
int song_position = queue.IdToPosition(song_id);
|
||||
if (song_position < 0)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_set_priority(playlist, pc,
|
||||
song_position, song_position + 1,
|
||||
priority);
|
||||
return SetPriorityRange(pc, song_position, song_position + 1,
|
||||
priority);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
|
||||
void
|
||||
playlist::DeleteInternal(player_control &pc,
|
||||
unsigned song, const struct song **queued_p)
|
||||
{
|
||||
unsigned songOrder;
|
||||
assert(song < GetLength());
|
||||
|
||||
assert(song < playlist->queue.GetLength());
|
||||
unsigned songOrder = queue.PositionToOrder(song);
|
||||
|
||||
songOrder = playlist->queue.PositionToOrder(song);
|
||||
|
||||
if (playlist->playing && playlist->current == (int)songOrder) {
|
||||
bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
|
||||
if (playing && current == (int)songOrder) {
|
||||
bool paused = pc_get_state(&pc) == PLAYER_STATE_PAUSE;
|
||||
|
||||
/* the current song is going to be deleted: stop the player */
|
||||
|
||||
pc_stop(pc);
|
||||
playlist->playing = false;
|
||||
pc_stop(&pc);
|
||||
playing = false;
|
||||
|
||||
/* see which song is going to be played instead */
|
||||
|
||||
playlist->current = playlist->queue.GetNextOrder(playlist->current);
|
||||
if (playlist->current == (int)songOrder)
|
||||
playlist->current = -1;
|
||||
current = queue.GetNextOrder(current);
|
||||
if (current == (int)songOrder)
|
||||
current = -1;
|
||||
|
||||
if (playlist->current >= 0 && !paused)
|
||||
if (current >= 0 && !paused)
|
||||
/* play the song after the deleted one */
|
||||
playlist_play_order(playlist, pc, playlist->current);
|
||||
PlayOrder(pc, current);
|
||||
else
|
||||
/* no songs left to play, stop playback
|
||||
completely */
|
||||
playlist_stop(playlist, pc);
|
||||
Stop(pc);
|
||||
|
||||
*queued_p = NULL;
|
||||
} else if (playlist->current == (int)songOrder)
|
||||
} else if (current == (int)songOrder)
|
||||
/* there's a "current song" but we're not playing
|
||||
currently - clear "current" */
|
||||
playlist->current = -1;
|
||||
current = -1;
|
||||
|
||||
/* now do it: remove the song */
|
||||
|
||||
playlist->queue.DeletePosition(song);
|
||||
queue.DeletePosition(song);
|
||||
|
||||
/* update the "current" and "queued" variables */
|
||||
|
||||
if (playlist->current > (int)songOrder) {
|
||||
playlist->current--;
|
||||
}
|
||||
if (current > (int)songOrder)
|
||||
current--;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_delete(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned song)
|
||||
playlist::DeletePosition(struct player_control &pc, unsigned song)
|
||||
{
|
||||
const struct song *queued;
|
||||
|
||||
if (song >= playlist->queue.GetLength())
|
||||
if (song >= queue.GetLength())
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *queued_song = GetQueuedSong();
|
||||
|
||||
playlist_delete_internal(playlist, pc, song, &queued);
|
||||
DeleteInternal(pc, song, &queued_song);
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_delete_range(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end)
|
||||
playlist::DeleteRange(struct player_control &pc, unsigned start, unsigned end)
|
||||
{
|
||||
const struct song *queued;
|
||||
|
||||
if (start >= playlist->queue.GetLength())
|
||||
if (start >= queue.GetLength())
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
if (end > playlist->queue.GetLength())
|
||||
end = playlist->queue.GetLength();
|
||||
if (end > queue.GetLength())
|
||||
end = queue.GetLength();
|
||||
|
||||
if (start >= end)
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *queued_song = GetQueuedSong();
|
||||
|
||||
do {
|
||||
playlist_delete_internal(playlist, pc, --end, &queued);
|
||||
DeleteInternal(pc, --end, &queued_song);
|
||||
} while (end != start);
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_delete_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id)
|
||||
playlist::DeleteId(struct player_control &pc, unsigned id)
|
||||
{
|
||||
int song = playlist->queue.IdToPosition(id);
|
||||
int song = queue.IdToPosition(id);
|
||||
if (song < 0)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_delete(playlist, pc, song);
|
||||
return DeletePosition(pc, song);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_delete_song(struct playlist *playlist, struct player_control *pc,
|
||||
const struct song *song)
|
||||
playlist::DeleteSong(struct player_control &pc, const struct song &song)
|
||||
{
|
||||
for (int i = playlist->queue.GetLength() - 1; i >= 0; --i)
|
||||
if (song == playlist->queue.Get(i))
|
||||
playlist_delete(playlist, pc, i);
|
||||
for (int i = queue.GetLength() - 1; i >= 0; --i)
|
||||
// TODO: compare URI instead of pointer
|
||||
if (&song == queue.Get(i))
|
||||
DeletePosition(pc, i);
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_move_range(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end, int to)
|
||||
playlist::MoveRange(player_control &pc, unsigned start, unsigned end, int to)
|
||||
{
|
||||
const struct song *queued;
|
||||
int currentSong;
|
||||
|
||||
if (!playlist->queue.IsValidPosition(start) ||
|
||||
!playlist->queue.IsValidPosition(end - 1))
|
||||
if (!queue.IsValidPosition(start) || !queue.IsValidPosition(end - 1))
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
if ((to >= 0 && to + end - start - 1 >= playlist->queue.GetLength()) ||
|
||||
(to < 0 && abs(to) > (int)playlist->queue.GetLength()))
|
||||
if ((to >= 0 && to + end - start - 1 >= GetLength()) ||
|
||||
(to < 0 && unsigned(abs(to)) > GetLength()))
|
||||
return PLAYLIST_RESULT_BAD_RANGE;
|
||||
|
||||
if ((int)start == to)
|
||||
/* nothing happens */
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
const struct song *const queued_song = GetQueuedSong();
|
||||
|
||||
/*
|
||||
* (to < 0) => move to offset from current song
|
||||
* (-playlist.length == to) => move to position BEFORE current song
|
||||
*/
|
||||
currentSong = playlist->current >= 0
|
||||
? (int)playlist->queue.OrderToPosition(playlist->current)
|
||||
: -1;
|
||||
if (to < 0 && playlist->current >= 0) {
|
||||
const int currentSong = GetCurrentPosition();
|
||||
if (to < 0 && currentSong >= 0) {
|
||||
if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
|
||||
/* no-op, can't be moved to offset of itself */
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
to = (currentSong + abs(to)) % playlist->queue.GetLength();
|
||||
to = (currentSong + abs(to)) % GetLength();
|
||||
if (start < (unsigned)to)
|
||||
to--;
|
||||
}
|
||||
|
||||
playlist->queue.MoveRange(start, end, to);
|
||||
queue.MoveRange(start, end, to);
|
||||
|
||||
if (!playlist->queue.random) {
|
||||
if (!queue.random) {
|
||||
/* update current/queued */
|
||||
if ((int)start <= playlist->current &&
|
||||
(unsigned)playlist->current < end)
|
||||
playlist->current += to - start;
|
||||
else if (playlist->current >= (int)end &&
|
||||
playlist->current <= to) {
|
||||
playlist->current -= end - start;
|
||||
} else if (playlist->current >= to &&
|
||||
playlist->current < (int)start) {
|
||||
playlist->current += end - start;
|
||||
}
|
||||
if ((int)start <= current && (unsigned)current < end)
|
||||
current += to - start;
|
||||
else if (current >= (int)end && current <= to)
|
||||
current -= end - start;
|
||||
else if (current >= to && current < (int)start)
|
||||
current += end - start;
|
||||
}
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
|
||||
return PLAYLIST_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum playlist_result
|
||||
playlist_move_id(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned id1, int to)
|
||||
playlist::MoveId(player_control &pc, unsigned id1, int to)
|
||||
{
|
||||
int song = playlist->queue.IdToPosition(id1);
|
||||
int song = queue.IdToPosition(id1);
|
||||
if (song < 0)
|
||||
return PLAYLIST_RESULT_NO_SUCH_SONG;
|
||||
|
||||
return playlist_move_range(playlist, pc, song, song+1, to);
|
||||
return MoveRange(pc, song, song + 1, to);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_shuffle(struct playlist *playlist, struct player_control *pc,
|
||||
unsigned start, unsigned end)
|
||||
playlist::Shuffle(player_control &pc, unsigned start, unsigned end)
|
||||
{
|
||||
const struct song *queued;
|
||||
|
||||
if (end > playlist->queue.GetLength())
|
||||
if (end > GetLength())
|
||||
/* correct the "end" offset */
|
||||
end = playlist->queue.GetLength();
|
||||
end = GetLength();
|
||||
|
||||
if ((start+1) >= end)
|
||||
if (start + 1 >= end)
|
||||
/* needs at least two entries. */
|
||||
return;
|
||||
|
||||
queued = playlist_get_queued_song(playlist);
|
||||
if (playlist->playing && playlist->current >= 0) {
|
||||
unsigned current_position;
|
||||
current_position = playlist->queue.OrderToPosition(playlist->current);
|
||||
const struct song *const queued_song = GetQueuedSong();
|
||||
if (playing && current >= 0) {
|
||||
unsigned current_position = queue.OrderToPosition(current);
|
||||
|
||||
if (current_position >= start && current_position < end)
|
||||
{
|
||||
if (current_position >= start && current_position < end) {
|
||||
/* put current playing song first */
|
||||
playlist->queue.SwapPositions(start, current_position);
|
||||
queue.SwapPositions(start, current_position);
|
||||
|
||||
if (playlist->queue.random) {
|
||||
playlist->current =
|
||||
playlist->queue.PositionToOrder(start);
|
||||
if (queue.random) {
|
||||
current = queue.PositionToOrder(start);
|
||||
} else
|
||||
playlist->current = start;
|
||||
current = start;
|
||||
|
||||
/* start shuffle after the current song */
|
||||
start++;
|
||||
}
|
||||
} else {
|
||||
/* no playback currently: reset playlist->current */
|
||||
/* no playback currently: reset current */
|
||||
|
||||
playlist->current = -1;
|
||||
current = -1;
|
||||
}
|
||||
|
||||
playlist->queue.ShuffleRange(start, end);
|
||||
queue.ShuffleRange(start, end);
|
||||
|
||||
playlist_increment_version(playlist);
|
||||
|
||||
playlist_update_queued_song(playlist, pc, queued);
|
||||
UpdateQueuedSong(pc, queued_song);
|
||||
OnModified();
|
||||
}
|
||||
|
@ -34,14 +34,13 @@ extern "C" {
|
||||
static void
|
||||
playlist_tag_event(void)
|
||||
{
|
||||
playlist_tag_changed(&global_partition->playlist);
|
||||
global_partition->playlist.TagChanged();
|
||||
}
|
||||
|
||||
static void
|
||||
playlist_event(void)
|
||||
{
|
||||
playlist_sync(&global_partition->playlist,
|
||||
&global_partition->pc);
|
||||
global_partition->playlist.SyncWithPlayer(global_partition->pc);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Internal header for the components of the playlist code.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MPD_PLAYLIST_INTERNAL_HXX
|
||||
#define MPD_PLAYLIST_INTERNAL_HXX
|
||||
|
||||
#include "Playlist.hxx"
|
||||
|
||||
struct player_control;
|
||||
|
||||
/**
|
||||
* Returns the song object which is currently queued. Returns none if
|
||||
* there is none (yet?) or if MPD isn't playing.
|
||||
*/
|
||||
const struct song *
|
||||
playlist_get_queued_song(struct playlist *playlist);
|
||||
|
||||
/**
|
||||
* Updates the "queued song". Calculates the next song according to
|
||||
* the current one (if MPD isn't playing, it takes the first song),
|
||||
* and queues this song. Clears the old queued song if there was one.
|
||||
*
|
||||
* @param prev the song which was previously queued, as determined by
|
||||
* playlist_get_queued_song()
|
||||
*/
|
||||
void
|
||||
playlist_update_queued_song(struct playlist *playlist,
|
||||
struct player_control *pc,
|
||||
const struct song *prev);
|
||||
|
||||
void
|
||||
playlist_play_order(struct playlist *playlist, struct player_control *pc,
|
||||
int orderNum);
|
||||
|
||||
#endif
|
@ -80,8 +80,7 @@ playlist_print_id(Client *client, const struct playlist *playlist,
|
||||
bool
|
||||
playlist_print_current(Client *client, const struct playlist *playlist)
|
||||
{
|
||||
int current_position = playlist_get_current_song(playlist);
|
||||
|
||||
int current_position = playlist->GetCurrentPosition();
|
||||
if (current_position < 0)
|
||||
return false;
|
||||
|
||||
|
@ -52,7 +52,7 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
|
||||
if (song == NULL)
|
||||
continue;
|
||||
|
||||
result = playlist_append_song(dest, pc, song, NULL);
|
||||
result = dest->AppendSong(*pc, song);
|
||||
song_free(song);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS) {
|
||||
g_free(base_uri);
|
||||
|
@ -132,8 +132,7 @@ playlist_load_spl(struct playlist *playlist, struct player_control *pc,
|
||||
for (unsigned i = start_index; i < end_index; ++i) {
|
||||
const auto &uri_utf8 = contents[i];
|
||||
|
||||
if ((playlist_append_uri(playlist, pc, uri_utf8.c_str(),
|
||||
nullptr)) != PLAYLIST_RESULT_SUCCESS) {
|
||||
if ((playlist->AppendURI(*pc, uri_utf8.c_str())) != PLAYLIST_RESULT_SUCCESS) {
|
||||
/* for windows compatibility, convert slashes */
|
||||
char *temp2 = g_strdup(uri_utf8.c_str());
|
||||
char *p = temp2;
|
||||
@ -142,9 +141,10 @@ playlist_load_spl(struct playlist *playlist, struct player_control *pc,
|
||||
*p = '/';
|
||||
p++;
|
||||
}
|
||||
if ((playlist_append_uri(playlist, pc, temp2, NULL)) != PLAYLIST_RESULT_SUCCESS) {
|
||||
|
||||
if (playlist->AppendURI(*pc, temp2) != PLAYLIST_RESULT_SUCCESS)
|
||||
g_warning("can't add file \"%s\"", temp2);
|
||||
}
|
||||
|
||||
g_free(temp2);
|
||||
}
|
||||
}
|
||||
|
@ -145,26 +145,16 @@ playlist_state_restore(const char *line, TextFile &file,
|
||||
seek_time =
|
||||
atoi(&(line[strlen(PLAYLIST_STATE_FILE_TIME)]));
|
||||
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_REPEAT)) {
|
||||
if (strcmp
|
||||
(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
|
||||
"1") == 0) {
|
||||
playlist_set_repeat(playlist, pc, true);
|
||||
} else
|
||||
playlist_set_repeat(playlist, pc, false);
|
||||
playlist->SetRepeat(*pc,
|
||||
strcmp(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
|
||||
"1") == 0);
|
||||
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) {
|
||||
if (strcmp
|
||||
(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
|
||||
"1") == 0) {
|
||||
playlist_set_single(playlist, pc, true);
|
||||
} else
|
||||
playlist_set_single(playlist, pc, false);
|
||||
playlist->SetSingle(*pc,
|
||||
strcmp(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
|
||||
"1") == 0);
|
||||
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) {
|
||||
if (strcmp
|
||||
(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
|
||||
"1") == 0) {
|
||||
playlist_set_consume(playlist, true);
|
||||
} else
|
||||
playlist_set_consume(playlist, false);
|
||||
playlist->SetConsume(strcmp(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
|
||||
"1") == 0);
|
||||
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) {
|
||||
pc_set_cross_fade(pc,
|
||||
atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
|
||||
@ -188,7 +178,7 @@ playlist_state_restore(const char *line, TextFile &file,
|
||||
}
|
||||
}
|
||||
|
||||
playlist_set_random(playlist, pc, random_mode);
|
||||
playlist->SetRandom(*pc, random_mode);
|
||||
|
||||
if (!playlist->queue.IsEmpty()) {
|
||||
if (!playlist->queue.IsValidPosition(current))
|
||||
@ -210,9 +200,9 @@ playlist_state_restore(const char *line, TextFile &file,
|
||||
if (state == PLAYER_STATE_STOP /* && config_option */)
|
||||
playlist->current = current;
|
||||
else if (seek_time == 0)
|
||||
playlist_play(playlist, pc, current);
|
||||
playlist->PlayPosition(*pc, current);
|
||||
else
|
||||
playlist_seek_song(playlist, pc, current, seek_time);
|
||||
playlist->SeekSongPosition(*pc, current, seek_time);
|
||||
|
||||
if (state == PLAYER_STATE_PAUSE)
|
||||
pc_pause(pc);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "PlaylistPrint.hxx"
|
||||
#include "ClientFile.hxx"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "protocol/ArgParser.hxx"
|
||||
#include "protocol/Result.hxx"
|
||||
#include "ls.hxx"
|
||||
@ -50,10 +51,7 @@ handle_add(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!client_allow_file(client, path, &error))
|
||||
return print_error(client, error);
|
||||
|
||||
result = playlist_append_file(&client->playlist,
|
||||
client->player_control,
|
||||
path,
|
||||
NULL);
|
||||
result = client->partition.AppendFile(path);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -64,9 +62,7 @@ handle_add(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
return COMMAND_RETURN_ERROR;
|
||||
}
|
||||
|
||||
result = playlist_append_uri(&client->playlist,
|
||||
client->player_control,
|
||||
uri, NULL);
|
||||
result = client->partition.AppendURI(uri);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -91,10 +87,7 @@ handle_addid(Client *client, int argc, char *argv[])
|
||||
if (!client_allow_file(client, path, &error))
|
||||
return print_error(client, error);
|
||||
|
||||
result = playlist_append_file(&client->playlist,
|
||||
client->player_control,
|
||||
path,
|
||||
&added_id);
|
||||
result = client->partition.AppendFile(path, &added_id);
|
||||
} else {
|
||||
if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
|
||||
command_error(client, ACK_ERROR_NO_EXIST,
|
||||
@ -102,9 +95,7 @@ handle_addid(Client *client, int argc, char *argv[])
|
||||
return COMMAND_RETURN_ERROR;
|
||||
}
|
||||
|
||||
result = playlist_append_uri(&client->playlist,
|
||||
client->player_control,
|
||||
uri, &added_id);
|
||||
result = client->partition.AppendURI(uri, &added_id);
|
||||
}
|
||||
|
||||
if (result != PLAYLIST_RESULT_SUCCESS)
|
||||
@ -114,15 +105,11 @@ handle_addid(Client *client, int argc, char *argv[])
|
||||
unsigned to;
|
||||
if (!check_unsigned(client, &to, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
result = playlist_move_id(&client->playlist,
|
||||
client->player_control,
|
||||
added_id, to);
|
||||
result = client->partition.MoveId(added_id, to);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS) {
|
||||
enum command_return ret =
|
||||
print_playlist_result(client, result);
|
||||
playlist_delete_id(&client->playlist,
|
||||
client->player_control,
|
||||
added_id);
|
||||
client->partition.DeleteId(added_id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -135,14 +122,11 @@ enum command_return
|
||||
handle_delete(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned start, end;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_range(client, &start, &end, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
result = playlist_delete_range(&client->playlist,
|
||||
client->player_control,
|
||||
start, end);
|
||||
enum playlist_result result = client->partition.DeleteRange(start, end);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -150,13 +134,11 @@ enum command_return
|
||||
handle_deleteid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned id;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_unsigned(client, &id, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
result = playlist_delete_id(&client->playlist,
|
||||
client->player_control, id);
|
||||
enum playlist_result result = client->partition.DeleteId(id);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -176,8 +158,7 @@ handle_shuffle(G_GNUC_UNUSED Client *client,
|
||||
if (argc == 2 && !check_range(client, &start, &end, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
playlist_shuffle(&client->playlist, client->player_control,
|
||||
start, end);
|
||||
client->partition.Shuffle(start, end);
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -185,7 +166,7 @@ enum command_return
|
||||
handle_clear(G_GNUC_UNUSED Client *client,
|
||||
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
||||
{
|
||||
playlist_clear(&client->playlist, client->player_control);
|
||||
client->partition.ClearQueue();
|
||||
return COMMAND_RETURN_OK;
|
||||
}
|
||||
|
||||
@ -296,10 +277,9 @@ handle_prio(Client *client, int argc, char *argv[])
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
enum playlist_result result =
|
||||
playlist_set_priority(&client->playlist,
|
||||
client->player_control,
|
||||
start_position, end_position,
|
||||
priority);
|
||||
client->partition.SetPriorityRange(start_position,
|
||||
end_position,
|
||||
priority);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS)
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
@ -327,9 +307,7 @@ handle_prioid(Client *client, int argc, char *argv[])
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
enum playlist_result result =
|
||||
playlist_set_priority_id(&client->playlist,
|
||||
client->player_control,
|
||||
song_id, priority);
|
||||
client->partition.SetPriorityId(song_id, priority);
|
||||
if (result != PLAYLIST_RESULT_SUCCESS)
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
@ -342,14 +320,14 @@ handle_move(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned start, end;
|
||||
int to;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_range(client, &start, &end, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (!check_int(client, &to, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
result = playlist_move_range(&client->playlist, client->player_control,
|
||||
start, end, to);
|
||||
|
||||
enum playlist_result result =
|
||||
client->partition.MoveRange(start, end, to);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -358,14 +336,12 @@ handle_moveid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned id;
|
||||
int to;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_unsigned(client, &id, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (!check_int(client, &to, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
result = playlist_move_id(&client->playlist, client->player_control,
|
||||
id, to);
|
||||
enum playlist_result result = client->partition.MoveId(id, to);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -373,14 +349,14 @@ enum command_return
|
||||
handle_swap(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned song1, song2;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_unsigned(client, &song1, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (!check_unsigned(client, &song2, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
result = playlist_swap_songs(&client->playlist, client->player_control,
|
||||
song1, song2);
|
||||
|
||||
enum playlist_result result =
|
||||
client->partition.SwapPositions(song1, song2);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
||||
@ -388,14 +364,12 @@ enum command_return
|
||||
handle_swapid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
{
|
||||
unsigned id1, id2;
|
||||
enum playlist_result result;
|
||||
|
||||
if (!check_unsigned(client, &id1, argv[1]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (!check_unsigned(client, &id2, argv[2]))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
result = playlist_swap_songs_id(&client->playlist,
|
||||
client->player_control,
|
||||
id1, id2);
|
||||
|
||||
enum playlist_result result = client->partition.SwapIds(id1, id2);
|
||||
return print_playlist_result(client, result);
|
||||
}
|
||||
|
@ -64,9 +64,7 @@ song_remove_event(void)
|
||||
sticker_song_delete(removed_song);
|
||||
#endif
|
||||
|
||||
playlist_delete_song(&global_partition->playlist,
|
||||
&global_partition->pc,
|
||||
removed_song);
|
||||
global_partition->DeleteSong(*removed_song);
|
||||
|
||||
/* clear "removed_song" and send signal to update thread */
|
||||
g_mutex_lock(remove_mutex);
|
||||
|
Loading…
Reference in New Issue
Block a user