MythTV master
playlistcontainer.cpp
Go to the documentation of this file.
1// C/C++
2#include <thread>
3#include <utility>
4
5// MythTV
10
11// mythmusic
12#include "playlist.h"
13#include "playlistcontainer.h"
14
16{
17 RunProlog();
18 while (!m_allMusic->doneLoading())
19 {
20 std::this_thread::sleep_for(250ms);
21 }
22 m_parent->load();
23 RunEpilog();
24}
25
26#define LOC QString("PlaylistContainer: ")
27#define LOC_WARN QString("PlaylistContainer, Warning: ")
28#define LOC_ERR QString("PlaylistContainer, Error: ")
29
31 m_playlistsLoader(new PlaylistLoadingThread(this, all_music)),
32 m_myHost(gCoreContext->GetHostName()),
33
34 m_ratingWeight( gCoreContext->GetNumSetting("IntelliRatingWeight", 2)),
35 m_playCountWeight(gCoreContext->GetNumSetting("IntelliPlayCountWeight", 2)),
36 m_lastPlayWeight( gCoreContext->GetNumSetting("IntelliLastPlayWeight", 2)),
37 m_randomWeight( gCoreContext->GetNumSetting("IntelliRandomWeight", 2))
38{
40}
41
43{
45 delete m_playlistsLoader;
46 m_playlistsLoader = nullptr;
47
48 delete m_activePlaylist;
49 delete m_streamPlaylist;
51 {
52 while (!m_allPlaylists->empty())
53 {
54 delete m_allPlaylists->front();
55 m_allPlaylists->pop_front();
56 }
57 delete m_allPlaylists;
58 }
59}
60
62 int &lastplay, int &random) const
63{
65 playcount = m_playCountWeight;
66 lastplay = m_lastPlayWeight;
67 random = m_randomWeight;
68}
69
71{
72 m_doneLoading = false;
75
78
79 m_allPlaylists = new QList<Playlist*>;
80
82
84
86 query.prepare("SELECT playlist_id FROM music_playlists "
87 "WHERE playlist_name != :DEFAULT"
88 " AND playlist_name != :BACKUP "
89 " AND playlist_name != :STREAM "
90 " AND (hostname = '' OR hostname = :HOST) "
91 "ORDER BY playlist_name;");
92 query.bindValue(":DEFAULT", DEFAULT_PLAYLIST_NAME);
93 query.bindValue(":BACKUP", "backup_playlist_storage");
94 query.bindValue(":STREAM", DEFAULT_STREAMLIST_NAME);
95 query.bindValue(":HOST", m_myHost);
96
97 if (!query.exec())
98 {
99 MythDB::DBError("Querying playlists", query);
100 }
101 else
102 {
103 while (query.next())
104 {
105 auto *temp_playlist = new Playlist();
106 // No, we don't destruct this ...
107 temp_playlist->setParent(this);
108 temp_playlist->loadPlaylistByID(query.value(0).toInt(), m_myHost);
109 m_allPlaylists->push_back(temp_playlist);
110 // ... cause it's sitting on this PtrList
111 }
112 }
113
114 m_doneLoading = true;
115}
116
117// resync all the playlists after a rescan just in case some tracks were removed
119{
120 // NOLINTNEXTLINE(modernize-loop-convert)
121 for (auto it = m_allPlaylists->begin(); it != m_allPlaylists->end(); ++it)
122 (*it)->resync();
123
125}
126
128{
129 // Debugging
131 for (const auto & playlist : std::as_const(*m_allPlaylists))
132 playlist->describeYourself();
133}
134
136{
137 // return a pointer to a playlist
138 // by id;
139
140 if (m_activePlaylist->getID() == id)
141 {
142 return m_activePlaylist;
143 }
144
145 auto idmatch = [id](const auto & playlist)
146 { return playlist->getID() == id; };
147 auto it = std::find_if(m_allPlaylists->cbegin(), m_allPlaylists->cend(), idmatch);
148 if (it != m_allPlaylists->cend())
149 return *it;
150
151 LOG(VB_GENERAL, LOG_ERR,
152 "getPlaylistName() called with unknown index number");
153 return nullptr;
154}
155
157{
158 // return a pointer to a playlist
159 // by name;
160 auto namematch = [name](const auto & playlist)
161 { return playlist->getName() == name; };
162 auto it = std::find_if(m_allPlaylists->cbegin(), m_allPlaylists->cend(), namematch);
163 if (it != m_allPlaylists->cend())
164 return *it;
165
166 LOG(VB_GENERAL, LOG_ERR, QString("getPlaylistName() called with unknown name: %1").arg(name));
167 return nullptr;
168}
169
171{
172 // NOLINTNEXTLINE(modernize-loop-convert)
173 for (auto it = m_allPlaylists->begin(); it != m_allPlaylists->end(); ++it)
174 {
175 if ((*it)->hasChanged())
176 (*it)->savePlaylist((*it)->getName(), m_myHost);
177 }
178
181}
182
184{
185 auto *new_list = new Playlist();
186 new_list->setParent(this);
187
188 // Need to touch the database to get persistent ID
189 new_list->savePlaylist(name, m_myHost);
190
191 m_allPlaylists->push_back(new_list);
192}
193
194void PlaylistContainer::copyNewPlaylist(const QString &name)
195{
196 auto *new_list = new Playlist();
197 new_list->setParent(this);
198
199 // Need to touch the database to get persistent ID
200 new_list->savePlaylist(name, m_myHost);
201
202 m_allPlaylists->push_back(new_list);
203 m_activePlaylist->copyTracks(new_list, false);
204}
205
207{
209 Playlist *copy_from = getPlaylist(index);
210 if (!copy_from)
211 {
212 LOG(VB_GENERAL, LOG_ERR, LOC + "copyToActive() " +
213 QString("Unknown playlist: %1").arg(index));
214 return;
215 }
216 copy_from->copyTracks(m_activePlaylist, true);
217}
218
219void PlaylistContainer::renamePlaylist(int index, const QString& new_name)
220{
221 Playlist *list_to_rename = getPlaylist(index);
222 if (list_to_rename)
223 {
224 list_to_rename->setName(new_name);
225 list_to_rename->changed();
226 }
227}
228
230{
231 Playlist *list_to_kill = getPlaylist(kill_me);
232 if (!list_to_kill)
233 {
234 LOG(VB_GENERAL, LOG_ERR, LOC + "deletePlaylist() " +
235 QString("Unknown playlist: %1").arg(kill_me));
236 return;
237 }
238
239 list_to_kill->removeAllTracks();
240 m_allPlaylists->removeAll(list_to_kill);
241
243 query.prepare("DELETE FROM music_playlists WHERE playlist_id = :ID ;");
244 query.bindValue(":ID", kill_me);
245
246 if (!query.exec() || query.numRowsAffected() < 1)
247 {
248 MythDB::DBError("playlist delete", query);
249 }
250}
251
252
253QString PlaylistContainer::getPlaylistName(int index, bool &reference)
254{
256 {
257 if (m_activePlaylist->getID() == index)
258 {
259 return m_activePlaylist->getName();
260 }
261
262 auto indexmatch = [index](const auto & playlist)
263 { return playlist->getID() == index; };
264 auto it = std::find_if(m_allPlaylists->cbegin(), m_allPlaylists->cend(), indexmatch);
265 if (it != m_allPlaylists->cend())
266 return (*it)->getName();
267 }
268
269 LOG(VB_GENERAL, LOG_ERR, LOC +
270 "getPlaylistName() called with unknown index number");
271
272 reference = true;
273 return tr("Something is Wrong");
274}
275
276bool PlaylistContainer::nameIsUnique(const QString& a_name, int which_id)
277{
278 if (a_name == DEFAULT_PLAYLIST_NAME)
279 return false;
280
281 auto itemfound = [a_name,which_id](const auto & playlist)
282 { return playlist->getName() == a_name &&
283 playlist->getID() != which_id; };
284 return std::none_of(m_allPlaylists->cbegin(), m_allPlaylists->cend(), itemfound);
285}
286
288{
289 QStringList res;
290
291 for (const auto & playlist : std::as_const(*m_allPlaylists))
292 {
293 res.append(playlist->getName());
294 }
295
296 return res;
297}
298
300{
302 {
303 return true;
304 }
306 return false;
307}
308
310{
312}
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:194
bool isFinished(void) const
Definition: mthread.cpp:256
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:281
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:207
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:298
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