From 129eb178eb45fd148ee8b9822b11e7a4bf8ccc00 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 24 Jan 2014 00:17:50 +0100 Subject: Queue*: move to queue/ --- src/queue/IdTable.hxx | 91 +++++++++ src/queue/Queue.cxx | 482 +++++++++++++++++++++++++++++++++++++++++++++++ src/queue/Queue.hxx | 378 +++++++++++++++++++++++++++++++++++++ src/queue/QueuePrint.cxx | 102 ++++++++++ src/queue/QueuePrint.hxx | 54 ++++++ src/queue/QueueSave.cxx | 121 ++++++++++++ src/queue/QueueSave.hxx | 42 +++++ 7 files changed, 1270 insertions(+) create mode 100644 src/queue/IdTable.hxx create mode 100644 src/queue/Queue.cxx create mode 100644 src/queue/Queue.hxx create mode 100644 src/queue/QueuePrint.cxx create mode 100644 src/queue/QueuePrint.hxx create mode 100644 src/queue/QueueSave.cxx create mode 100644 src/queue/QueueSave.hxx (limited to 'src/queue') diff --git a/src/queue/IdTable.hxx b/src/queue/IdTable.hxx new file mode 100644 index 000000000..8e445243d --- /dev/null +++ b/src/queue/IdTable.hxx @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2003-2014 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_ID_TABLE_HXX +#define MPD_ID_TABLE_HXX + +#include "Compiler.h" + +#include + +#include + +/** + * A table that maps id numbers to position numbers. + */ +class IdTable { + unsigned size; + + unsigned next; + + int *data; + +public: + IdTable(unsigned _size):size(_size), next(1), data(new int[size]) { + std::fill_n(data, size, -1); + } + + ~IdTable() { + delete[] data; + } + + int IdToPosition(unsigned id) const { + return id < size + ? data[id] + : -1; + } + + unsigned GenerateId() { + assert(next > 0); + assert(next < size); + + while (true) { + unsigned id = next; + + ++next; + if (next == size) + next = 1; + + if (data[id] < 0) + return id; + } + } + + unsigned Insert(unsigned position) { + unsigned id = GenerateId(); + data[id] = position; + return id; + } + + void Move(unsigned id, unsigned position) { + assert(id < size); + assert(data[id] >= 0); + + data[id] = position; + } + + void Erase(unsigned id) { + assert(id < size); + assert(data[id] >= 0); + + data[id] = -1; + } +}; + +#endif diff --git a/src/queue/Queue.cxx b/src/queue/Queue.cxx new file mode 100644 index 000000000..99b545ab1 --- /dev/null +++ b/src/queue/Queue.cxx @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2003-2014 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 "Queue.hxx" +#include "DetachedSong.hxx" + +Queue::Queue(unsigned _max_length) + :max_length(_max_length), length(0), + version(1), + items(new Item[max_length]), + order(new unsigned[max_length]), + id_table(max_length * HASH_MULT), + repeat(false), + single(false), + consume(false), + random(false) +{ +} + +Queue::~Queue() +{ + Clear(); + + delete[] items; + delete[] order; +} + +int +Queue::GetNextOrder(unsigned _order) const +{ + assert(_order < length); + + if (single && repeat && !consume) + return _order; + else if (_order + 1 < length) + return _order + 1; + else if (repeat && (_order > 0 || !consume)) + /* restart at first song */ + return 0; + else + /* end of queue */ + return -1; +} + +void +Queue::IncrementVersion() +{ + static unsigned long max = ((uint32_t) 1 << 31) - 1; + + version++; + + if (version >= max) { + for (unsigned i = 0; i < length; i++) + items[i].version = 0; + + version = 1; + } +} + +void +Queue::ModifyAtOrder(unsigned _order) +{ + assert(_order < length); + + unsigned position = order[_order]; + ModifyAtPosition(position); +} + +unsigned +Queue::Append(DetachedSong &&song, uint8_t priority) +{ + assert(!IsFull()); + + const unsigned position = length++; + const unsigned id = id_table.Insert(position); + + auto &item = items[position]; + item.song = new DetachedSong(std::move(song)); + item.id = id; + item.version = version; + item.priority = priority; + + order[position] = position; + + return id; +} + +void +Queue::SwapPositions(unsigned position1, unsigned position2) +{ + unsigned id1 = items[position1].id; + unsigned id2 = items[position2].id; + + std::swap(items[position1], items[position2]); + + items[position1].version = version; + items[position2].version = version; + + id_table.Move(id1, position2); + id_table.Move(id2, position1); +} + +void +Queue::MovePostion(unsigned from, unsigned to) +{ + const Item tmp = items[from]; + + /* move songs to one less in from->to */ + + for (unsigned i = from; i < to; i++) + MoveItemTo(i + 1, i); + + /* move songs to one more in to->from */ + + for (unsigned i = from; i > to; i--) + MoveItemTo(i - 1, i); + + /* put song at _to_ */ + + id_table.Move(tmp.id, to); + items[to] = tmp; + items[to].version = version; + + /* now deal with order */ + + if (random) { + for (unsigned i = 0; i < length; i++) { + if (order[i] > from && order[i] <= to) + order[i]--; + else if (order[i] < from && + order[i] >= to) + order[i]++; + else if (from == order[i]) + order[i] = to; + } + } +} + +void +Queue::MoveRange(unsigned start, unsigned end, unsigned to) +{ + Item tmp[end - start]; + // Copy the original block [start,end-1] + for (unsigned i = start; i < end; i++) + tmp[i - start] = items[i]; + + // If to > start, we need to move to-start items to start, starting from end + for (unsigned i = end; i < end + to - start; i++) + MoveItemTo(i, start + i - end); + + // If to < start, we need to move start-to items to newend (= end + to - start), starting from to + // This is the same as moving items from start-1 to to (decreasing), with start-1 going to end-1 + // We have to iterate in this order to avoid writing over something we haven't yet moved + for (int i = start - 1; i >= int(to); i--) + MoveItemTo(i, i + end - start); + + // Copy the original block back in, starting at to. + for (unsigned i = start; i< end; i++) + { + id_table.Move(tmp[i - start].id, to + i - start); + items[to + i - start] = tmp[i-start]; + items[to + i - start].version = version; + } + + if (random) { + // Update the positions in the queue. + // Note that the ranges for these cases are the same as the ranges of + // the loops above. + for (unsigned i = 0; i < length; i++) { + if (order[i] >= end && order[i] < to + end - start) + order[i] -= end - start; + else if (order[i] < start && + order[i] >= to) + order[i] += end - start; + else if (start <= order[i] && order[i] < end) + order[i] += to - start; + } + } +} + +void +Queue::MoveOrder(unsigned from_order, unsigned to_order) +{ + assert(from_order < length); + assert(to_order <= length); + + const unsigned from_position = OrderToPosition(from_order); + + if (from_order < to_order) { + for (unsigned i = from_order; i < to_order; ++i) + order[i] = order[i + 1]; + } else { + for (unsigned i = from_order; i > to_order; --i) + order[i] = order[i - 1]; + } + + order[to_order] = from_position; +} + +void +Queue::DeletePosition(unsigned position) +{ + assert(position < length); + + delete items[position].song; + + const unsigned id = PositionToId(position); + const unsigned _order = PositionToOrder(position); + + --length; + + /* release the song id */ + + id_table.Erase(id); + + /* delete song from songs array */ + + for (unsigned i = position; i < length; i++) + MoveItemTo(i + 1, i); + + /* delete the entry from the order array */ + + for (unsigned i = _order; i < length; i++) + order[i] = order[i + 1]; + + /* readjust values in the order array */ + + for (unsigned i = 0; i < length; i++) + if (order[i] > position) + --order[i]; +} + +void +Queue::Clear() +{ + for (unsigned i = 0; i < length; i++) { + Item *item = &items[i]; + + delete item->song; + + id_table.Erase(item->id); + } + + length = 0; +} + +static void +queue_sort_order_by_priority(Queue *queue, unsigned start, unsigned end) +{ + assert(queue != nullptr); + assert(queue->random); + assert(start <= end); + assert(end <= queue->length); + + auto cmp = [queue](unsigned a_pos, unsigned b_pos){ + const Queue::Item &a = queue->items[a_pos]; + const Queue::Item &b = queue->items[b_pos]; + + return a.priority > b.priority; + }; + + std::stable_sort(queue->order + start, queue->order + end, cmp); +} + +void +Queue::ShuffleOrderRange(unsigned start, unsigned end) +{ + assert(random); + assert(start <= end); + assert(end <= length); + + rand.AutoCreate(); + std::shuffle(order + start, order + end, rand); +} + +/** + * Sort the "order" of items by priority, and then shuffle each + * priority group. + */ +void +Queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end) +{ + assert(random); + assert(start <= end); + assert(end <= length); + + if (start == end) + return; + + /* first group the range by priority */ + queue_sort_order_by_priority(this, start, end); + + /* now shuffle each priority group */ + unsigned group_start = start; + uint8_t group_priority = GetOrderPriority(start); + + for (unsigned i = start + 1; i < end; ++i) { + const uint8_t priority = GetOrderPriority(i); + assert(priority <= group_priority); + + if (priority != group_priority) { + /* start of a new group - shuffle the one that + has just ended */ + ShuffleOrderRange(group_start, i); + group_start = i; + group_priority = priority; + } + } + + /* shuffle the last group */ + ShuffleOrderRange(group_start, end); +} + +void +Queue::ShuffleOrder() +{ + ShuffleOrderRangeWithPriority(0, length); +} + +void +Queue::ShuffleOrderFirst(unsigned start, unsigned end) +{ + rand.AutoCreate(); + + std::uniform_int_distribution distribution(start, end - 1); + SwapOrders(start, distribution(rand)); +} + +void +Queue::ShuffleOrderLast(unsigned start, unsigned end) +{ + rand.AutoCreate(); + + std::uniform_int_distribution distribution(start, end - 1); + SwapOrders(end - 1, distribution(rand)); +} + +void +Queue::ShuffleRange(unsigned start, unsigned end) +{ + assert(start <= end); + assert(end <= length); + + rand.AutoCreate(); + + for (unsigned i = start; i < end; i++) { + std::uniform_int_distribution distribution(start, + end - 1); + unsigned ri = distribution(rand); + SwapPositions(i, ri); + } +} + +unsigned +Queue::FindPriorityOrder(unsigned start_order, uint8_t priority, + unsigned exclude_order) const +{ + assert(random); + assert(start_order <= length); + + for (unsigned i = start_order; i < length; ++i) { + const unsigned position = OrderToPosition(i); + const Item *item = &items[position]; + if (item->priority <= priority && i != exclude_order) + return i; + } + + return length; +} + +unsigned +Queue::CountSamePriority(unsigned start_order, uint8_t priority) const +{ + assert(random); + assert(start_order <= length); + + for (unsigned i = start_order; i < length; ++i) { + const unsigned position = OrderToPosition(i); + const Item *item = &items[position]; + if (item->priority != priority) + return i - start_order; + } + + return length - start_order; +} + +bool +Queue::SetPriority(unsigned position, uint8_t priority, int after_order) +{ + assert(position < length); + + Item *item = &items[position]; + uint8_t old_priority = item->priority; + if (old_priority == priority) + return false; + + item->version = version; + item->priority = priority; + + if (!random) + /* don't reorder if not in random mode */ + return true; + + unsigned _order = PositionToOrder(position); + if (after_order >= 0) { + if (_order == (unsigned)after_order) + /* don't reorder the current song */ + return true; + + if (_order < (unsigned)after_order) { + /* the specified song has been played already + - enqueue it only if its priority has just + become bigger than the current one's */ + + const unsigned after_position = + OrderToPosition(after_order); + const Item *after_item = + &items[after_position]; + if (old_priority > after_item->priority || + priority <= after_item->priority) + /* priority hasn't become bigger */ + return true; + } + } + + /* move the item to the beginning of the priority group (or + create a new priority group) */ + + const unsigned before_order = + FindPriorityOrder(after_order + 1, priority, _order); + const unsigned new_order = before_order > _order + ? before_order - 1 + : before_order; + MoveOrder(_order, new_order); + + /* shuffle the song within that priority group */ + + const unsigned priority_count = CountSamePriority(new_order, priority); + assert(priority_count >= 1); + ShuffleOrderFirst(new_order, new_order + priority_count); + + return true; +} + +bool +Queue::SetPriorityRange(unsigned start_position, unsigned end_position, + uint8_t priority, int after_order) +{ + assert(start_position <= end_position); + assert(end_position <= length); + + bool modified = false; + int after_position = after_order >= 0 + ? (int)OrderToPosition(after_order) + : -1; + for (unsigned i = start_position; i < end_position; ++i) { + after_order = after_position >= 0 + ? (int)PositionToOrder(after_position) + : -1; + + modified |= SetPriority(i, priority, after_order); + } + + return modified; +} diff --git a/src/queue/Queue.hxx b/src/queue/Queue.hxx new file mode 100644 index 000000000..016619e65 --- /dev/null +++ b/src/queue/Queue.hxx @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2003-2014 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_QUEUE_HXX +#define MPD_QUEUE_HXX + +#include "Compiler.h" +#include "IdTable.hxx" +#include "util/LazyRandomEngine.hxx" + +#include + +#include +#include + +class DetachedSong; + +/** + * A queue of songs. This is the backend of the playlist: it contains + * an ordered list of songs. + * + * Songs can be addressed in three possible ways: + * + * - the position in the queue + * - the unique id (which stays the same, regardless of moves) + * - the order number (which only differs from "position" in random mode) + */ +struct Queue { + /** + * reserve max_length * HASH_MULT elements in the id + * number space + */ + static constexpr unsigned HASH_MULT = 4; + + /** + * One element of the queue: basically a song plus some queue specific + * information attached. + */ + struct Item { + DetachedSong *song; + + /** the unique id of this item in the queue */ + unsigned id; + + /** when was this item last changed? */ + uint32_t version; + + /** + * The priority of this item, between 0 and 255. High + * priority value means that this song gets played first in + * "random" mode. + */ + uint8_t priority; + }; + + /** configured maximum length of the queue */ + unsigned max_length; + + /** number of songs in the queue */ + unsigned length; + + /** the current version number */ + uint32_t version; + + /** all songs in "position" order */ + Item *items; + + /** map order numbers to positions */ + unsigned *order; + + /** map song ids to positions */ + IdTable id_table; + + /** repeat playback when the end of the queue has been + reached? */ + bool repeat; + + /** play only current song. */ + bool single; + + /** remove each played files. */ + bool consume; + + /** play back songs in random order? */ + bool random; + + /** random number generator for shuffle and random mode */ + LazyRandomEngine rand; + + explicit Queue(unsigned max_length); + + /** + * Deinitializes a queue object. It does not free the queue + * pointer itself. + */ + ~Queue(); + + Queue(const Queue &) = delete; + Queue &operator=(const Queue &) = delete; + + unsigned GetLength() const { + assert(length <= max_length); + + return length; + } + + /** + * Determine if the queue is empty, i.e. there are no songs. + */ + bool IsEmpty() const { + return length == 0; + } + + /** + * Determine if the maximum number of songs has been reached. + */ + bool IsFull() const { + assert(length <= max_length); + + return length >= max_length; + } + + /** + * Is that a valid position number? + */ + bool IsValidPosition(unsigned position) const { + return position < length; + } + + /** + * Is that a valid order number? + */ + bool IsValidOrder(unsigned _order) const { + return _order < length; + } + + int IdToPosition(unsigned id) const { + return id_table.IdToPosition(id); + } + + int PositionToId(unsigned position) const + { + assert(position < length); + + return items[position].id; + } + + gcc_pure + unsigned OrderToPosition(unsigned _order) const { + assert(_order < length); + + return order[_order]; + } + + gcc_pure + unsigned PositionToOrder(unsigned position) const { + assert(position < length); + + for (unsigned i = 0;; ++i) { + assert(i < length); + + if (order[i] == position) + return i; + } + } + + gcc_pure + uint8_t GetPriorityAtPosition(unsigned position) const { + assert(position < length); + + return items[position].priority; + } + + const Item &GetOrderItem(unsigned i) const { + assert(IsValidOrder(i)); + + return items[OrderToPosition(i)]; + } + + uint8_t GetOrderPriority(unsigned i) const { + return GetOrderItem(i).priority; + } + + /** + * Returns the song at the specified position. + */ + DetachedSong &Get(unsigned position) const { + assert(position < length); + + return *items[position].song; + } + + /** + * Returns the song at the specified order number. + */ + DetachedSong &GetOrder(unsigned _order) const { + return Get(OrderToPosition(_order)); + } + + /** + * Is the song at the specified position newer than the specified + * version? + */ + bool IsNewerAtPosition(unsigned position, uint32_t _version) const { + assert(position < length); + + return _version > version || + items[position].version >= _version || + items[position].version == 0; + } + + /** + * Returns the order number following the specified one. This takes + * end of queue and "repeat" mode into account. + * + * @return the next order number, or -1 to stop playback + */ + gcc_pure + int GetNextOrder(unsigned order) const; + + /** + * Increments the queue's version number. This handles integer + * overflow well. + */ + void IncrementVersion(); + + /** + * Marks the specified song as "modified". Call + * IncrementVersion() after all modifications have been made. + * number. + */ + void ModifyAtPosition(unsigned position) { + assert(position < length); + + items[position].version = version; + } + + /** + * Marks the specified song as "modified". Call + * IncrementVersion() after all modifications have been made. + * number. + */ + void ModifyAtOrder(unsigned order); + + /** + * Appends a song to the queue and returns its position. Prior to + * that, the caller must check if the queue is already full. + * + * If a song is not in the database (determined by + * Song::IsInDatabase()), it is freed when removed from the + * queue. + * + * @param priority the priority of this new queue item + */ + unsigned Append(DetachedSong &&song, uint8_t priority); + + /** + * Swaps two songs, addressed by their position. + */ + void SwapPositions(unsigned position1, unsigned position2); + + /** + * Swaps two songs, addressed by their order number. + */ + void SwapOrders(unsigned order1, unsigned order2) { + std::swap(order[order1], order[order2]); + } + + /** + * Moves a song to a new position. + */ + void MovePostion(unsigned from, unsigned to); + + /** + * Moves a range of songs to a new position. + */ + void MoveRange(unsigned start, unsigned end, unsigned to); + + /** + * Removes a song from the playlist. + */ + void DeletePosition(unsigned position); + + /** + * Removes all songs from the playlist. + */ + void Clear(); + + /** + * Initializes the "order" array, and restores "normal" order. + */ + void RestoreOrder() { + for (unsigned i = 0; i < length; ++i) + order[i] = i; + } + + /** + * Shuffle the order of items in the specified range, ignoring + * their priorities. + */ + void ShuffleOrderRange(unsigned start, unsigned end); + + /** + * Shuffle the order of items in the specified range, taking their + * priorities into account. + */ + void ShuffleOrderRangeWithPriority(unsigned start, unsigned end); + + /** + * Shuffles the virtual order of songs, but does not move them + * physically. This is used in random mode. + */ + void ShuffleOrder(); + + void ShuffleOrderFirst(unsigned start, unsigned end); + + /** + * Shuffles the virtual order of the last song in the specified + * (order) range. This is used in random mode after a song has been + * appended by queue_append(). + */ + void ShuffleOrderLast(unsigned start, unsigned end); + + /** + * Shuffles a (position) range in the queue. The songs are physically + * shuffled, not by using the "order" mapping. + */ + void ShuffleRange(unsigned start, unsigned end); + + bool SetPriority(unsigned position, uint8_t priority, int after_order); + + bool SetPriorityRange(unsigned start_position, unsigned end_position, + uint8_t priority, int after_order); + +private: + /** + * Moves a song to a new position in the "order" list. + */ + void MoveOrder(unsigned from_order, unsigned to_order); + + void MoveItemTo(unsigned from, unsigned to) { + unsigned from_id = items[from].id; + + items[to] = items[from]; + items[to].version = version; + id_table.Move(from_id, to); + } + + /** + * Find the first item that has this specified priority or + * higher. + */ + gcc_pure + unsigned FindPriorityOrder(unsigned start_order, uint8_t priority, + unsigned exclude_order) const; + + gcc_pure + unsigned CountSamePriority(unsigned start_order, + uint8_t priority) const; +}; + +#endif diff --git a/src/queue/QueuePrint.cxx b/src/queue/QueuePrint.cxx new file mode 100644 index 000000000..1f3c8fd57 --- /dev/null +++ b/src/queue/QueuePrint.cxx @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2003-2014 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 "QueuePrint.hxx" +#include "Queue.hxx" +#include "SongFilter.hxx" +#include "SongPrint.hxx" +#include "Client.hxx" + +/** + * Send detailed information about a range of songs in the queue to a + * client. + * + * @param client the client which has requested information + * @param start the index of the first song (including) + * @param end the index of the last song (excluding) + */ +static void +queue_print_song_info(Client &client, const Queue &queue, + unsigned position) +{ + song_print_info(client, queue.Get(position)); + client_printf(client, "Pos: %u\nId: %u\n", + position, queue.PositionToId(position)); + + uint8_t priority = queue.GetPriorityAtPosition(position); + if (priority != 0) + client_printf(client, "Prio: %u\n", priority); +} + +void +queue_print_info(Client &client, const Queue &queue, + unsigned start, unsigned end) +{ + assert(start <= end); + assert(end <= queue.GetLength()); + + for (unsigned i = start; i < end; ++i) + queue_print_song_info(client, queue, i); +} + +void +queue_print_uris(Client &client, const Queue &queue, + unsigned start, unsigned end) +{ + assert(start <= end); + assert(end <= queue.GetLength()); + + for (unsigned i = start; i < end; ++i) { + client_printf(client, "%i:", i); + song_print_uri(client, queue.Get(i)); + } +} + +void +queue_print_changes_info(Client &client, const Queue &queue, + uint32_t version) +{ + for (unsigned i = 0; i < queue.GetLength(); i++) { + if (queue.IsNewerAtPosition(i, version)) + queue_print_song_info(client, queue, i); + } +} + +void +queue_print_changes_position(Client &client, const Queue &queue, + uint32_t version) +{ + for (unsigned i = 0; i < queue.GetLength(); i++) + if (queue.IsNewerAtPosition(i, version)) + client_printf(client, "cpos: %i\nId: %i\n", + i, queue.PositionToId(i)); +} + +void +queue_find(Client &client, const Queue &queue, + const SongFilter &filter) +{ + for (unsigned i = 0; i < queue.GetLength(); i++) { + const DetachedSong &song = queue.Get(i); + + if (filter.Match(song)) + queue_print_song_info(client, queue, i); + } +} diff --git a/src/queue/QueuePrint.hxx b/src/queue/QueuePrint.hxx new file mode 100644 index 000000000..1aa876219 --- /dev/null +++ b/src/queue/QueuePrint.hxx @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2003-2014 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. + */ + +/* + * This library sends information about songs in the queue to the + * client. + */ + +#ifndef MPD_QUEUE_PRINT_HXX +#define MPD_QUEUE_PRINT_HXX + +#include + +struct Queue; +class SongFilter; +class Client; + +void +queue_print_info(Client &client, const Queue &queue, + unsigned start, unsigned end); + +void +queue_print_uris(Client &client, const Queue &queue, + unsigned start, unsigned end); + +void +queue_print_changes_info(Client &client, const Queue &queue, + uint32_t version); + +void +queue_print_changes_position(Client &client, const Queue &queue, + uint32_t version); + +void +queue_find(Client &client, const Queue &queue, + const SongFilter &filter); + +#endif diff --git a/src/queue/QueueSave.cxx b/src/queue/QueueSave.cxx new file mode 100644 index 000000000..87de02c56 --- /dev/null +++ b/src/queue/QueueSave.cxx @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2003-2014 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 "QueueSave.hxx" +#include "Queue.hxx" +#include "PlaylistError.hxx" +#include "DetachedSong.hxx" +#include "SongSave.hxx" +#include "DatabaseSong.hxx" +#include "fs/TextFile.hxx" +#include "util/StringUtil.hxx" +#include "util/UriUtil.hxx" +#include "util/Error.hxx" +#include "fs/Traits.hxx" +#include "Log.hxx" + +#include + +#define PRIO_LABEL "Prio: " + +static void +queue_save_database_song(FILE *fp, int idx, const DetachedSong &song) +{ + fprintf(fp, "%i:%s\n", idx, song.GetURI()); +} + +static void +queue_save_full_song(FILE *fp, const DetachedSong &song) +{ + song_save(fp, song); +} + +static void +queue_save_song(FILE *fp, int idx, const DetachedSong &song) +{ + if (song.IsInDatabase()) + queue_save_database_song(fp, idx, song); + else + queue_save_full_song(fp, song); +} + +void +queue_save(FILE *fp, const Queue &queue) +{ + for (unsigned i = 0; i < queue.GetLength(); i++) { + uint8_t prio = queue.GetPriorityAtPosition(i); + if (prio != 0) + fprintf(fp, PRIO_LABEL "%u\n", prio); + + queue_save_song(fp, i, queue.Get(i)); + } +} + +void +queue_load_song(TextFile &file, const char *line, Queue &queue) +{ + if (queue.IsFull()) + return; + + uint8_t priority = 0; + if (StringStartsWith(line, PRIO_LABEL)) { + priority = strtoul(line + sizeof(PRIO_LABEL) - 1, nullptr, 10); + + line = file.ReadLine(); + if (line == nullptr) + return; + } + + DetachedSong *song; + + if (StringStartsWith(line, SONG_BEGIN)) { + const char *uri = line + sizeof(SONG_BEGIN) - 1; + if (!uri_has_scheme(uri) && !PathTraitsUTF8::IsAbsolute(uri)) + return; + + Error error; + song = song_load(file, uri, error); + if (song == nullptr) { + LogError(error); + return; + } + } else { + char *endptr; + long ret = strtol(line, &endptr, 10); + if (ret < 0 || *endptr != ':' || endptr[1] == 0) { + LogError(playlist_domain, + "Malformed playlist line in state file"); + return; + } + + const char *uri = endptr + 1; + + if (uri_has_scheme(uri)) { + song = new DetachedSong(uri); + } else { + song = DatabaseDetachSong(uri, IgnoreError()); + if (song == nullptr) + return; + } + } + + queue.Append(std::move(*song), priority); + delete song; +} diff --git a/src/queue/QueueSave.hxx b/src/queue/QueueSave.hxx new file mode 100644 index 000000000..c9a646369 --- /dev/null +++ b/src/queue/QueueSave.hxx @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2003-2014 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. + */ + +/* + * This library saves the queue into the state file, and also loads it + * back into memory. + */ + +#ifndef MPD_QUEUE_SAVE_HXX +#define MPD_QUEUE_SAVE_HXX + +#include + +struct Queue; +class TextFile; + +void +queue_save(FILE *fp, const Queue &queue); + +/** + * Loads one song from the state file and appends it to the queue. + */ +void +queue_load_song(TextFile &file, const char *line, Queue &queue); + +#endif -- cgit v1.2.3