# Accessing the media library The medialibrary is an optional component of the Qt interface, if you're developing a feature that depends on medialibrary component you should ensure that it won't break the application if you start the application with `--no-media-library` or if you compile VLC without medialibrary support. From the QML side you can check that `MainCtx.mediaLibraryAvailable` is true From C++ side you can check that `MainCtx::hasMediaLibrary()` returns true. ## Modeling medialibray views If you need to model a new view from the medialibrary, the usual way is to Create a class that inherit from MLBaseModel, this class is a [QAbstractListModel](https://doc.qt.io/qt-5/qabstractlistmodel.html) for the interaction on the QML side. Loading the data done by implementing a subclass `BaseLoader` which performs the raw queries on the medialibrary (counting elements, loading a range of elements, loading a single element). These functions are called from a background thread you should not access the model or the GUI from there. From the qml side, the different properties of the items are access using a role name, the data associated to a role is access thought the `itemRoleData`, and a mapping between roles ID and their name (string) through the `roleNames` function A model can react to events from the medialibrary by overriding the `onVlcMlEvent` function, this function is called from the Qt thread, so it is safe to acts on the model/UI from there a simple model might be ```cpp class MLStuff : public MLItem { //store item data here }; class MLStuffModel : public MLBaseModel { Q_OBJECT public: enum Roles { STUFF_ID = Qt::UserRole + 1, STUFF_ROLE_FOO, STUFF_ROLE_BAR }; Q_ENUM(Roles); //map Roles to their string representation QHash roleNames() const override; protected: //get data for a given Role on the object item QVariant itemRoleData(MLItem *item, int role) const override; //create an Loader instance to load the data std::unique_ptr createLoader() const override; private: // allows to sort using a given role vlc_ml_sorting_criteria_t roleToCriteria(int role) const override; // react to events from the medialibrary virtual void onVlcMlEvent( const MLEvent &event ) override; struct Loader : public BaseLoader { Loader(const MLStuffModel &model) : BaseLoader(model) {} // count the number of items size_t count(vlc_medialibrary_t* ml) const override; // load a range of items std::vector> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override; // load a single item std::unique_ptr loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override; }; }; ``` the model should be registered in the QML engine using `qmlRegisterType` in `MainUI.cpp` ```cpp qmlRegisterType( uri, versionMajor, versionMinor, "MLStuffModel" ); ``` On the QML side a model is instantiated locally and is given the medialibrary as ```qml import org.videolan.medialib 0.1 MLStuffModel { id: stuffModel ctx: MediaLib } ``` ## Querying the medialibrary Queries should **not** be made directly on the Qt thread and you should **not** block the Qt thread while the medialibrary is performing a query. If you need to query on the medialibrary, you need to use the `runOnMLThread` endpoint on the `MediaLib` instance. These queries will be executed asynchronous, and the result will be provided to you through a callback. A sample call: ```cpp //this structure is used to exchange data between struct Ctx { bool success; }; uint64_t taskid = m_mediaLib->runOnMLThread( //reference on the QObject making the call this, //this callback is executed in the medialibray thread [url, indexed](vlc_medialibrary_t* ml, Ctx& ctx) { //making whatever calls on the medialibrary int res; if ( indexed ) res = vlc_ml_add_folder( ml, qtu( url ) ); else res = vlc_ml_remove_folder( ml, qtu( url ) ); ctx.success = (res == VLC_SUCCESS); }, //callback executed on the GUI thread, we waranty that //"this" still exists [this, indexed](quint64 taskid, Ctx& ctx){ if (ctx.success) { m_indexed = indexed; emit isIndexedChanged(); } }, //Optionnal named queue if queries needs to be executed sequentially "ML_FOLDER_ADD_QUEUE"); ``` * The first callback will be called in a background thread, you can perform queries on the medialibrary from there, the second callback will be called on the GUI thread. * The ML or UI callback may not be called if the caller is destroyed before the callback is executed. Note that it may be destroyed during the execution of the ML callback, so you should **not** try to access the caller from this callback, the UI callback warranties that the caller still exists on the main thread. * A structure type can be specified to pass data between the two callbacks. Data put inside the structure should adhere to the RAII principle, the structure will be freed by broker, you have no warranty that the UI callback will actually be called. * If you need to pass data from the call site, you should pass it though lambda captures. Beware that the object making the call may be destroyed during the execution of the ML callback, so be wary of the life cycle of what you give (usually pass arguments by copy, move or using shared pointers)