Commit 79990b94 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

media library: good riddance

Big pile of non-sensical and unmaintained code.
parent 8e3288f9
...@@ -3967,7 +3967,6 @@ AC_ARG_ENABLE(media-library, ...@@ -3967,7 +3967,6 @@ AC_ARG_ENABLE(media-library,
AS_IF([test "${enable_media_library}" = "yes"], [ AS_IF([test "${enable_media_library}" = "yes"], [
AC_DEFINE([MEDIA_LIBRARY], 1, [Define if you want to use the VLC media library]) AC_DEFINE([MEDIA_LIBRARY], 1, [Define if you want to use the VLC media library])
VLC_ADD_CPPFLAGS([qt4],"-DMEDIA_LIBRARY") VLC_ADD_CPPFLAGS([qt4],"-DMEDIA_LIBRARY")
VLC_ADD_PLUGIN([media_library])
dnl dnl
dnl SQLite dnl SQLite
...@@ -4135,7 +4134,6 @@ AC_CONFIG_FILES([ ...@@ -4135,7 +4134,6 @@ AC_CONFIG_FILES([
modules/lua/Makefile modules/lua/Makefile
modules/meta_engine/Makefile modules/meta_engine/Makefile
modules/misc/Makefile modules/misc/Makefile
modules/media_library/Makefile
modules/mux/Makefile modules/mux/Makefile
modules/notify/Makefile modules/notify/Makefile
modules/packetizer/Makefile modules/packetizer/Makefile
......
...@@ -192,7 +192,6 @@ $Id$ ...@@ -192,7 +192,6 @@ $Id$
* magnify: zoom video filter * magnify: zoom video filter
* marq: Overlays a marquee on the video * marq: Overlays a marquee on the video
* mash: OpenMash based decoder * mash: OpenMash based decoder
* media_library: a sql based media library
* mediacodec: Android Jelly Bean MediaCodec decoder module * mediacodec: Android Jelly Bean MediaCodec decoder module
* mediadirs: Picture/Music/Video user directories as service discoveries * mediadirs: Picture/Music/Video user directories as service discoveries
* minimal_macosx: a minimal Mac OS X GUI, using the FrameWork * minimal_macosx: a minimal Mac OS X GUI, using the FrameWork
......
...@@ -10,7 +10,6 @@ BASE_SUBDIRS = \ ...@@ -10,7 +10,6 @@ BASE_SUBDIRS = \
gui \ gui \
meta_engine \ meta_engine \
misc \ misc \
media_library \
notify \ notify \
packetizer \ packetizer \
services_discovery \ services_discovery \
......
SOURCES_media_library = sql_media_library.c \
sql_media_library.h \
sql_monitor.c \
sql_search.c \
sql_add.c \
sql_update.c \
sql_delete.c \
item_list.c \
item_list.h \
ml_watch.c \
media_pool.c
/*****************************************************************************
* item_list.c: An input_item_t+media_id couple list for the media library
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN Team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
#include "item_list.h"
/**
* @short item hash list for the media library monitoring system
*/
/**
* @brief Add an item to the head of the list
* @param p_ml
* @param p_media media object. ID must be non zero and valid
* @param p_item input item to add, MUST NOT be NULL
* @param locked flag set if the list is locked. do not use
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int __item_list_add( watch_thread_t *p_wt, ml_media_t* p_media, input_item_t *p_item,
bool locked )
{
if( !locked )
vlc_mutex_lock( &p_wt->list_mutex );
ml_LockMedia( p_media );
assert( p_media->i_id );
/* Ensure duplication does not occur */
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item->i_id == p_item->i_id )
{
ml_UnlockMedia( p_media );
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_EGENERIC;
}
}
item_list_t *p_new = ( item_list_t* ) calloc( 1, sizeof( item_list_t ) );
if( !p_new )
{
ml_UnlockMedia( p_media );
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_ENOMEM;
}
p_new->p_next = p_wt->p_hlist[ item_hash( p_item ) ];
p_new->i_refs = 1;
p_new->i_media_id = p_media->i_id;
p_new->p_media = p_media;
p_new->p_item = p_item;
p_wt->p_hlist[ item_hash( p_item ) ] = p_new;
ml_UnlockMedia( p_media );
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_SUCCESS;
}
/**
* @brief Delete an item from the list
* @param p_ml this media library
* @param i_media_id media library's media ID
*/
item_list_t* item_list_delMedia( watch_thread_t *p_wt, int i_media_id )
{
vlc_mutex_lock( &p_wt->list_mutex );
item_list_t *p_prev = NULL;
il_foreachhashlist( p_wt->p_hlist, p_elt, ixx )
{
if( p_elt->i_media_id == i_media_id )
{
if( p_prev )
p_prev->p_next = p_elt->p_next;
else
p_wt->p_hlist[ixx] = p_elt->p_next;
p_elt->p_next = NULL;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt;
}
else
{
p_prev = p_elt;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Delete an item from the list and return the single element
* @param p_ml this media library
* @param p_item item to delete
* @return The element from the list containing p_item
*/
item_list_t* item_list_delItem( watch_thread_t *p_wt, input_item_t *p_item, bool locked )
{
if( !locked )
vlc_mutex_lock( &p_wt->list_mutex );
item_list_t *p_prev = NULL;
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
if( p_prev )
p_prev->p_next = p_elt->p_next;
else
p_wt->p_hlist[ item_hash( p_item ) ] = p_elt->p_next;
p_elt->p_next = NULL;
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt;
}
else
{
p_prev = p_elt;
}
}
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Find an input item
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return input item if found, NULL if not found
*/
input_item_t* item_list_itemOfMediaId( watch_thread_t *p_wt, int i_media_id )
{
item_list_t* p_tmp = item_list_listitemOfMediaId( p_wt, i_media_id );
if( p_tmp )
return p_tmp->p_item;
else
return NULL;
}
/**
* @brief Find an item list item
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return input item if found, NULL if not found
*/
item_list_t* item_list_listitemOfMediaId( watch_thread_t *p_wt, int i_media_id )
{
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachhashlist( p_wt->p_hlist, p_elt, ixx )
{
if( p_elt->i_media_id == i_media_id )
{
p_elt->i_age = 0;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Find a media
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return media if found. NULL otherwise
*/
ml_media_t* item_list_mediaOfMediaId( watch_thread_t *p_wt, int i_media_id )
{
item_list_t* p_tmp = item_list_listitemOfMediaId( p_wt, i_media_id );
if( p_tmp )
return p_tmp->p_media;
else
return NULL;
}
/**
* @brief Find a media ID by its input_item
* @param p_ml this media library
* @param p_item item to find
* @return media_id found, or VLC_EGENERIC
*/
int item_list_mediaIdOfItem( watch_thread_t *p_wt, input_item_t *p_item )
{
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
if( p_elt->i_media_id <= 0 )
/* TODO! */
p_elt->i_age = 0;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt->i_media_id;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_EGENERIC;
}
/**
* @brief Find a media by its input_item
* @param p_ml this media library
* @param p_item item to find
* @return media found, or VLC_EGENERIC
*/
ml_media_t* item_list_mediaOfItem( watch_thread_t *p_wt, input_item_t* p_item,
bool locked )
{
if( !locked )
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
p_wt->p_hlist[ item_hash( p_item ) ] = p_elt->p_next;
p_elt->p_next = NULL;
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt->p_media;
}
}
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Flag an item as updated
* @param p_ml this media library
* @param p_item item to find and flag
* @param b_played raise play count or not, update last play
* @return media_id found, or VLC_EGENERIC
*/
int item_list_updateInput( watch_thread_t *p_wt, input_item_t *p_item,
bool b_played )
{
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
/* Item found, flag and return */
p_elt->i_age = 0;
p_elt->i_update |= b_played ? 3 : 1;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt->i_media_id;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_EGENERIC;
}
/**
* @brief Free every item in the item list.
* @param p_wt Watch thread
* @note All decref of objects must be handled by watch system
*/
void item_list_destroy( watch_thread_t* p_wt )
{
vlc_mutex_lock( &p_wt->list_mutex );
for( int i = 0; i < ML_ITEMLIST_HASH_LENGTH ; i++ )
{
for( item_list_t* p_elt = p_wt->p_hlist[i] ; p_elt; p_elt = p_wt->p_hlist[i] )
{
p_wt->p_hlist[i] = p_elt->p_next;
free( p_elt );
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
}
/*****************************************************************************
* item_list.h : Item list data structure for Watching system
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef ML_ITEM_LIST_H
#define ML_ITEM_LIST_H
#include <vlc_input.h>
#include <vlc_arrays.h>
struct watch_thread_t;
typedef struct watch_thread_t watch_thread_t;
typedef struct item_list_t item_list_t;
/**
* Definition of item_list_t
*/
struct item_list_t {
input_item_t *p_item; /**< Input item */
ml_media_t *p_media; /**< Media item */
item_list_t *p_next; /**< Next element in the list */
int i_media_id; /**< Media id */
int i_age; /**< Time spent in this list without activity */
int i_refs; /**< Number of important refs */
int i_update; /**< Flag set when the input item is updated:
0: no update,
1: meta update,
2: increment play count,
3: both */
};
#define ML_ITEMLIST_HASH_LENGTH 40
#define il_foreachhashlist( a, b, c ) \
for( int c = 0 ; c < ML_ITEMLIST_HASH_LENGTH ; c++ ) \
for( item_list_t* b = a[c]; b; b = b->p_next )
#define il_foreachlist( a, b ) for( item_list_t* b = a ; b; b = b->p_next )
#define item_list_add( a, b, c ) __item_list_add( a, b, c, false )
int __item_list_add( watch_thread_t *p_wt, ml_media_t* p_media,
input_item_t *p_item, bool );
item_list_t* item_list_delMedia( watch_thread_t *p_wt, int i_media_id );
item_list_t* item_list_delItem( watch_thread_t *p_wt, input_item_t *p_item, bool );
item_list_t* item_list_listitemOfMediaId( watch_thread_t *p_wt, int i_media_id );
input_item_t* item_list_itemOfMediaId( watch_thread_t *p_wt, int i_media_id );
ml_media_t* item_list_mediaOfMediaId( watch_thread_t *p_wt, int i_media_id );
ml_media_t* item_list_mediaOfItem( watch_thread_t *p_wt, input_item_t* p_item, bool );
int item_list_mediaIdOfItem( watch_thread_t *p_wt, input_item_t *p_item );
int item_list_updateInput( watch_thread_t *p_wt, input_item_t *p_item,
bool b_play_count );
void item_list_destroy( watch_thread_t* p_wt );
/**
* @brief Simple hash function
* @param item_id Hash Key
* @return Hash index
*/
static inline int item_hash( input_item_t* p_item )
{
return DictHash( p_item->psz_uri, ML_ITEMLIST_HASH_LENGTH );
}
#endif /* ML_ITEM_LIST_H */
/*****************************************************************************
* media_pool.c : Media pool for watching system
*****************************************************************************
* Copyright (C) 2009-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
#define mp_foreachlist( a, b ) for( ml_poolobject_t* b = a; b; b = b->p_next )
static inline int mediapool_hash( int media_id )
{
return media_id % ML_MEDIAPOOL_HASH_LENGTH;
}
/**
* @brief Get a media from the pool
* @param p_ml ML object
* @param media_id The media id of the object to get
* @return the found media or NULL if not found
*/
ml_media_t* pool_GetMedia( media_library_t* p_ml, int media_id )
{
vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
ml_media_t* p_media = NULL;
mp_foreachlist( p_ml->p_sys->p_mediapool[ mediapool_hash( media_id ) ], p_item )
{
if( p_item->p_media->i_id == media_id )
{
p_media = p_item->p_media;
break;
}
}
if( p_media )
ml_gc_incref( p_media );
vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
return p_media;
}
/**
* @brief Insert a media into the media pool
* @param p_ml ML object
* @param p_media Media object to insert
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int pool_InsertMedia( media_library_t* p_ml, ml_media_t* p_media, bool locked )
{
if( !locked )
ml_LockMedia( p_media );
assert( p_media );
assert( p_media->i_id > 0 );
if( p_media->ml_gc_data.pool )
{
msg_Dbg( p_ml, "Already in pool! %s %d", p_media->psz_uri, p_media->i_id );
ml_UnlockMedia( p_media );
return VLC_EGENERIC;
}
p_media->ml_gc_data.pool = true;
int i_ret = VLC_SUCCESS;
vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
mp_foreachlist( p_ml->p_sys->p_mediapool[ (mediapool_hash(p_media->i_id)) ], p_item )
{
if( p_media == p_item->p_media )
{
i_ret = VLC_EGENERIC;
break;
}
else if( p_media->i_id == p_item->p_media->i_id )
{
i_ret = VLC_EGENERIC;
msg_Warn( p_ml, "A media of the same id was found, but in different objects!" );
break;
}
}
if( i_ret == VLC_SUCCESS )
{
ml_poolobject_t* p_new = ( ml_poolobject_t * ) calloc( 1, sizeof( ml_poolobject_t* ) );
if( !p_new )
i_ret = VLC_EGENERIC;
else
{
ml_gc_incref( p_media );
p_new->p_media = p_media;
p_new->p_next = p_ml->p_sys->p_mediapool[ ( mediapool_hash( p_media->i_id ) ) ];
p_ml->p_sys->p_mediapool[ ( mediapool_hash( p_media->i_id ) ) ] = p_new;
}
}
vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
if( !locked )
ml_UnlockMedia( p_media );
return i_ret;
}
/**
* @brief Perform a single garbage collection scan on the media pool
* @param p_ml The ML object
* @note Scans all media and removes any medias not held by any other objects.
*/
void pool_GC( media_library_t* p_ml )
{
vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
ml_poolobject_t* p_prev = NULL;
ml_media_t* p_media = NULL;
for( int i_idx = 0; i_idx < ML_MEDIAPOOL_HASH_LENGTH; i_idx++ )
{
p_prev = NULL;
for( ml_poolobject_t* p_item = p_ml->p_sys->p_mediapool[ i_idx ];
p_item != NULL; p_item = p_item->p_next )
{
p_media = p_item->p_media;
int refs;
refs = p_media->ml_gc_data.refs;
if( refs == 1 )
{
if( p_prev == NULL )
p_ml->p_sys->p_mediapool[i_idx] = p_item->p_next;
else
p_prev->p_next = p_item->p_next;
p_media->ml_gc_data.pool = false;
ml_gc_decref( p_item->p_media );//This should destroy the object
free( p_item );
}
p_prev = p_item;
}
}
vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
}
This diff is collapsed.
This diff is collapsed.
/*****************************************************************************
* sql_delete.c: SQL-based media library: all database delete functions
*****************************************************************************
* Copyright (C) 2008-2010 The VideoLAN Team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sql_media_library.h"
/**
* @brief Generic DELETE function for many medias
* Delete a media and all its referencies which don't point
* an anything else.
*
* @param p_ml This media_library_t object
* @param p_array list of ids to delete
* @return VLC_SUCCESS or VLC_EGENERIC
* TODO: Expand to delete media/artist/album given any params
*/
int Delete( media_library_t *p_ml, vlc_array_t *p_array )
{
char *psz_idlist = NULL, *psz_tmp = NULL;
int i_return = VLC_ENOMEM;
int i_rows = 0, i_cols = 0;
char **pp_results = NULL;
if( vlc_array_count( p_array ) <= 0 )
{
i_return = VLC_SUCCESS;
goto quit_delete_final;
}
for( int i = 0; i < vlc_array_count( p_array ); i++ )
{
ml_element_t* find = ( ml_element_t * )
vlc_array_item_at_index( p_array, i );
assert( find->criteria == ML_ID );
if( !psz_idlist )
{
if( asprintf( &psz_tmp, "( %d", find->value.i ) == -1)
{
goto quit_delete_final;
}
}
else
{
if( asprintf( &psz_tmp, "%s, %d", psz_idlist,
find->value.i ) == -1)
{
goto quit_delete_final;
}
}
free( psz_idlist );
psz_idlist = psz_tmp;
psz_tmp = NULL;
}
free( psz_tmp );
if( asprintf( &psz_tmp, "%s )", psz_idlist ? psz_idlist : "(" ) == -1 )
{
goto quit_delete_final;
}
psz_idlist = psz_tmp;
psz_tmp = NULL;
msg_Dbg( p_ml, "Multi Delete id list: %s", psz_idlist );
/**
* Below ensures you are emitting media-deleted only
* for existant media
*/
Begin( p_ml );
i_return = Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT id FROM media WHERE id IN %s", psz_idlist );
if( i_return != VLC_SUCCESS )
goto quit;
i_return = QuerySimple( p_ml,
"DELETE FROM media WHERE media.id IN %s", psz_idlist );
if( i_return != VLC_SUCCESS )
goto quit;
i_return = QuerySimple( p_ml,
"DELETE FROM extra WHERE extra.id IN %s", psz_idlist );
if( i_return != VLC_SUCCESS )
goto quit;
quit:
if( i_return == VLC_SUCCESS )
{
Commit( p_ml );
/* Emit delete on var media-deleted */
for( int i = 1; i <= i_rows; i++ )
{
var_SetInteger( p_ml, "media-deleted", atoi( pp_results[i*i_cols] ) );
}
}
else
Rollback( p_ml );
quit_delete_final:
FreeSQLResult( p_ml, pp_results );
free( psz_tmp );
free( psz_idlist );
return i_return;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -935,7 +935,6 @@ modules/lua/libs/volume.c ...@@ -935,7 +935,6 @@ modules/lua/libs/volume.c
modules/lua/meta.c modules/lua/meta.c
modules/lua/vlc.c modules/lua/vlc.c
modules/lua/vlc.h modules/lua/vlc.h
modules/media_library/sql_media_library.c
modules/meta_engine/folder.c modules/meta_engine/folder.c
modules/meta_engine/taglib.cpp modules/meta_engine/taglib.cpp
modules/misc/audioscrobbler.c modules/misc/audioscrobbler.c
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment