1 /*****************************************************************************
2 * Copyright (C) 2019 VLC authors and VideoLAN
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17 *****************************************************************************/
19 #include "networkmediamodel.hpp"
21 #include "components/mediacenter/mlhelper.hpp"
23 #include "playlist/media.hpp"
24 #include "playlist/playlist_controller.hpp"
29 NETWORK_NAME
= Qt::UserRole
+ 1,
41 NetworkMediaModel::NetworkMediaModel( QObject
* parent
)
42 : QAbstractListModel( parent
)
47 QVariant
NetworkMediaModel::data( const QModelIndex
& index
, int role
) const
51 auto idx
= index
.row();
52 if ( idx
< 0 || (size_t)idx
>= m_items
.size() )
54 const auto& item
= m_items
[idx
];
63 case NETWORK_CANINDEX
:
64 return item
.canBeIndexed
;
67 case NETWORK_PROTOCOL
:
70 return QVariant::fromValue( item
.tree
);
72 return item
.mediaSource
->description
;
78 QHash
<int, QByteArray
> NetworkMediaModel::roleNames() const
81 { NETWORK_NAME
, "name" },
82 { NETWORK_MRL
, "mrl" },
83 { NETWORK_INDEXED
, "indexed" },
84 { NETWORK_CANINDEX
, "can_index" },
85 { NETWORK_TYPE
, "type" },
86 { NETWORK_PROTOCOL
, "protocol" },
87 { NETWORK_TREE
, "tree" },
88 { NETWORK_SOURCE
, "source" },
92 int NetworkMediaModel::rowCount(const QModelIndex
& parent
) const
94 if ( parent
.isValid() )
96 assert( m_items
.size() < INT32_MAX
);
97 return static_cast<int>( m_items
.size() );
100 Qt::ItemFlags
NetworkMediaModel::flags( const QModelIndex
& idx
) const
102 return QAbstractListModel::flags( idx
) | Qt::ItemIsEditable
;
105 bool NetworkMediaModel::setData( const QModelIndex
& idx
, const QVariant
& value
, int role
)
107 printf("NetworkMediaModel::setData `\n");
111 if ( role
!= NETWORK_INDEXED
)
113 auto enabled
= value
.toBool();
114 if ( m_items
[idx
.row()].indexed
== enabled
)
116 printf("NetworkMediaModel::setData change indexed`\n");
119 res
= vlc_ml_add_folder( m_ml
, qtu( m_items
[idx
.row()].mainMrl
.toString( QUrl::None
) ) );
121 res
= vlc_ml_remove_folder( m_ml
, qtu( m_items
[idx
.row()].mainMrl
.toString( QUrl::None
) ) );
122 m_items
[idx
.row()].indexed
= enabled
;
123 emit
dataChanged(idx
, idx
, { NETWORK_INDEXED
});
124 return res
== VLC_SUCCESS
;
128 void NetworkMediaModel::setIndexed(bool indexed
)
130 if (indexed
== m_indexed
|| !m_canBeIndexed
)
134 res
= vlc_ml_add_folder( m_ml
, qtu( m_url
.toString( QUrl::None
) ) );
136 res
= vlc_ml_remove_folder( m_ml
, qtu( m_url
.toString( QUrl::None
) ) );
138 if (res
== VLC_SUCCESS
) {
140 emit
isIndexedChanged();
144 void NetworkMediaModel::setCtx(QmlMainContext
* ctx
)
148 m_ml
= vlc_ml_instance_get( m_ctx
->getIntf() );
150 if (m_ctx
&& m_hasTree
) {
151 initializeMediaSources();
156 void NetworkMediaModel::setTree(QVariant parentTree
)
158 if (parentTree
.canConvert
<NetworkTreeItem
>())
159 m_treeItem
= parentTree
.value
<NetworkTreeItem
>();
161 m_treeItem
= NetworkTreeItem();
163 if (m_ctx
&& m_hasTree
) {
164 initializeMediaSources();
169 bool NetworkMediaModel::addToPlaylist(int index
)
171 if (!(m_ctx
&& m_hasTree
))
173 if (index
< 0 || (size_t)index
>= m_items
.size() )
175 auto item
= m_items
[index
];
176 vlc::playlist::Media media
{ item
.tree
.media
.get() };
177 m_ctx
->getIntf()->p_sys
->p_mainPlaylistController
->append( { media
}, false);
181 bool NetworkMediaModel::addToPlaylist(const QVariantList
&itemIdList
)
184 for (const QVariant
& varValue
: itemIdList
)
186 if (varValue
.canConvert
<int>())
188 auto index
= varValue
.value
<int>();
189 ret
|= addToPlaylist(index
);
195 bool NetworkMediaModel::addAndPlay(int index
)
197 if (!(m_ctx
&& m_hasTree
))
199 if (index
< 0 || (size_t)index
>= m_items
.size() )
201 auto item
= m_items
[index
];
202 vlc::playlist::Media media
{ item
.tree
.media
.get() };
203 m_ctx
->getIntf()->p_sys
->p_mainPlaylistController
->append( { media
}, true);
207 bool NetworkMediaModel::addAndPlay(const QVariantList
& itemIdList
)
210 for (const QVariant
& varValue
: itemIdList
)
212 if (varValue
.canConvert
<int>())
214 auto index
= varValue
.value
<int>();
216 ret
|= addAndPlay(index
);
218 ret
|= addToPlaylist(index
);
225 bool NetworkMediaModel::initializeMediaSources()
227 auto libvlc
= vlc_object_instance(m_ctx
->getIntf());
230 if (!m_items
.empty()) {
239 auto tree
= m_treeItem
.source
->tree
;
240 std::unique_ptr
<NetworkSourceListener
> l
{ new NetworkSourceListener( m_treeItem
.source
, this ) };
241 if ( l
->listener
== nullptr )
244 if (m_treeItem
.media
)
246 m_name
= m_treeItem
.media
->psz_name
;
248 m_url
= QUrl::fromEncoded( QByteArray
{ m_treeItem
.media
->psz_uri
}.append( '/' ) );
250 m_type
= static_cast<ItemType
>(m_treeItem
.media
->i_type
);
252 m_canBeIndexed
= canBeIndexed( m_url
, m_type
);
253 emit
canBeIndexedChanged();
254 if ( vlc_ml_is_indexed( m_ml
, QByteArray(m_treeItem
.media
->psz_uri
).append('/').constData(), &m_indexed
) != VLC_SUCCESS
) {
257 emit
isIndexedChanged();
261 vlc_media_tree_Preparse( tree
, libvlc
, m_treeItem
.media
.get() );
262 m_parsingPending
= true;
263 emit
parsingPendingChanged(m_parsingPending
);
265 m_listener
= std::move( l
);
270 void NetworkMediaModel::onItemCleared( MediaSourcePtr mediaSource
, input_item_node_t
*)
272 input_item_node_t
*res
;
273 input_item_node_t
*parent
;
274 if ( vlc_media_tree_Find( m_treeItem
.source
->tree
, m_treeItem
.media
.get(),
275 &res
, &parent
) == false )
277 refreshMediaList( std::move( mediaSource
), res
->pp_children
, res
->i_children
, true );
280 void NetworkMediaModel::onItemAdded( MediaSourcePtr mediaSource
, input_item_node_t
* parent
,
281 input_item_node_t
*const children
[],
284 if ( parent
->p_item
== m_treeItem
.media
.get() )
285 refreshMediaList( std::move( mediaSource
), children
, count
, false );
288 void NetworkMediaModel::onItemRemoved( MediaSourcePtr
,
289 input_item_node_t
*const children
[],
292 for ( auto i
= 0u; i
< count
; ++i
)
294 input_item_t
* p_item
= children
[i
]->p_item
;
295 input_item_Hold( p_item
);
296 QMetaObject::invokeMethod(this, [this, p_item
]() {
297 QUrl itemUri
= QUrl::fromEncoded(p_item
->psz_uri
);
298 auto it
= std::find_if( begin( m_items
), end( m_items
), [p_item
, itemUri
](const Item
& i
) {
299 return QString::compare( qfu(p_item
->psz_name
), i
.name
, Qt::CaseInsensitive
) == 0 &&
300 itemUri
.scheme() == i
.mainMrl
.scheme();
302 if ( it
== end( m_items
) )
304 input_item_Release( p_item
);
307 auto mrlIt
= std::find_if( begin( (*it
).mrls
), end( (*it
).mrls
),
308 [itemUri
]( const QUrl
& mrl
) {
309 return mrl
== itemUri
;
311 input_item_Release( p_item
);
312 if ( mrlIt
== end( (*it
).mrls
) )
314 (*it
).mrls
.erase( mrlIt
);
315 if ( (*it
).mrls
.empty() == false )
317 auto idx
= std::distance( begin( m_items
), it
);
318 beginRemoveRows({}, idx
, idx
);
325 void NetworkMediaModel::onItemPreparseEnded(MediaSourcePtr
, input_item_node_t
*, enum input_item_preparse_status
)
327 QMetaObject::invokeMethod(this, [this]() {
328 m_parsingPending
= false;
329 emit
parsingPendingChanged(false);
333 void NetworkMediaModel::refreshMediaList( MediaSourcePtr mediaSource
,
334 input_item_node_t
* const children
[], size_t count
,
337 auto items
= new std::vector
<Item
>;
338 for ( auto i
= 0u; i
< count
; ++i
)
340 auto it
= children
[i
]->p_item
;
342 item
.name
= it
->psz_name
;
344 item
.indexed
= false;
345 item
.type
= static_cast<ItemType
>(it
->i_type
);
346 item
.mainMrl
= (item
.type
== TYPE_DIRECTORY
|| item
.type
== TYPE_NODE
) ?
347 QUrl::fromEncoded(QByteArray(it
->psz_uri
).append('/')) :
348 QUrl::fromEncoded(it
->psz_uri
);
350 item
.canBeIndexed
= canBeIndexed( item
.mainMrl
, item
.type
);
351 item
.mediaSource
= mediaSource
;
353 if ( item
.canBeIndexed
== true )
355 if ( vlc_ml_is_indexed( m_ml
, qtu( item
.mainMrl
.toString( QUrl::None
) ),
356 &item
.indexed
) != VLC_SUCCESS
)
357 item
.indexed
= false;
359 item
.tree
= NetworkTreeItem( mediaSource
, it
);
360 items
->push_back( std::move( item
) );
362 QMetaObject::invokeMethod(this, [this, clear
, items
]() {
363 std::unique_ptr
<std::vector
<Item
>> itemsPtr
{ items
};
367 m_items
.erase( begin( m_items
) , end( m_items
) );
370 beginInsertRows( {}, m_items
.size(), m_items
.size() + items
->size() - 1 );
371 std::move( begin( *items
), end( *items
), std::back_inserter( m_items
) );
379 bool NetworkMediaModel::canBeIndexed(const QUrl
& url
, ItemType itemType
)
381 return static_cast<input_item_type_e
>(itemType
) != ITEM_TYPE_FILE
&& (url
.scheme() == "smb" || url
.scheme() == "ftp");