MythTV master
playlistcontainer.cpp
Go to the documentation of this file.
1// C/C++
2#include <algorithm>
3#include <thread>
4#include <utility>
5
6// MythTV
11
12// mythmusic
13#include "playlist.h"
14#include "playlistcontainer.h"
15
17{
18 RunProlog();
19 while (!m_allMusic->doneLoading())
20 {
21 std::this_thread::sleep_for(250ms);
22 }
23 m_parent->load();
24 RunEpilog();
25}
26
27#define LOC QString("PlaylistContainer: ")
28#define LOC_WARN QString("PlaylistContainer, Warning: ")
29#define LOC_ERR QString("PlaylistContainer, Error: ")
30
32 m_playlistsLoader(new PlaylistLoadingThread(this, all_music)),
33 m_myHost(gCoreContext->GetHostName()),
34
35 m_ratingWeight( gCoreContext->GetNumSetting("IntelliRatingWeight", 2)),
36 m_playCountWeight(gCoreContext->GetNumSetting("IntelliPlayCountWeight", 2)),
37 m_lastPlayWeight( gCoreContext->GetNumSetting("IntelliLastPlayWeight", 2)),
38 m_randomWeight( gCoreContext->GetNumSetting("IntelliRandomWeight", 2))
39{
41}
42
44{
46 delete m_playlistsLoader;
47 m_playlistsLoader = nullptr;
48
49 delete m_activePlaylist;
50 delete m_streamPlaylist;
52 {
53 while (!m_allPlaylists->empty())
54 {
55 delete m_allPlaylists->front();
56 m_allPlaylists->pop_front();
57 }
58 delete m_allPlaylists;
59 }
60}
61
63 int &lastplay, int &random) const
64{
66 playcount = m_playCountWeight;
67 lastplay = m_lastPlayWeight;
68 random = m_randomWeight;
69}
70
72{
73 m_doneLoading = false;
76
79
80 m_allPlaylists = new QList<Playlist*>;
81
83
85
87 query.prepare("SELECT playlist_id FROM music_playlists "
88 "WHERE playlist_name != :DEFAULT"
89 " AND playlist_name != :BACKUP "
90 " AND playlist_name != :STREAM "
91 " AND (hostname = '' OR hostname = :HOST) "
92 "ORDER BY playlist_name;");
93 query.bindValue(":DEFAULT", DEFAULT_PLAYLIST_NAME);
94 query.bindValue(":BACKUP", "backup_playlist_storage");
95 query.bindValue(":STREAM", DEFAULT_STREAMLIST_NAME);
96 query.bindValue(":HOST", m_myHost);
97
98 if (!query.exec())
99 {
100 MythDB::DBError("Querying playlists", query);
101 }
102 else
103 {
104 while (query.next())
105 {
106 auto *temp_playlist = new Playlist();
107 // No, we don't destruct this ...
108 temp_playlist->setParent(this);
109 temp_playlist->loadPlaylistByID(query.value(0).toInt(), m_myHost);
110 m_allPlaylists->push_back(temp_playlist);
111 // ... cause it's sitting on this PtrList
112 }
113 }
114
115 m_doneLoading = true;
116}
117
118// resync all the playlists after a rescan just in case some tracks were removed
120{
121 // NOLINTNEXTLINE(modernize-loop-convert)
122 for (auto it = m_allPlaylists->begin(); it != m_allPlaylists->end(); ++it)
123 (*it)->resync();
124
126}
127
129{
130 // Debugging
132 for (const auto & playlist : std::as_const(*m_allPlaylists))
133 playlist->describeYourself();
134}
135
137{
138 // return a pointer to a playlist
139 // by id;
140
141 if (m_activePlaylist->getID() == id)
142 {
143 return m_activePlaylist;
144 }
145
146 auto idmatch = [id](const auto & playlist)
147 { return playlist->getID() == id; };
148 auto it = std::ranges::find_if(std::as_const(*m_allPlaylists), idmatch);
149 if (it != m_allPlaylists->cend())
150 return *it;
151
152 LOG(VB_GENERAL, LOG_ERR,
153 "getPlaylistName() called with unknown index number");
154 return nullptr;
155}
156
158{
159 // return a pointer to a playlist
160 // by name;
161 auto namematch = [name](const auto & playlist)
162 { return playlist->getName() == name; };
163 auto it = std::ranges::find_if(std::as_const(*m_allPlaylists), namematch);
164 if (it != m_allPlaylists->cend())
165 return *it;
166
167 LOG(VB_GENERAL, LOG_ERR, QString("getPlaylistName() called with unknown name: %1").arg(name));
168 return nullptr;
169}
170
172{
173 // NOLINTNEXTLINE(modernize-loop-convert)
174 for (auto it = m_allPlaylists->begin(); it != m_allPlaylists->end(); ++it)
175 {
176 if ((*it)->hasChanged())
177 (*it)->savePlaylist((*it)->getName(), m_myHost);
178 }
179
182}
183
185{
186 auto *new_list = new Playlist();
187 new_list->setParent(this);
188
189 // Need to touch the database to get persistent ID
190 new_list->savePlaylist(name, m_myHost);
191
192 m_allPlaylists->push_back(new_list);
193}
194
195void PlaylistContainer::copyNewPlaylist(const QString &name)
196{
197 auto *new_list = new Playlist();
198 new_list->setParent(this);
199
200 // Need to touch the database to get persistent ID
201 new_list->savePlaylist(name, m_myHost);
202
203 m_allPlaylists->push_back(new_list);
204 m_activePlaylist->copyTracks(new_list, false);
205}
206
208{
210 Playlist *copy_from = getPlaylist(index);
211 if (!copy_from)
212 {
213 LOG(VB_GENERAL, LOG_ERR, LOC + "copyToActive() " +
214 QString("Unknown playlist: %1").arg(index));
215 return;
216 }
217 copy_from->copyTracks(m_activePlaylist, true);
218}
219
220void PlaylistContainer::renamePlaylist(int index, const QString& new_name)
221{
222 Playlist *list_to_rename = getPlaylist(index);
223 if (list_to_rename)
224 {
225 list_to_rename->setName(new_name);
226 list_to_rename->changed();
227 }
228}
229
231{
232 Playlist *list_to_kill = getPlaylist(kill_me);
233 if (!list_to_kill)
234 {
235 LOG(VB_GENERAL, LOG_ERR, LOC + "deletePlaylist() " +
236 QString("Unknown playlist: %1").arg(kill_me));
237 return;
238 }
239
240 list_to_kill->removeAllTracks();
241 m_allPlaylists->removeAll(list_to_kill);
242
244 query.prepare("DELETE FROM music_playlists WHERE playlist_id = :ID ;");
245 query.bindValue(":ID", kill_me);
246
247 if (!query.exec() || query.numRowsAffected() < 1)
248 {
249 MythDB::DBError("playlist delete", query);
250 }
251}
252
253
254QString PlaylistContainer::getPlaylistName(int index, bool &reference)
255{
257 {
258 if (m_activePlaylist->getID() == index)
259 {
260 return m_activePlaylist->getName();
261 }
262
263 auto indexmatch = [index](const auto & playlist)
264 { return playlist->getID() == index; };
265 auto it = std::ranges::find_if(std::as_const(*m_allPlaylists), indexmatch);
266 if (it != m_allPlaylists->cend())
267 return (*it)->getName();
268 }
269
270 LOG(VB_GENERAL, LOG_ERR, LOC +
271 "getPlaylistName() called with unknown index number");
272
273 reference = true;
274 return tr("Something is Wrong");
275}
276
277bool PlaylistContainer::nameIsUnique(const QString& a_name, int which_id)
278{
279 if (a_name == DEFAULT_PLAYLIST_NAME)
280 return false;
281
282 auto itemfound = [a_name,which_id](const auto & playlist)
283 { return playlist->getName() == a_name &&
284 playlist->getID() != which_id; };
285 return std::ranges::none_of(std::as_const(*m_allPlaylists), itemfound);
286}
287
289{
290 QStringList res;
291
292 for (const auto & playlist : std::as_const(*m_allPlaylists))
293 {
294 res.append(playlist->getName());
295 }
296
297 return res;
298}
299
301{
303 {
304 return true;
305 }
307 return false;
308}
309
311{
313}
bool doneLoading() const
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int numRowsAffected() const
Definition: mythdbcon.h:217
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:178
bool isFinished(void) const
Definition: mthread.cpp:240
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:265
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:191
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:282
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
void describeYourself(void) const
Playlist * getPlaylist(int id)
PlaylistLoadingThread * m_playlistsLoader
void renamePlaylist(int index, const QString &new_name)
PlaylistContainer(AllMusic *all_music)
void FillIntelliWeights(int &rating, int &playcount, int &lastplay, int &random) const
void copyToActive(int index)
void createNewPlaylist(const QString &name)
QString getPlaylistName(int index, bool &reference)
bool nameIsUnique(const QString &a_name, int which_id)
void deletePlaylist(int kill_me)
Playlist * m_streamPlaylist
QList< Playlist * > * m_allPlaylists
void copyNewPlaylist(const QString &name)
Playlist * m_activePlaylist
QStringList getPlaylistNames(void)
PlaylistContainer * m_parent
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
int getID(void) const
Definition: playlist.h:111
void setName(const QString &a_name)
Definition: playlist.h:107
QString getName(void)
Definition: playlist.h:106
void copyTracks(Playlist *to_ptr, bool update_display)
Definition: playlist.cpp:43
void resync(void)
make sure all tracks are still valid after a scan
Definition: playlist.cpp:613
void describeYourself(void) const
Definition: playlist.cpp:481
void removeAllTracks(void)
Definition: playlist.cpp:89
void changed(void)
Definition: playlist.cpp:990
void setParent(PlaylistContainer *myparent)
Definition: playlist.h:53
void savePlaylist(const QString &a_name, const QString &a_host)
Definition: playlist.cpp:998
void loadPlaylist(const QString &a_name, const QString &a_host)
Definition: playlist.cpp:529
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def rating(profile, smoonURL, gate)
Definition: scan.py:36
#define LOC
static constexpr const char * DEFAULT_STREAMLIST_NAME
static constexpr const char * DEFAULT_PLAYLIST_NAME