* 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"
static const char* ppsz_AudioExtensions[] = { EXTENSIONS_AUDIO_CSV, NULL };
static int load( vlc_object_t* );
static void unload( vlc_object_t* );
-static int CreateInputItemFromMedia( media_library_t *p_ml,
- input_item_t **pp_item,
- ml_media_t *p_media );
+static int CreateInputItemFromMedia( input_item_t **pp_item,
+ ml_media_t *p_media );
struct ml_table_elt
{
int column_id;
- char column_name[];
+ const char* column_name;
};
+static int compare_ml_elts( const void *a, const void *b )
+{
+ return strcmp( ( (struct ml_table_elt* )a )->column_name,
+ ( ( struct ml_table_elt* )b )->column_name );
+}
+
static const struct ml_table_elt ml_table_map[]=
{
- { ML_ALBUM_COVER, "album_cover" },
- { ML_ALBUM_ID, "album_id" },
- { ML_ALBUM, "album_title" },
- { ML_COMMENT, "comment" },
- { ML_COVER, "cover" },
- { ML_DIRECTORY, "directory_id" },
- { ML_DISC_NUMBER, "disc" },
- { ML_DURATION, "duration" },
- { ML_EXTRA, "extra" },
- { ML_FILESIZE, "filesize" },
- { ML_FIRST_PLAYED, "first_played" },
- { ML_GENRE, "genre" },
- { ML_ID, "id" },
- { ML_IMPORT_TIME, "import_time" },
- { ML_LANGUAGE, "language" },
- { ML_LAST_PLAYED, "last_played" },
- { ML_LAST_SKIPPED, "last_skipped" },
- { ML_ORIGINAL_TITLE,"original_title" },
- { ML_PEOPLE_ID, "people_id" },
- { ML_PEOPLE, "people_name" },
- { ML_PEOPLE_ROLE, "people_role" },
- { ML_PLAYED_COUNT, "played_count" },
- { ML_PREVIEW, "preview" },
- { ML_SCORE, "score" },
- { ML_SKIPPED_COUNT, "skipped_count" },
- { ML_TITLE, "title" },
- { ML_TRACK_NUMBER, "track" },
- { ML_TYPE, "type" },
- { ML_URI, "uri" },
- { ML_VOTE, "vote" },
- { ML_YEAR, "year" }
+ { ML_ALBUM_COVER, "album_cover" },
+ { ML_ALBUM_ID, "album_id" },
+ { ML_ALBUM, "album_title" },
+ { ML_COMMENT, "comment" },
+ { ML_COVER, "cover" },
+ { ML_DIRECTORY, "directory_id" },
+ { ML_DISC_NUMBER, "disc" },
+ { ML_DURATION, "duration" },
+ { ML_EXTRA, "extra" },
+ { ML_FILESIZE, "filesize" },
+ { ML_FIRST_PLAYED, "first_played" },
+ { ML_GENRE, "genre" },
+ { ML_ID, "id" },
+ { ML_IMPORT_TIME, "import_time" },
+ { ML_LANGUAGE, "language" },
+ { ML_LAST_PLAYED, "last_played" },
+ { ML_LAST_SKIPPED, "last_skipped" },
+ { ML_ORIGINAL_TITLE, "original_title" },
+ { ML_PEOPLE_ID, "people_id" },
+ { ML_PEOPLE, "people_name" },
+ { ML_PEOPLE_ROLE, "people_role" },
+ { ML_PLAYED_COUNT, "played_count" },
+ { ML_PREVIEW, "preview" },
+ { ML_SCORE, "score" },
+ { ML_SKIPPED_COUNT, "skipped_count" },
+ { ML_TITLE, "title" },
+ { ML_TRACK_NUMBER, "track" },
+ { ML_TYPE, "type" },
+ { ML_URI, "uri" },
+ { ML_VOTE, "vote" },
+ { ML_YEAR, "year" }
};
/*****************************************************************************
RECURSIVE_LONGTEXT, false )
add_bool( "ml-auto-add", true, N_("Auto add new medias"),
N_( "Automatically add new medias to ML" ), false )
+ add_bool( "ml-synchronous", true, N_("Use transactions"),
+ N_( "Disabling transactions saves I/O but can corrupt database in case of crash" ), false )
vlc_module_end()
vlc_mutex_init( &p_ml->p_sys->lock );
/* Initialise Sql module */
- InitDatabase( p_ml );
+ if ( InitDatabase( p_ml ) != VLC_SUCCESS )
+ {
+ vlc_mutex_destroy( &p_ml->p_sys->lock );
+ free( p_ml->p_sys );
+ return VLC_EGENERIC;
+ }
/* Initialise the media pool */
ARRAY_INIT( p_ml->p_sys->mediapool );
/* Analyze first row */
int *indexes = ( int* ) calloc( i_cols + 1, sizeof( int ) );
if( !indexes )
+ {
+ vlc_array_destroy( p_intermediate_array );
return VLC_ENOMEM;
+ }
const int count = sizeof( ml_table_map )/ sizeof( struct ml_table_elt );
for( int col = 0; col < i_cols; col++ )
{
- //binary search
- int low = 0, high = count - 1;
- int answer = -1;
- while( low <= high ) {
- int mid = (low + high ) / 2;
- char* mid_val = ml_table_map[mid].column_name;
- int cmp = strcmp( mid_val, res( 0, col ) );
- if( cmp > 0 )
- low = mid + 1;
- else if ( cmp < 0 )
- high = mid - 1;
- else
- {
- answer = mid; break;
- }
- }
- if( answer == -1 )
+ struct ml_table_elt key, *result = NULL;
+ key.column_name = res( 0, col );
+ result = bsearch( &key, ml_table_map, count,
+ sizeof( struct ml_table_elt ), compare_ml_elts );
+
+ if( !result )
msg_Warn( p_ml, "unknown column: %s", res( 0, col ) );
else
- indexes[col] = ml_table_map[answer].column_id;
+ indexes[col] = result->column_id;
}
/* Read rows 1 to i_rows */
if( !p_media )
{
free( indexes );
- return VLC_ENOMEM;
+ i_ret = VLC_ENOMEM;
+ goto quit_sqlmediaarray;
}
p_result = ( ml_result_t * ) calloc( 1, sizeof( ml_result_t ) );
if( !p_result )
{
ml_gc_decref( p_media );
free( indexes );
- return VLC_ENOMEM;
+ i_ret = VLC_ENOMEM;
+ goto quit_sqlmediaarray;
}
char* psz_append_pname = NULL;
}
else /* This is a repeat row and the people need to be put together */
{
+ free( p_result );
ml_LockMedia( p_append->value.p_media );
if( psz_append_pname && i_append_pid && psz_append_prole )
ml_CreateAppendPersonAdv( &(p_append->value.p_media->p_people),
if( i_ret != VLC_SUCCESS )
goto quit_createemptydatabase;
+ i_ret = QuerySimple( p_ml, "CREATE INDEX album_title_index ON album (title);" );
+ if( i_ret != VLC_SUCCESS )
+ goto quit_createemptydatabase;
+
/* Add "unknown" entry to albums */
i_ret = QuerySimple( p_ml,
"INSERT INTO album ( id, title, cover, album_artist_id ) "
if( i_ret != VLC_SUCCESS )
goto quit_createemptydatabase;
+ i_ret = QuerySimple( p_ml, "CREATE INDEX media_ui_index ON media (uri);" );
+ if( i_ret != VLC_SUCCESS )
+ goto quit_createemptydatabase;
+
/* People */
i_ret = QuerySimple( p_ml,
"CREATE TABLE people ( "
return VLC_SUCCESS;
}
+/**
+ * @brief Journal and synchronous disc and writes
+ *
+ * @param p_ml media library object
+ * @param b_sync boolean
+ * @return <= 0 on error.
+ */
+static int SetSynchronous( media_library_t *p_ml, bool b_sync )
+{
+ int i_rows, i_cols;
+ char **pp_results;
+ int i_return;
+ if ( b_sync )
+ i_return = Query( p_ml, &pp_results, &i_rows, &i_cols,
+ "PRAGMA synchronous = ON;PRAGMA journal_mode = TRUNCATE" );
+ else
+ i_return = Query( p_ml, &pp_results, &i_rows, &i_cols,
+ "PRAGMA synchronous = OFF;PRAGMA journal_mode = MEMORY" );
+ if( i_return != VLC_SUCCESS )
+ i_return = -1;
+ else
+ i_return = atoi( pp_results[ 1 ] );
+
+ FreeSQLResult( p_ml, pp_results );
+
+ return i_return;
+}
/**
* @brief Initiates database (create the database and the tables if needed)
/* Select database name */
char *psz_dbhost = NULL, *psz_user = NULL, *psz_pass = NULL;
int i_port = 0;
+ bool b_sync = false;
psz_dbhost = config_GetPsz( p_ml, "ml-filename" );
psz_user = config_GetPsz( p_ml, "ml-username" );
psz_pass = config_GetPsz( p_ml, "ml-password" );
i_port = config_GetInt( p_ml, "ml-port" );
+ b_sync = config_GetInt( p_ml, "ml-synchronous" );
/* Let's consider that a filename with a DIR_SEP is a full URL */
if( strchr( psz_dbhost, DIR_SEP_CHAR ) == NULL )
p_ml->p_sys->p_sql = sql_Create( p_ml, NULL, psz_dbhost, i_port, psz_user,
psz_pass );
if( !p_ml->p_sys->p_sql )
- {
- vlc_mutex_destroy( &p_ml->p_sys->lock );
- free( p_ml->p_sys );
return VLC_EGENERIC;
- }
/* Let's check if tables exist */
int i_version = GetDatabaseVersion( p_ml );
#error "ML versioning code needs to be updated. Is this done correctly?"
#endif
+ SetSynchronous( p_ml, b_sync );
+
msg_Dbg( p_ml, "ML initialized" );
return VLC_SUCCESS;
}
else
{
ml_LockMedia( p_media );
- if( p_media->b_sparse == true && select == ML_MEDIA )
+ if( p_media->b_sparse && select == ML_MEDIA )
reload = true;
/* Utilise ML_MEDIA_EXTRA load? TODO */
ml_UnlockMedia( p_media );
ml_media_t* p_media = media_New( p_ml, i_media, ML_MEDIA, true );
if( p_media == NULL )
return NULL;
- CreateInputItemFromMedia( p_ml, &p_item, p_media );
+ CreateInputItemFromMedia( &p_item, p_media );
watch_add_Item( p_ml, p_item, p_media );
ml_gc_decref( p_media );
}
p_media->psz_title = input_item_GetTitle ( p_item );
p_media->psz_uri = input_item_GetURL ( p_item );
if( !p_media->psz_uri )
- p_media->psz_uri = strdup( p_item->psz_uri );
+ p_media->psz_uri = input_item_GetURI( p_item );
p_media->psz_album = input_item_GetAlbum ( p_item );
p_media->psz_cover = input_item_GetArtURL ( p_item );
p_media->psz_genre = input_item_GetGenre ( p_item );
ml_LockMedia( p_media );
if( p_media->psz_title && *p_media->psz_title )
input_item_SetTitle( p_item, p_media->psz_title );
- if( p_media->psz_uri && *p_media->psz_uri )
+ if( p_media->psz_uri && *p_media->psz_uri && !strncmp( p_media->psz_uri, "http", 4 ) )
input_item_SetURL( p_item, p_media->psz_uri );
if( p_media->psz_album && *p_media->psz_album )
input_item_SetAlbum( p_item, p_media->psz_album );
/**
* @brief Copy a ml_media_t to an input_item_t
- * @param p_ml The Media Library object
* @param pp_item A pointer to a new input_item (return value)
* @param p_media The media to copy as an input item
* @note This function is threadsafe
*/
-static int CreateInputItemFromMedia( media_library_t *p_ml,
- input_item_t **pp_item,
- ml_media_t *p_media )
+static int CreateInputItemFromMedia( input_item_t **pp_item,
+ ml_media_t *p_media )
{
- playlist_t *p_pl = pl_Get( p_ml );
- *pp_item = input_item_New( VLC_OBJECT( p_pl ),
- p_media->psz_uri,
- p_media->psz_title );
+ *pp_item = input_item_New( p_media->psz_uri, p_media->psz_title );
/* ITEM_TYPE_FILE ); */
if( !*pp_item )
return VLC_EGENERIC;