1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice,
11 // this list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the author nor the names of other contributors may
18 // be used to endorse or promote products derived from this software
19 // without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34
35 #include "pch.hpp"
36 #include "LocalMetadataProxy.h"
37
38 #include <musikcore/debug.h>
39 #include <musikcore/db/ScopedTransaction.h>
40 #include <musikcore/library/query/AlbumListQuery.h>
41 #include <musikcore/library/query/AllCategoriesQuery.h>
42 #include <musikcore/library/query/AppendPlaylistQuery.h>
43 #include <musikcore/library/query/CategoryListQuery.h>
44 #include <musikcore/library/query/CategoryTrackListQuery.h>
45 #include <musikcore/library/query/DeletePlaylistQuery.h>
46 #include <musikcore/library/query/SearchTrackListQuery.h>
47 #include <musikcore/library/query/GetPlaylistQuery.h>
48 #include <musikcore/library/query/SavePlaylistQuery.h>
49 #include <musikcore/library/query/TrackMetadataQuery.h>
50 #include <musikcore/library/query/TrackListQueryBase.h>
51 #include <musikcore/library/QueryRegistry.h>
52 #include <musikcore/library/LibraryFactory.h>
53 #include <musikcore/library/track/LibraryTrack.h>
54 #include <musikcore/library/LocalLibraryConstants.h>
55 #include <musikcore/runtime/Message.h>
56 #include <musikcore/support/Messages.h>
57 #include <musikcore/support/Common.h>
58 #include <vector>
59 #include <map>
60
61 #pragma warning(push, 0)
62 #include <nlohmann/json.hpp>
63 #pragma warning(pop)
64
65 #define TAG "LocalMetadataProxy"
66
67 using namespace musik::core;
68 using namespace musik::core::db;
69 using namespace musik::core::library::query;
70 using namespace musik::core::library;
71 using namespace musik::core::runtime;
72 using namespace musik::core::sdk;
73
74 using PredicateList = musik::core::library::query::category::PredicateList;
75
76 /* HELPERS */
77
78 #ifdef __APPLE__
79 static __thread char threadLocalBuffer[4096];
80 #else
81 static thread_local char threadLocalBuffer[4096];
82 #endif
83
getValue(IValue * value)84 static inline std::string getValue(IValue* value) {
85 threadLocalBuffer[0] = 0;
86 if (value->GetValue(threadLocalBuffer, sizeof(threadLocalBuffer))) {
87 return std::string(threadLocalBuffer);
88 }
89 return "";
90 }
91
toPredicateList(IValue ** predicates,size_t count)92 static inline PredicateList toPredicateList(IValue** predicates, size_t count) {
93 PredicateList predicateList;
94 if (predicates && count) {
95 for (size_t i = 0; i < count; i++) {
96 auto predicate = predicates[i];
97 if (predicate) {
98 predicateList.push_back({ getValue(predicate), predicate->GetId() });
99 }
100 }
101 }
102 return predicateList;
103 }
104
105 /* QUERIES */
106
107 class ExternalIdListToTrackListQuery : public TrackListQueryBase {
108 public:
ExternalIdListToTrackListQuery(ILibraryPtr library,const char ** externalIds,size_t externalIdCount)109 ExternalIdListToTrackListQuery(
110 ILibraryPtr library,
111 const char** externalIds,
112 size_t externalIdCount)
113 {
114 this->library = library;
115 this->externalIds = externalIds;
116 this->externalIdCount = externalIdCount;
117 }
118
GetResult()119 std::shared_ptr<TrackList> GetResult() noexcept override {
120 return this->result;
121 }
122
GetHeaders()123 Headers GetHeaders() noexcept override {
124 return Headers();
125 }
126
GetDurations()127 Durations GetDurations() noexcept override {
128 return Durations();
129 }
130
GetQueryHash()131 size_t GetQueryHash() noexcept override {
132 return 0;
133 }
134
135 protected:
OnRun(musik::core::db::Connection & db)136 bool OnRun(musik::core::db::Connection& db) override {
137 std::string sql = "SELECT id, external_id FROM tracks WHERE external_id IN(";
138 for (size_t i = 0; i < externalIdCount; i++) {
139 sql += (i == 0) ? "?" : ",?";
140 }
141 sql += ");";
142
143 Statement query(sql.c_str(), db);
144
145 for (size_t i = 0; i < externalIdCount; i++) {
146 query.BindText((int) i, externalIds[i]);
147 }
148
149 /* gotta eat up some memory to preserve the input order. map the
150 external id to the id so we can ensure we return the list in the
151 same order it was requested. this is faster than executing one
152 query per ID (we do this because WHERE IN() does not preserve input
153 ordering... */
154 struct Record { int64_t id; std::string externalId; };
155 std::map<std::string, int64_t> records;
156
157 while (query.Step() == Row) {
158 records[query.ColumnText(1)] = query.ColumnInt64(0);
159 }
160
161 /* order the output here... */
162 this->result = std::make_shared<TrackList>(this->library);
163 auto end = records.end();
164 for (size_t i = 0; i < externalIdCount; i++) {
165 auto r = records.find(externalIds[i]);
166 if (r != end) {
167 this->result->Add(r->second);
168 }
169 }
170
171 return true;
172 }
173
Name()174 std::string Name() override {
175 return "ExternalIdListToTrackListQuery";
176 }
177
178 private:
179 ILibraryPtr library;
180 const char** externalIds;
181 size_t externalIdCount;
182 std::shared_ptr<TrackList> result;
183 };
184
185 class RemoveFromPlaylistQuery : public QueryBase {
186 public:
RemoveFromPlaylistQuery(ILibraryPtr library,int64_t playlistId,const char ** externalIds,const int * sortOrders,size_t count)187 RemoveFromPlaylistQuery(
188 ILibraryPtr library,
189 int64_t playlistId,
190 const char** externalIds,
191 const int* sortOrders,
192 size_t count)
193 {
194 this->library = library;
195 this->playlistId = playlistId;
196 this->externalIds = externalIds;
197 this->sortOrders = sortOrders;
198 this->count = count;
199 this->updated = 0;
200 }
201
GetResult()202 size_t GetResult() noexcept {
203 return this->updated;
204 }
205
206 protected:
OnRun(musik::core::db::Connection & db)207 bool OnRun(musik::core::db::Connection& db) override {
208 this->updated = 0;
209
210 ScopedTransaction transaction(db);
211
212 {
213 Statement deleteStmt(
214 "DELETE FROM playlist_tracks "
215 "WHERE playlist_id=? AND track_external_id=? AND sort_order=?",
216 db);
217
218 for (size_t i = 0; i < count; i++) {
219 const auto id = this->externalIds[i];
220 const auto o = this->sortOrders[i];
221
222 deleteStmt.ResetAndUnbind();
223 deleteStmt.BindInt64(0, this->playlistId);
224 deleteStmt.BindText(1, this->externalIds[i]);
225 deleteStmt.BindInt32(2, this->sortOrders[i]);
226 if (deleteStmt.Step() == Done) {
227 ++this->updated;
228 }
229 }
230 }
231
232 bool error = false;
233
234 {
235 Statement playlistTracks(
236 "SELECT track_external_id, sort_order FROM playlist_tracks "
237 "WHERE playlist_id=? ORDER BY sort_order ASC",
238 db);
239
240 Statement updateStmt(
241 "UPDATE playlist_tracks "
242 "SET sort_order=? "
243 "WHERE playlist_id=? AND track_external_id=? AND sort_order=?",
244 db);
245
246 int order = 0;
247
248 playlistTracks.BindInt64(0, this->playlistId);
249 while (playlistTracks.Step() == Row) {
250 updateStmt.ResetAndUnbind();
251 updateStmt.BindInt32(0, order++);
252 updateStmt.BindInt64(1, this->playlistId);
253 updateStmt.BindText(2, playlistTracks.ColumnText(0));
254 updateStmt.BindInt32(3, playlistTracks.ColumnInt32(1));
255 if (updateStmt.Step() != Done) {
256 error = true;
257 break;
258 }
259 }
260 }
261
262 if (!error) {
263 transaction.CommitAndRestart();
264 }
265 else {
266 this->updated = 0;
267 }
268
269 if (this->updated > 0) {
270 this->library->GetMessageQueue().Broadcast(
271 Message::Create(nullptr, message::PlaylistModified, playlistId));
272 }
273
274 return true;
275 }
276
Name()277 std::string Name() override {
278 return "RemoveFromPlaylistQuery";
279 }
280
281 private:
282 ILibraryPtr library;
283 int64_t playlistId;
284 const char** externalIds;
285 const int* sortOrders;
286 size_t count;
287 size_t updated;
288 std::shared_ptr<TrackList> result;
289 };
290
291 /* DATA PROVIDER */
292
LocalMetadataProxy(musik::core::ILibraryPtr library)293 LocalMetadataProxy::LocalMetadataProxy(musik::core::ILibraryPtr library)
294 : library(library) {
295
296 }
297
Release()298 void LocalMetadataProxy::Release() noexcept {
299 delete this;
300 }
301
QueryTracks(const char * query,int limit,int offset)302 ITrackList* LocalMetadataProxy::QueryTracks(const char* query, int limit, int offset) {
303 try {
304 auto search = std::make_shared<SearchTrackListQuery>(
305 this->library,
306 SearchTrackListQuery::MatchType::Substring,
307 std::string(query ? query : ""),
308 TrackSortType::Album);
309
310 if (limit >= 0) {
311 search->SetLimitAndOffset(limit, offset);
312 }
313
314 this->library->EnqueueAndWait(search);
315
316 if (search->GetStatus() == IQuery::Finished) {
317 return search->GetSdkResult();
318 }
319 }
320 catch (...) {
321 musik::debug::error(TAG, "QueryTracks failed");
322 }
323
324 return nullptr;
325 }
326
QueryTrackById(int64_t trackId)327 ITrack* LocalMetadataProxy::QueryTrackById(int64_t trackId) {
328 try {
329 const auto target = std::make_shared<LibraryTrack>(trackId, this->library);
330 const auto search = std::make_shared<TrackMetadataQuery>(target, this->library);
331 this->library->EnqueueAndWait(search);
332 if (search->GetStatus() == IQuery::Finished) {
333 return search->Result()->GetSdkValue();
334 }
335 }
336 catch (...) {
337 musik::debug::error(TAG, "QueryTrackById failed");
338 }
339
340 return nullptr;
341 }
342
QueryTrackByExternalId(const char * externalId)343 ITrack* LocalMetadataProxy::QueryTrackByExternalId(const char* externalId) {
344 if (strlen(externalId)) {
345 try {
346 auto target = std::make_shared<LibraryTrack>(0, this->library);
347 target->SetValue("external_id", externalId);
348 auto search = std::make_shared<TrackMetadataQuery>(target, this->library);
349 this->library->EnqueueAndWait(search);
350 if (search->GetStatus() == IQuery::Finished) {
351 return search->Result()->GetSdkValue();
352 }
353 }
354 catch (...) {
355 musik::debug::error(TAG, "QueryTrackByExternalId failed");
356 }
357 }
358
359 return nullptr;
360 }
361
QueryTracksByCategory(const char * categoryType,int64_t selectedId,const char * filter,int limit,int offset)362 ITrackList* LocalMetadataProxy::QueryTracksByCategory(
363 const char* categoryType, int64_t selectedId, const char* filter, int limit, int offset)
364 {
365 try {
366 std::shared_ptr<TrackListQueryBase> search;
367
368 if (std::string(categoryType) == constants::Playlists::TABLE_NAME) {
369 search = std::make_shared<GetPlaylistQuery>(this->library, selectedId);
370 }
371 else {
372 if (categoryType && strlen(categoryType) && selectedId > 0) {
373 search = std::make_shared<CategoryTrackListQuery>(
374 this->library, categoryType, selectedId, filter);
375 }
376 else {
377 search = std::make_shared<CategoryTrackListQuery>(this->library, filter);
378 }
379 }
380
381 if (limit >= 0) {
382 search->SetLimitAndOffset(limit, offset);
383 }
384
385 this->library->EnqueueAndWait(search);
386
387 if (search->GetStatus() == IQuery::Finished) {
388 return search->GetSdkResult();
389 }
390 }
391 catch (...) {
392 musik::debug::error(TAG, "QueryTracksByCategory failed");
393 }
394
395 return nullptr;
396 }
397
QueryTracksByCategories(IValue ** categories,size_t categoryCount,const char * filter,int limit,int offset)398 ITrackList* LocalMetadataProxy::QueryTracksByCategories(
399 IValue** categories, size_t categoryCount, const char* filter, int limit, int offset)
400 {
401 try {
402 PredicateList list = toPredicateList(categories, categoryCount);
403
404 auto query = std::make_shared<CategoryTrackListQuery>(this->library, list, filter);
405
406 if (limit >= 0) {
407 query->SetLimitAndOffset(limit, offset);
408 }
409
410 this->library->EnqueueAndWait(query);
411
412 if (query->GetStatus() == IQuery::Finished) {
413 return query->GetSdkResult();
414 }
415 }
416 catch (...) {
417 musik::debug::error(TAG, "QueryTracksByCategory failed");
418 }
419
420 return nullptr;
421 }
422
QueryCategory(const char * type,const char * filter)423 IValueList* LocalMetadataProxy::QueryCategory(const char* type, const char* filter) {
424 return QueryCategoryWithPredicate(type, "", -1LL, filter);
425 }
426
ListCategories()427 IValueList* LocalMetadataProxy::ListCategories() {
428 try {
429 auto query = std::make_shared<AllCategoriesQuery>();
430 this->library->EnqueueAndWait(query);
431
432 if (query->GetStatus() == IQuery::Finished) {
433 return query->GetSdkResult();
434 }
435 }
436 catch (...) {
437 musik::debug::error(TAG, "ListCategories failed");
438 }
439
440 return nullptr;
441 }
442
443
QueryCategoryWithPredicate(const char * type,const char * predicateType,int64_t predicateId,const char * filter)444 IValueList* LocalMetadataProxy::QueryCategoryWithPredicate(
445 const char* type, const char* predicateType, int64_t predicateId, const char* filter)
446 {
447 try {
448 const std::string field = predicateType ? predicateType : "";
449 const category::PredicateList predicates = { { field, predicateId } };
450
451 auto search = std::make_shared<CategoryListQuery>(
452 CategoryListQuery::MatchType::Substring,
453 type,
454 predicates,
455 std::string(filter ? filter : ""));
456
457 this->library->EnqueueAndWait(search);
458
459 if (search->GetStatus() == IQuery::Finished) {
460 return search->GetSdkResult();
461 }
462 }
463 catch (...) {
464 musik::debug::error(TAG, "QueryCategory failed");
465 }
466
467 return nullptr;
468 }
469
QueryCategoryWithPredicates(const char * type,IValue ** predicates,size_t predicateCount,const char * filter)470 IValueList* LocalMetadataProxy::QueryCategoryWithPredicates(
471 const char* type, IValue** predicates, size_t predicateCount, const char* filter)
472 {
473 try {
474 auto predicateList = toPredicateList(predicates, predicateCount);
475
476 auto query = std::make_shared<CategoryListQuery>(
477 CategoryListQuery::MatchType::Substring,
478 type,
479 predicateList,
480 std::string(filter ? filter : ""));
481
482 this->library->EnqueueAndWait(query);
483
484 if (query->GetStatus() == IQuery::Finished) {
485 return query->GetSdkResult();
486 }
487 }
488 catch (...) {
489 musik::debug::error(TAG, "QueryCategory failed");
490 }
491
492 return nullptr;
493 }
494
QueryAlbums(const char * categoryIdName,int64_t categoryIdValue,const char * filter)495 IMapList* LocalMetadataProxy::QueryAlbums(
496 const char* categoryIdName, int64_t categoryIdValue, const char* filter)
497 {
498 try {
499 auto search = std::make_shared<AlbumListQuery>(
500 std::string(categoryIdName ? categoryIdName : ""),
501 categoryIdValue,
502 std::string(filter ? filter : ""));
503
504 this->library->EnqueueAndWait(search);
505
506 if (search->GetStatus() == IQuery::Finished) {
507 return search->GetSdkResult();
508 }
509 }
510 catch (...) {
511 musik::debug::error(TAG, "QueryAlbums failed");
512 }
513
514 return nullptr;
515 }
516
QueryAlbums(const char * filter)517 IMapList* LocalMetadataProxy::QueryAlbums(const char* filter) {
518 return this->QueryAlbums(nullptr, -1, filter);
519 }
520
521 template <typename TrackListType>
savePlaylist(ILibraryPtr library,TrackListType trackList,const char * playlistName,const int64_t playlistId)522 static uint64_t savePlaylist(
523 ILibraryPtr library,
524 TrackListType trackList,
525 const char* playlistName,
526 const int64_t playlistId)
527 {
528 try {
529 /* replacing (and optionally renaming) an existing playlist */
530 if (playlistId != 0) {
531 std::shared_ptr<SavePlaylistQuery> query =
532 SavePlaylistQuery::Replace(library, playlistId, trackList);
533
534 library->EnqueueAndWait(query);
535
536 if (query->GetStatus() == IQuery::Finished) {
537 if (strlen(playlistName)) {
538 query = SavePlaylistQuery::Rename(library, playlistId, playlistName);
539
540 library->EnqueueAndWait(query);
541
542 if (query->GetStatus() == IQuery::Finished) {
543 return playlistId;
544 }
545 }
546 else {
547 return playlistId;
548 }
549 }
550 }
551 else {
552 std::shared_ptr<SavePlaylistQuery> query =
553 SavePlaylistQuery::Save(library, playlistName, trackList);
554
555 library->EnqueueAndWait(query);
556
557 if (query->GetStatus() == IQuery::Finished) {
558 return query->GetPlaylistId();
559 }
560 }
561 }
562 catch (...) {
563 musik::debug::error(TAG, "SavePlaylist failed");
564 }
565
566 return 0;
567 }
568
SavePlaylistWithIds(int64_t * trackIds,size_t trackIdCount,const char * playlistName,const int64_t playlistId)569 int64_t LocalMetadataProxy::SavePlaylistWithIds(
570 int64_t* trackIds,
571 size_t trackIdCount,
572 const char* playlistName,
573 const int64_t playlistId)
574 {
575 if (playlistId == 0 && (!playlistName || !strlen(playlistName))) {
576 return 0;
577 }
578
579 std::shared_ptr<TrackList> trackList =
580 std::make_shared<TrackList>(this->library, trackIds, trackIdCount);
581
582 return savePlaylist(this->library, trackList, playlistName, playlistId);
583 }
584
SavePlaylistWithExternalIds(const char ** externalIds,size_t externalIdCount,const char * playlistName,const int64_t playlistId)585 int64_t LocalMetadataProxy::SavePlaylistWithExternalIds(
586 const char** externalIds,
587 size_t externalIdCount,
588 const char* playlistName,
589 const int64_t playlistId)
590 {
591 if (playlistId == 0 && (!playlistName || !strlen(playlistName))) {
592 return 0;
593 }
594
595 try {
596 using Query = ExternalIdListToTrackListQuery;
597
598 std::shared_ptr<Query> query =
599 std::make_shared<Query>(this->library, externalIds, externalIdCount);
600
601 library->EnqueueAndWait(query);
602
603 if (query->GetStatus() == IQuery::Finished) {
604 return savePlaylist(this->library, query->GetResult(), playlistName, playlistId);
605 }
606 }
607 catch (...) {
608 musik::debug::error(TAG, "SavePlaylistWithExternalIds failed");
609 }
610
611 return 0;
612 }
613
SavePlaylistWithTrackList(ITrackList * trackList,const char * playlistName,const int64_t playlistId)614 int64_t LocalMetadataProxy::SavePlaylistWithTrackList(
615 ITrackList* trackList,
616 const char* playlistName,
617 const int64_t playlistId)
618 {
619 if (playlistId == 0 && (!playlistName || !strlen(playlistName))) {
620 return 0;
621 }
622
623 return savePlaylist(this->library, trackList, playlistName, playlistId);
624 }
625
RenamePlaylist(const int64_t playlistId,const char * name)626 bool LocalMetadataProxy::RenamePlaylist(const int64_t playlistId, const char* name)
627 {
628 if (strlen(name)) {
629 try {
630 std::shared_ptr<SavePlaylistQuery> query =
631 SavePlaylistQuery::Rename(library, playlistId, name);
632
633 this->library->EnqueueAndWait(query);
634
635 if (query->GetStatus() == IQuery::Finished) {
636 return true;
637 }
638 }
639 catch (...) {
640 musik::debug::error(TAG, "RenamePlaylist failed");
641 }
642 }
643
644 return false;
645 }
646
DeletePlaylist(const int64_t playlistId)647 bool LocalMetadataProxy::DeletePlaylist(const int64_t playlistId) {
648 try {
649 std::shared_ptr<DeletePlaylistQuery> query =
650 std::make_shared<DeletePlaylistQuery>(library, playlistId);
651
652 this->library->EnqueueAndWait(query);
653
654 if (query->GetStatus() == IQuery::Finished) {
655 return true;
656 }
657 }
658 catch (...) {
659 musik::debug::error(TAG, "DeletePlaylist failed");
660 }
661
662 return false;
663 }
664
665 template <typename TrackListType>
appendToPlaylist(ILibraryPtr library,const int64_t playlistId,TrackListType trackList,int offset)666 static bool appendToPlaylist(
667 ILibraryPtr library,
668 const int64_t playlistId,
669 TrackListType trackList,
670 int offset)
671 {
672 try {
673 std::shared_ptr<AppendPlaylistQuery> query =
674 std::make_shared<AppendPlaylistQuery>(
675 library, playlistId, trackList, offset);
676
677 library->EnqueueAndWait(query);
678
679 if (query->GetStatus() == IQuery::Finished) {
680 return true;
681 }
682 }
683 catch (...) {
684 musik::debug::error(TAG, "AppendToPlaylist failed");
685 }
686
687 return false;
688 }
689
AppendToPlaylistWithIds(const int64_t playlistId,const int64_t * ids,size_t idCount,int offset)690 bool LocalMetadataProxy::AppendToPlaylistWithIds(
691 const int64_t playlistId,
692 const int64_t* ids,
693 size_t idCount,
694 int offset)
695 {
696 std::shared_ptr<TrackList> trackList =
697 std::make_shared<TrackList>(this->library, ids, idCount);
698
699 return appendToPlaylist(this->library, playlistId, trackList, offset);
700 }
701
AppendToPlaylistWithExternalIds(const int64_t playlistId,const char ** externalIds,size_t externalIdCount,int offset)702 bool LocalMetadataProxy::AppendToPlaylistWithExternalIds(
703 const int64_t playlistId,
704 const char** externalIds,
705 size_t externalIdCount,
706 int offset)
707 {
708 using Query = ExternalIdListToTrackListQuery;
709
710 try {
711 std::shared_ptr<Query> query =
712 std::make_shared<Query>(this->library, externalIds, externalIdCount);
713
714 library->EnqueueAndWait(query);
715
716 if (query->GetStatus() == IQuery::Finished) {
717 return appendToPlaylist(this->library, playlistId, query->GetResult(), offset);
718 }
719 }
720 catch (...) {
721 musik::debug::error(TAG, "AppendToPlaylistWithExternalIds failed");
722 }
723
724 return 0;
725
726 }
727
AppendToPlaylistWithTrackList(const int64_t playlistId,ITrackList * trackList,int offset)728 bool LocalMetadataProxy::AppendToPlaylistWithTrackList(
729 const int64_t playlistId, ITrackList* trackList, int offset)
730 {
731 return appendToPlaylist(this->library, playlistId, trackList, offset);
732 }
733
RemoveTracksFromPlaylist(const int64_t playlistId,const char ** externalIds,const int * sortOrders,int count)734 size_t LocalMetadataProxy::RemoveTracksFromPlaylist(
735 const int64_t playlistId,
736 const char** externalIds,
737 const int* sortOrders,
738 int count)
739 {
740 try {
741 auto query = std::make_shared<RemoveFromPlaylistQuery>(
742 this->library, playlistId, externalIds, sortOrders, count);
743
744 library->EnqueueAndWait(query);
745
746 if (query->GetStatus() == IQuery::Finished) {
747 return query->GetResult();
748 }
749 }
750 catch (...) {
751 musik::debug::error(TAG, "RemoveTracksFromPlaylist failed");
752 }
753
754 return 0;
755 }
756
QueryTracksByExternalId(const char ** externalIds,size_t externalIdCount)757 ITrackList* LocalMetadataProxy::QueryTracksByExternalId(
758 const char** externalIds, size_t externalIdCount)
759 {
760 try {
761 auto query = std::make_shared<ExternalIdListToTrackListQuery>(
762 this->library, externalIds, externalIdCount);
763
764 library->EnqueueAndWait(query);
765
766 if (query->GetStatus() == IQuery::Finished) {
767 return query->GetSdkResult();
768 }
769 }
770 catch (...) {
771 musik::debug::error(TAG, "QueryTracksByExternalId failed");
772 }
773
774 return nullptr;
775 }
776
SendRawQuery(const char * query,IAllocator & allocator,char ** resultData,int * resultSize)777 bool LocalMetadataProxy::SendRawQuery(
778 const char* query, IAllocator& allocator, char** resultData, int* resultSize)
779 {
780 if (!resultData || !resultSize) {
781 return false;
782 }
783
784 try {
785 nlohmann::json json = nlohmann::json::parse(query);
786 auto localLibrary = LibraryFactory::Instance().DefaultLocalLibrary();
787 std::string name = json["name"];
788 auto libraryQuery = QueryRegistry::CreateLocalQueryFor(name, query, localLibrary);
789 if (libraryQuery) {
790 localLibrary->EnqueueAndWait(libraryQuery);
791 if (libraryQuery->GetStatus() == IQuery::Finished) {
792 std::string result = libraryQuery->SerializeResult();
793 *resultData = static_cast<char*>(allocator.Allocate(result.size() + 1));
794 if (*resultData) {
795 *resultSize = (int) result.size() + 1;
796 strncpy(*resultData, result.c_str(), *resultSize);
797 return true;
798 }
799 else {
800 musik::debug::error(TAG, "SendRawQuery failed: memory allocation failed");
801 }
802 }
803 else {
804 musik::debug::error(TAG, "SendRawQuery failed: query returned failure");
805 }
806 }
807 else {
808 musik::debug::error(TAG, "SendRawQuery failed: could not find query in registry");
809 }
810 }
811 catch (...) {
812 musik::debug::error(TAG, "SendRawQuery failed: exception thrown");
813 }
814 return false;
815 }