gmyth/gmyth/gmyth_scheduler.c
author melunko
Thu Mar 13 16:29:38 2008 +0000 (2008-03-13)
branchtrunk
changeset 942 c93bfa74c71f
parent 939 1418e08cee7a
child 944 ec1d5934d8e8
permissions -rw-r--r--
[svn r951] gmyth now is 0.8.1. Added methods epg_is_connected() and scheduler_is_connected()
     1 /**
     2  * GMyth Library
     3  *
     4  * @file gmyth/gmyth_scheduler.c
     5  *
     6  * @brief <p> The scheduler encapsulates all functions for browsing, scheduling
     7  * and modifying the recorded content.
     8  *
     9  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    10  * @author Alexsandro Jose Virginio dos Santos <alexsandro.santos@indt.org.br>
    11  *
    12  *
    13  * This program is free software; you can redistribute it and/or modify
    14  * it under the terms of the GNU Lesser General Public License as published by
    15  * the Free Software Foundation; either version 2 of the License, or
    16  * (at your option) any later version.
    17  *
    18  * This program is distributed in the hope that it will be useful,
    19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    21  * GNU General Public License for more details.
    22  *
    23  * You should have received a copy of the GNU Lesser General Public License
    24  * along with this program; if not, write to the Free Software
    25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    26  */
    27 
    28 #ifdef HAVE_CONFIG_H
    29 #include "config.h"
    30 #endif
    31 
    32 #include <assert.h>
    33 
    34 #include <glib.h>
    35 #include <glib/gprintf.h>
    36 
    37 #include "gmyth_scheduler.h"
    38 #include "gmyth_util.h"
    39 #include "gmyth_query.h"
    40 #include "gmyth_socket.h"
    41 #include "gmyth_debug.h"
    42 #include "gmyth_remote_util.h"
    43 
    44 static void     gmyth_scheduler_class_init(GMythSchedulerClass * klass);
    45 static void     gmyth_scheduler_init(GMythScheduler * object);
    46 
    47 static void     gmyth_scheduler_dispose(GObject * object);
    48 static void     gmyth_scheduler_finalize(GObject * object);
    49 
    50 
    51 static gint
    52 gmyth_scheduler_is_program_live_recorded (GMythScheduler * scheduler,
    53                                           ScheduleInfo *schedule_info,
    54                                           GMythProgramInfo **prog_info);
    55 static gboolean
    56 gmyth_scheduler_set_live_record (GMythScheduler * scheduler,
    57                                 ScheduleInfo * schedule_info,
    58                                 gint recorder_num, gboolean enable);
    59 static gboolean
    60 gmyth_scheduler_change_record_group (GMythScheduler* scheduler,
    61                                      GMythProgramInfo *prog_info,
    62                                      gchar *new_group);
    63 
    64 static gint
    65 gmyth_scheduler_query_schedule_list (GMythScheduler * scheduler,
    66                                      GList ** schedule_list,
    67                                      gchar *filter);
    68 static gint 
    69 gmyth_scheduler_query_schedule_id (GMythScheduler *scheduler,
    70                                    GMythProgramInfo *prog_info);
    71 static gint gmyth_scheduler_query_old_schedule (GMythScheduler *scheduler,
    72                                     gint channel_id, GTimeVal *startts,
    73                                     gboolean reactivate);
    74 
    75 static gboolean update_backend(GMythScheduler * scheduler, gint record_id);
    76 
    77 G_DEFINE_TYPE(GMythScheduler, gmyth_scheduler, G_TYPE_OBJECT)
    78     static void     gmyth_scheduler_class_init(GMythSchedulerClass * klass)
    79 {
    80     GObjectClass   *gobject_class;
    81 
    82     gobject_class = (GObjectClass *) klass;
    83 
    84     gobject_class->dispose = gmyth_scheduler_dispose;
    85     gobject_class->finalize = gmyth_scheduler_finalize;
    86 }
    87 
    88 static void
    89 gmyth_scheduler_init(GMythScheduler * sched)
    90 {
    91     sched->recordid = 0;
    92     sched->type = 0;
    93     sched->search = 0;
    94     sched->profile = g_string_new("");
    95 
    96     sched->dupin = 0;
    97     sched->dupmethod = 0;
    98     sched->autoexpire = 0;
    99     sched->autotranscode = 0;
   100     sched->transcoder = 0;
   101 
   102     sched->autocommflag = 0;
   103     sched->autouserjob1 = 0;
   104     sched->autouserjob2 = 0;
   105     sched->autouserjob3 = 0;
   106     sched->autouserjob4 = 0;
   107 
   108     sched->startoffset = 0;
   109     sched->endoffset = 0;
   110     sched->maxepisodes = 0;
   111     sched->maxnewest = 0;
   112 
   113     sched->recpriority = 0;
   114     sched->recgroup = g_string_new("");
   115     sched->playgroup = g_string_new("");
   116 
   117     sched->prefinput = 0;
   118     sched->inactive = 0;
   119 
   120     sched->search_type = g_string_new("");
   121     sched->search_what = g_string_new("");
   122 
   123     sched->msqlquery = gmyth_query_new();
   124 }
   125 
   126 static void
   127 gmyth_scheduler_dispose(GObject * object)
   128 {
   129     GMythScheduler *scheduler = GMYTH_SCHEDULER(object);
   130 
   131     if (scheduler->backend_info) {
   132         g_object_unref(scheduler->backend_info);
   133         scheduler->backend_info = NULL;
   134     }
   135 
   136     if (scheduler->msqlquery) {
   137         g_object_unref(scheduler->msqlquery);
   138         scheduler->msqlquery = NULL;
   139     }
   140 
   141     g_string_free(scheduler->profile, TRUE);
   142     g_string_free(scheduler->recgroup, TRUE);
   143     g_string_free(scheduler->playgroup, TRUE);
   144     g_string_free(scheduler->search_type, TRUE);
   145     g_string_free(scheduler->search_what, TRUE);
   146 
   147     G_OBJECT_CLASS(gmyth_scheduler_parent_class)->dispose(object);
   148 }
   149 
   150 static void
   151 gmyth_scheduler_finalize(GObject * object)
   152 {
   153     g_signal_handlers_destroy(object);
   154 
   155     G_OBJECT_CLASS(gmyth_scheduler_parent_class)->finalize(object);
   156 }
   157 
   158 /** Creates a new instance of GMythScheduler.
   159  * 
   160  * @return a new instance of GMythScheduler.
   161  */
   162 GMythScheduler *
   163 gmyth_scheduler_new()
   164 {
   165     GMythScheduler *scheduler =
   166         GMYTH_SCHEDULER(g_object_new(GMYTH_SCHEDULER_TYPE, NULL));
   167 
   168     return scheduler;
   169 }
   170 
   171 gboolean
   172 gmyth_scheduler_connect(GMythScheduler * scheduler,
   173                         GMythBackendInfo * backend_info)
   174 {
   175     return gmyth_scheduler_connect_with_timeout(scheduler, backend_info,
   176                                                 0);
   177 }
   178 
   179 /** Connects to the Mysql database in the backend. The backend address
   180  * is loaded from the GMythSettings instance.
   181  *
   182  * @param scheduler the GMythScheduler instance to be connected.
   183  * @return true if connection was success, false if failed.
   184  */
   185 gboolean
   186 gmyth_scheduler_connect_with_timeout(GMythScheduler * scheduler,
   187                                      GMythBackendInfo * backend_info,
   188                                      guint timeout)
   189 {
   190     assert(scheduler);
   191     g_return_val_if_fail(backend_info != NULL, FALSE);
   192 
   193     if (scheduler->backend_info)
   194         g_object_unref(scheduler->backend_info);
   195 
   196     scheduler->backend_info = g_object_ref(backend_info);
   197 
   198     if (scheduler->msqlquery == NULL) {
   199         g_warning("[%s] GMythScheduler db initializing", __FUNCTION__);
   200         scheduler->msqlquery = gmyth_query_new();
   201     }
   202 
   203     if (!gmyth_query_connect_with_timeout(scheduler->msqlquery,
   204                                           scheduler->backend_info,
   205                                           timeout)) {
   206         g_warning("[%s] Error while connecting to db", __FUNCTION__);
   207         return FALSE;
   208     }
   209 
   210     return TRUE;
   211 }
   212 
   213 gboolean
   214 gmyth_scheduler_is_connected (GMythScheduler *scheduler)
   215 {
   216     g_return_val_if_fail (scheduler != NULL, FALSE);
   217 
   218     return gmyth_query_is_alive (scheduler->msqlquery);
   219 }
   220 
   221 /** Disconnects from the Mysql database in the backend.
   222  *
   223  * @param scheduler the GMythScheduler instance to be disconnected
   224  * @return true if disconnection was success, false if failed.
   225  */
   226 gboolean
   227 gmyth_scheduler_disconnect(GMythScheduler * scheduler)
   228 {
   229     assert(scheduler);
   230 
   231     if (scheduler->msqlquery != NULL) {
   232         gmyth_query_disconnect(scheduler->msqlquery);
   233     }
   234 
   235     return TRUE;
   236 }
   237 
   238 /** Retrieves from the backend Mysql database the list of recording schedules.
   239  * 
   240  * @param scheduler The GMythScheduler instance.
   241  * @param schedule_list the GList pointer to be filled with the loaded list of ScheduleInfo items.
   242  * @return The amount of schedules retrieved from database, or -1 if error.
   243  */
   244 gint
   245 gmyth_scheduler_get_schedule_list (GMythScheduler * scheduler,
   246                                    GList ** schedule_list)
   247 {
   248     return gmyth_scheduler_query_schedule_list (scheduler, schedule_list, NULL);
   249 }
   250 
   251 ScheduleInfo*
   252 gmyth_scheduler_get_schedule (GMythScheduler * scheduler, gint sched_id)
   253 {
   254     GList *sched_list = NULL;
   255     ScheduleInfo *schedule = NULL;
   256     gchar *filter;
   257     gint count;
   258 
   259     filter = g_strdup_printf ("WHERE recordid=%d", sched_id);
   260 
   261     count = gmyth_scheduler_query_schedule_list (scheduler, &sched_list, filter);
   262     if ((count > 0) && (sched_list != NULL)) {
   263         schedule = (ScheduleInfo*) sched_list->data;
   264 	g_list_free (sched_list);
   265     }
   266 
   267     g_free (filter);
   268     return schedule;
   269 }
   270 
   271 static gint
   272 gmyth_scheduler_query_schedule_list (GMythScheduler * scheduler,
   273 		                     GList ** schedule_list,
   274 				     gchar *filter)
   275 {
   276 	
   277     ScheduleInfo   *schedule;
   278     MYSQL_RES      *msql_res;
   279     GString        *query_str = g_string_new("");
   280     gchar          *date_time = NULL;
   281 
   282     assert(scheduler);
   283     if (scheduler->msqlquery == NULL) {
   284         g_warning("[%s] Scheduler db connection not initialized",
   285                   __FUNCTION__);
   286         return -1;
   287     }
   288 
   289     if (filter == NULL) {
   290         g_string_printf(query_str,
   291                         "SELECT recordid,programid,chanid,starttime,startdate,"
   292                         "endtime,enddate,title,subtitle,description,category,type,parentid,seriesid FROM record;");
   293     } else {
   294         g_string_printf(query_str,
   295                         "SELECT recordid,programid,chanid,starttime,startdate,"
   296                         "endtime,enddate,title,subtitle,description,category,type,parentid,seriesid FROM record "
   297 			"%s;", filter);
   298 
   299     }
   300 
   301     msql_res =
   302         gmyth_query_process_statement(scheduler->msqlquery,
   303                                       query_str->str);
   304 
   305     if (msql_res == NULL) {
   306         g_warning("DB retrieval of schedule list failed");
   307         return -1;
   308     } else {
   309         MYSQL_ROW       row;
   310 
   311         *schedule_list = NULL;
   312 
   313         while ((row = mysql_fetch_row(msql_res)) != NULL) {
   314             schedule = g_new0(ScheduleInfo, 1);
   315             gint type = 0;
   316 
   317             schedule->schedule_id =
   318                 (guint) g_ascii_strtoull(row[0], NULL, 10);
   319             schedule->program_id = g_string_new (row[1]);
   320             schedule->channel_id = (gint) g_ascii_strtoull (row[2], NULL, 10);
   321 
   322             /*
   323              * generate a time_t from a time and a date db field 
   324              */
   325             date_time = g_strdup_printf("%sT%s", row[4], row[3]);
   326             schedule->start_time =
   327                 gmyth_util_string_to_time_val(date_time);
   328             g_free(date_time);
   329 
   330             /*
   331              * generate a time_t from a time and a date db field 
   332              */
   333             date_time = g_strdup_printf("%sT%s", row[6], row[5]);
   334             schedule->end_time = gmyth_util_string_to_time_val(date_time);
   335             g_free(date_time);
   336 
   337             schedule->title = g_string_new(row[7]);
   338             schedule->subtitle = g_string_new(row[8]);
   339             schedule->description = g_string_new(row[9]);
   340             schedule->category = g_string_new(row[10]);
   341             type = g_ascii_strtoull (row[11], NULL, 10);
   342             if (type == 4) {
   343                 schedule->type = GMYTH_SCHEDULE_ALL_OCCURRENCES;
   344             } else if (type == 1) {
   345                 schedule->type = GMYTH_SCHEDULE_ONE_OCCURRENCE;
   346             } else if (type == 8) {
   347                 schedule->type = GMYTH_SCHEDULE_EXCEPTION;
   348                 schedule->parentid = (gint) g_ascii_strtoull (row[12], NULL, 10);
   349             }
   350 
   351             schedule->seriesid = g_string_new (row[13]);
   352 
   353             (*schedule_list) = g_list_append(*(schedule_list), schedule);
   354         }
   355     }
   356 
   357     mysql_free_result(msql_res);
   358     g_string_free(query_str, TRUE);
   359 
   360     return (*schedule_list == NULL) ? 0 : g_list_length(*schedule_list);
   361 }
   362 
   363 /** Retrieves from the backend Mysql database the list of recorded programs.
   364  * 
   365  * @param scheduler The GMythScheduler instance.
   366  * @param recorded_list the GList pointer to be filled with the loaded RecordInfo items.
   367  * @return The amount of recorded retrieved from database, or -1 if error.
   368  */
   369 gint
   370 gmyth_scheduler_get_recorded_list(GMythScheduler * scheduler,
   371                                   GList ** recorded_list)
   372 {
   373     RecordedInfo   *record;
   374     MYSQL_RES      *msql_res;
   375     GString        *query_str = g_string_new("");
   376 
   377     assert(scheduler);
   378 
   379     g_string_printf(query_str,
   380                     "SELECT recordid,programid,chanid,starttime,progstart,"
   381                     "endtime,progend,title,subtitle,description,category,"
   382                     "filesize,basename,seriesid FROM recorded WHERE recgroup != 'LiveTV'");
   383 
   384     if (scheduler->msqlquery == NULL) {
   385         g_warning("[%s] Scheduler db connection not initialized",
   386                   __FUNCTION__);
   387         return -1;
   388     }
   389 
   390     msql_res =
   391         gmyth_query_process_statement(scheduler->msqlquery,
   392                                       query_str->str);
   393 
   394     if (msql_res == NULL) {
   395         g_warning("DB retrieval of recording list failed");
   396         return -1;
   397     } else {
   398         MYSQL_ROW       row;
   399 
   400         (*recorded_list) = NULL;
   401 
   402         while ((row = mysql_fetch_row(msql_res)) != NULL) {
   403             record = g_new0(RecordedInfo, 1);
   404 
   405             record->record_id = (guint) g_ascii_strtoull(row[0], NULL, 10);
   406             record->program_id = g_string_new (row[1]);
   407             record->channel_id = (gint) g_ascii_strtoull(row[2], NULL, 10);
   408             record->start_time = gmyth_util_string_to_time_val(row[3]);
   409             record->end_time = gmyth_util_string_to_time_val(row[5]);
   410 
   411             record->title = g_string_new(row[7]);
   412             record->subtitle = g_string_new(row[8]);
   413             record->description = g_string_new(row[9]);
   414             record->category = g_string_new(row[10]);
   415             record->filesize = g_ascii_strtoull(row[11], NULL, 10);
   416             record->basename = g_string_new(row[12]);
   417             record->seriesid = g_string_new(row[13]);
   418 
   419             (*recorded_list) = g_list_append((*recorded_list), record);
   420         }
   421     }
   422 
   423     mysql_free_result(msql_res);
   424     g_string_free(query_str, TRUE);
   425 
   426     return (*recorded_list == NULL) ? 0 : g_list_length(*recorded_list);
   427 }
   428 
   429 RecordedInfo*
   430 gmyth_scheduler_get_recorded_info (GMythScheduler *scheduler,
   431                                    const gchar* basename)
   432 {
   433     RecordedInfo   *record = NULL;
   434     MYSQL_RES      *msql_res;
   435     GString        *query_str = g_string_new("");
   436 
   437     assert(scheduler);
   438 
   439     g_string_printf(query_str,
   440                     "SELECT recordid,programid,chanid,starttime,progstart,"
   441                     "endtime,progend,title,subtitle,description,category,"
   442                     "filesize,basename,seriesid FROM recorded "
   443                     "WHERE recgroup != 'LiveTV' AND basename = '%s'", basename);
   444 
   445     if (scheduler->msqlquery == NULL) {
   446         g_warning("[%s] Scheduler db connection not initialized", 
   447                         __FUNCTION__);
   448         return NULL;
   449     }
   450 
   451     msql_res =
   452         gmyth_query_process_statement(scheduler->msqlquery,
   453                                       query_str->str);
   454 
   455     if (msql_res == NULL) {
   456         g_warning("DB retrieval of recording list failed");
   457         return NULL;
   458     } else {
   459         MYSQL_ROW       row;
   460         row = mysql_fetch_row(msql_res);
   461         if (row != NULL) {
   462             record = g_new0(RecordedInfo, 1);
   463             record->record_id = (guint) g_ascii_strtoull(row[0], NULL, 10);
   464             record->program_id = g_string_new (row[1]);
   465             record->channel_id = (gint) g_ascii_strtoull(row[2], NULL, 10);
   466             record->start_time = gmyth_util_string_to_time_val(row[3]);
   467             record->end_time = gmyth_util_string_to_time_val(row[5]);
   468             record->title = g_string_new(row[7]);
   469             record->subtitle = g_string_new(row[8]);
   470             record->description = g_string_new(row[9]);
   471             record->category = g_string_new(row[10]);
   472             record->filesize = g_ascii_strtoull(row[11], NULL, 10);
   473             record->basename = g_string_new(row[12]);
   474             record->seriesid = g_string_new(row[13]);
   475         }
   476     }
   477 
   478     mysql_free_result(msql_res);
   479     g_string_free(query_str, TRUE);
   480 
   481     return record;
   482 }
   483 
   484 
   485 static void
   486 _set_value(GMythQuery * myth_query, char *field, gchar * value,
   487            gint rec_id)
   488 {
   489     gchar          *query =
   490         g_strdup_printf
   491         ("UPDATE record SET recordid = %d, %s = \"%s\" WHERE recordid = %d;",
   492          rec_id, field, value, rec_id);
   493 
   494     gmyth_query_process_statement(myth_query, query);
   495     g_free(query);
   496 }
   497 
   498 static void
   499 _set_int_value(GMythQuery * myth_query, char *field, gint value,
   500                gint rec_id)
   501 {
   502     gchar *str_value = g_strdup_printf("%d", value);
   503 
   504     _set_value(myth_query, field, str_value, rec_id);
   505     g_free(str_value);
   506 }
   507 
   508 ScheduleInfo*
   509 gmyth_scheduler_add_schedule_program (GMythScheduler * scheduler,
   510                                       GMythProgramInfo *program,
   511                                       GMythScheduleType type)
   512 {
   513     ScheduleInfo *info;
   514 
   515     info = g_new0 (ScheduleInfo, 1);
   516     info->program_id = g_string_new (program->program_id->str);
   517     info->channel_id = program->channel_id;
   518     info->start_time = g_new0 (GTimeVal, 1);
   519     *info->start_time = *program->startts;
   520     info->end_time = g_new0 (GTimeVal, 1);
   521     *info->end_time = *program->endts;
   522     info->seriesid = g_string_new (program->seriesid->str);
   523     info->title = g_string_new (program->title->str);
   524     info->subtitle = g_string_new (program->subtitle->str);
   525     info->description = g_string_new (program->description->str);
   526     info->category = g_string_new (program->category->str);
   527     info->type = type;
   528 
   529     if (gmyth_scheduler_add_schedule_full (scheduler, info, type))
   530     {
   531         if (!program->recstartts)
   532             program->recstartts = g_new0 (GTimeVal, 1);
   533         *program->recstartts = *info->start_time;
   534 
   535         if (!program->recendts)
   536             program->recendts = g_new0 (GTimeVal, 1);
   537         *program->recendts = *info->end_time;
   538 
   539         program->recordid = info->schedule_id;
   540         return info;
   541     }
   542 
   543     gmyth_schedule_info_free (info);
   544     return NULL;
   545 }
   546 
   547 
   548 gboolean
   549 gmyth_scheduler_add_schedule_full (GMythScheduler * scheduler,
   550                              ScheduleInfo *schedule_info, GMythScheduleType type)
   551 {
   552     MYSQL_RES      *msql_res;
   553     gchar          *query_str = NULL;
   554     gchar          *station = NULL;
   555     gulong          rec_id;
   556 
   557     g_return_val_if_fail (IS_GMYTH_SCHEDULER (scheduler), FALSE);
   558 
   559     if (scheduler->msqlquery == NULL) {
   560         g_warning("[%s] Scheduler db connection not initialized",
   561                   __FUNCTION__);
   562         return FALSE;
   563     }
   564 
   565     /*
   566     if (!gmyth_query_is_alive (scheduler->msqlquery)) {
   567 	g_warning ("[%s] MySql connection is not alive", __FUNCTION__);
   568         return FALSE;
   569     }
   570     */
   571 
   572     {
   573         /* Before adding the schedule, we need to check if program is been played */
   574         GMythProgramInfo *prog_info = NULL;
   575 	gint recorder_num = 
   576                 gmyth_scheduler_is_program_live_recorded (scheduler, schedule_info,
   577                                                           &prog_info);
   578 	if ((recorder_num > 0) && (prog_info != NULL)) {
   579 	    gboolean res;
   580             gmyth_scheduler_change_record_group (scheduler, prog_info, "Default");
   581 
   582             res = gmyth_scheduler_set_live_record (scheduler, schedule_info,
   583 			recorder_num, TRUE);
   584 	    schedule_info->schedule_id = gmyth_scheduler_query_schedule_id (scheduler, prog_info);
   585             return res;
   586 	} 
   587     }
   588 
   589     /* Now, verifies if there is an old schedule for this program */
   590     /* If threre is one, we reactivate it */
   591     {
   592         gint sched_id;
   593 	time_t now = time (NULL);
   594 
   595 	if (schedule_info->start_time->tv_sec < now) {
   596 	    sched_id = gmyth_scheduler_query_old_schedule (scheduler, schedule_info->channel_id,
   597 			                               schedule_info->start_time, TRUE);
   598 	    if (sched_id > 0) {
   599                 schedule_info->schedule_id = sched_id;
   600 	        return TRUE;
   601             } 
   602 	}
   603     }
   604 
   605     msql_res =
   606         gmyth_query_process_statement_with_increment(scheduler->msqlquery,
   607 			              "INSERT record (recordid) VALUE (0);",
   608                                       &rec_id);
   609     mysql_free_result(msql_res);
   610 
   611     // Retrieves the station info
   612     query_str =
   613         g_strdup_printf
   614         ("SELECT callsign FROM channel WHERE chanid = %d;",
   615          schedule_info->channel_id);
   616     msql_res =
   617         gmyth_query_process_statement(scheduler->msqlquery, query_str);
   618     if (msql_res == NULL) {
   619         g_warning("[%s] msql query returned NULL MYSQL_RES", __FUNCTION__);
   620         return FALSE;
   621     } else {
   622         MYSQL_ROW       row;
   623 
   624         if ((row = mysql_fetch_row(msql_res)) != NULL) {
   625             station = g_strdup(row[0]);
   626         }
   627     }
   628     mysql_free_result(msql_res);
   629     g_free(query_str);
   630 
   631     // _set_value (field, value, id);
   632     _set_int_value(scheduler->msqlquery, "chanid",
   633                    schedule_info->channel_id, rec_id);
   634     _set_value(scheduler->msqlquery, "station", station, rec_id);
   635     _set_value(scheduler->msqlquery, "title", schedule_info->title->str,
   636                rec_id);
   637     // / subtitle, description 
   638     _set_value(scheduler->msqlquery, "starttime",
   639                gmyth_util_time_to_string_only_time(schedule_info->
   640                                                    start_time), rec_id);
   641     _set_value(scheduler->msqlquery, "startdate",
   642                gmyth_util_time_to_string_only_date(schedule_info->
   643                                                    start_time), rec_id);
   644     _set_value(scheduler->msqlquery, "endtime",
   645                gmyth_util_time_to_string_only_time(schedule_info->
   646                                                    end_time), rec_id);
   647     _set_value(scheduler->msqlquery, "enddate",
   648                gmyth_util_time_to_string_only_date(schedule_info->
   649                                                    end_time), rec_id);
   650     // / category, series id, program id
   651     // _set_value (scheduler->msqlquery, "findday",
   652     // (gmyth_util_time_val_to_date( schedule_info->start_time
   653     // ))->tm_wday, rec_id);
   654     // _set_value (scheduler->msqlquery, "findtime",
   655     // gmyth_util_time_to_string_only_time( schedule_info->start_time),
   656     // rec_id);
   657     // _set_int_value (scheduler->msqlquery, "findid",
   658     // (gint)(schedule_info->start_time->tv_sec/60/60/24 + 719528),
   659     // rec_id);
   660 
   661     if (schedule_info->seriesid)
   662        _set_value(scheduler->msqlquery, "seriesid",
   663                   schedule_info->seriesid->str, rec_id);
   664  
   665     _set_value(scheduler->msqlquery, "parentid", "0", rec_id);
   666     _set_value(scheduler->msqlquery, "search", "0", rec_id);
   667 
   668     if (type == GMYTH_SCHEDULE_ALL_OCCURRENCES) {
   669         _set_int_value(scheduler->msqlquery, "type", 3, rec_id);
   670     } else if (type == GMYTH_SCHEDULE_ONE_OCCURRENCE) {
   671         _set_int_value(scheduler->msqlquery, "type", 1, rec_id);
   672     } else if (type == GMYTH_SCHEDULE_EXCEPTION) {
   673         _set_int_value(scheduler->msqlquery, "type", 8, rec_id);
   674         _set_int_value(scheduler->msqlquery, "parentid", schedule_info->parentid,
   675 		       rec_id);
   676     }
   677 
   678     _set_value(scheduler->msqlquery, "recpriority", "0", rec_id);
   679     _set_value(scheduler->msqlquery, "startoffset", "0", rec_id);
   680     _set_value(scheduler->msqlquery, "endoffset", "0", rec_id);
   681     _set_value(scheduler->msqlquery, "dupmethod", "6", rec_id); // ?
   682     _set_value(scheduler->msqlquery, "dupin", "15", rec_id);    // ?
   683 
   684     _set_value(scheduler->msqlquery, "prefinput", "0", rec_id);
   685     _set_value(scheduler->msqlquery, "inactive", "0", rec_id);
   686     _set_value(scheduler->msqlquery, "profile", "Default", rec_id);
   687     _set_value(scheduler->msqlquery, "recgroup", "Default", rec_id);
   688     _set_value(scheduler->msqlquery, "storagegroup", "Default", rec_id);
   689     _set_value(scheduler->msqlquery, "playgroup", "Default", rec_id);
   690     _set_value(scheduler->msqlquery, "autoexpire", "1", rec_id);
   691     _set_value(scheduler->msqlquery, "maxepisodes", "0", rec_id);
   692     _set_value(scheduler->msqlquery, "maxnewest", "0", rec_id);
   693     _set_value(scheduler->msqlquery, "autocommflag", "1", rec_id);
   694     _set_value(scheduler->msqlquery, "autotranscode", "0", rec_id);
   695     _set_value(scheduler->msqlquery, "transcoder", "0", rec_id);
   696 
   697     _set_value(scheduler->msqlquery, "autouserjob1", "0", rec_id);
   698     _set_value(scheduler->msqlquery, "autouserjob2", "0", rec_id);
   699     _set_value(scheduler->msqlquery, "autouserjob3", "0", rec_id);
   700     _set_value(scheduler->msqlquery, "autouserjob4", "0", rec_id);
   701 
   702     schedule_info->schedule_id = rec_id;
   703 
   704     /* Notify the backend of changes */
   705     return update_backend(scheduler, rec_id);
   706 }
   707 
   708 /** Requests the Mysql database in the backend to add a new schedule.
   709  * 
   710  * @param scheduler the GMythScheduler instance.
   711  * @param schedule_info the ScheduleInfo with recording schedule information
   712  * to be added. record_id = -1 to add a new schedule, otherwise this
   713  * function will update the schedule in the db
   714  * @return gboolean returns FALSE if some error occurs, TRUE otherwise
   715  */
   716 gboolean
   717 gmyth_scheduler_add_schedule (GMythScheduler * scheduler,
   718                              ScheduleInfo * schedule_info)
   719 {
   720     return gmyth_scheduler_add_schedule_full (scheduler, schedule_info, 
   721                                               GMYTH_SCHEDULE_ONE_OCCURRENCE);
   722 }
   723 
   724 static gint
   725 gmyth_scheduler_query_schedule_id (GMythScheduler *scheduler,
   726 		                   GMythProgramInfo *prog_info)
   727 {
   728     MYSQL_RES *mysql_res;
   729     gint sched_id = -1;
   730     gchar *query_str, *startts;
   731 
   732     startts = gmyth_util_time_to_string_from_time_val (prog_info->recstartts);
   733     query_str = g_strdup_printf ("SELECT recordid from recorded "
   734 		    "WHERE chanid= \"%d\" AND starttime = \"%s\";",
   735                     prog_info->channel_id, startts);
   736 
   737     mysql_res = gmyth_query_process_statement(scheduler->msqlquery, query_str);
   738 
   739     if (mysql_res) {
   740         MYSQL_ROW msql_row = mysql_fetch_row(mysql_res);
   741         if (msql_row) {
   742             sched_id = g_ascii_strtoull (msql_row[0], NULL, 10);
   743         } 
   744     }
   745 
   746     return sched_id;
   747 }
   748 
   749 /* Queries if there is an old schedule previously set to this program.
   750  * If reactivate flag is set, the recording will be reactivated.
   751  * @returns the schedule id.
   752  */
   753 static int
   754 gmyth_scheduler_query_old_schedule (GMythScheduler *scheduler,
   755 		                    gint channel_id, GTimeVal *startts,
   756                                     gboolean reactivate)
   757 {
   758     MYSQL_RES *mysql_res;
   759     gint sched_id = -1;
   760     gchar *query_str, *startts_str;
   761 
   762     startts_str = gmyth_util_time_to_string_from_time_val (startts);
   763     query_str = g_strdup_printf ("SELECT recordid from oldrecorded "
   764                     "WHERE chanid= \"%d\" AND starttime=\"%s\";",
   765                     channel_id, startts_str);
   766 
   767     mysql_res = gmyth_query_process_statement(scheduler->msqlquery, query_str);
   768     g_free (query_str);
   769 
   770     if (mysql_res) {
   771         MYSQL_ROW msql_row = mysql_fetch_row(mysql_res);
   772         if (msql_row) {
   773             sched_id = g_ascii_strtoull (msql_row[0], NULL, 10);
   774             if ((sched_id > 0) && (reactivate)) {
   775                 query_str = g_strdup_printf ("UPDATE oldrecorded SET reactivate = 1 "
   776                     "WHERE chanid= \"%d\" AND starttime=\"%s\";",
   777                     channel_id, startts_str);
   778                 update_backend (scheduler, 0);
   779             }
   780         }
   781     }
   782 
   783     return sched_id;
   784 }
   785 
   786 /** Queries if the given schedule info is currently been recorded by any 
   787  * backend recorder.
   788  * @return The recorder num currently recording the scheduled item, or -1
   789  * if program is not been recorded.
   790  */
   791 static gint
   792 gmyth_scheduler_is_program_live_recorded (GMythScheduler * scheduler,
   793                                          ScheduleInfo *schedule_info,
   794                                          GMythProgramInfo **prog_info)
   795 {
   796    GList *list; 
   797    gint recorder_num = -1;
   798 
   799    if (gmyth_remote_util_get_recorder_list (scheduler->backend_info, &list) > 0) {
   800         GList *iter = list;
   801 	while (iter) {
   802 	    GMythRecorder *recorder = GMYTH_RECORDER ( iter->data );
   803 	    
   804             if (gmyth_recorder_is_recording (recorder)) {
   805                 GMythProgramInfo *pinfo = gmyth_recorder_get_current_program_info (recorder);
   806 		if (pinfo != NULL) {
   807 		    if ((schedule_info->channel_id == pinfo->channel_id) &&
   808 			(schedule_info->start_time->tv_sec == pinfo->startts->tv_sec)) {
   809 			recorder_num = recorder->recorder_num;
   810                         *prog_info = pinfo;
   811 			break;
   812 		    }
   813 		    g_object_unref (pinfo);
   814 		}
   815 	    }
   816 
   817 	    iter = iter->next;
   818 	}
   819 
   820 	g_list_foreach (list, (GFunc) g_object_unref, NULL);
   821 	g_list_free (list);
   822    }
   823 
   824    return recorder_num;
   825 
   826 }
   827 
   828 static gboolean
   829 gmyth_scheduler_set_live_record (GMythScheduler * scheduler,
   830                                 ScheduleInfo * schedule_info,
   831 				gint recorder_num, gboolean enable)
   832 {
   833     gchar* datastr;
   834     GMythSocket *socket;
   835     gboolean ret = FALSE;
   836     GMythStringList *strlist = gmyth_string_list_new();
   837 
   838     datastr = g_strdup_printf ("QUERY_RECORDER %d", recorder_num);
   839     gmyth_string_list_append_char_array (strlist, datastr);
   840     gmyth_string_list_append_char_array (strlist, "SET_LIVE_RECORDING");
   841     gmyth_string_list_append_int (strlist, enable ? 1 : 0);
   842 
   843     socket = gmyth_backend_info_get_connected_socket (scheduler->backend_info);
   844     if (socket != NULL) {
   845         ret = (gmyth_socket_sendreceive_stringlist(socket, strlist) > 0);
   846         g_object_unref (socket);
   847     } else {
   848         g_warning("[%s] Connection to backend failed!", __FUNCTION__);
   849     }
   850 
   851     g_free(datastr);
   852     g_object_unref(strlist);
   853     return ret;
   854 }
   855 
   856 static gboolean
   857 gmyth_scheduler_change_record_group (GMythScheduler* scheduler,
   858                                      GMythProgramInfo *prog_info,
   859                                      gchar *new_group)
   860 
   861 {
   862     gchar *query_str = NULL;
   863     gchar* startts = NULL;
   864 
   865     if (prog_info == NULL) {
   866         g_warning ("Scheduler receiver a NULL program info to change its record group");
   867         return FALSE;
   868     }
   869 
   870     startts = gmyth_util_time_to_string_from_time_val (prog_info->recstartts);
   871 
   872     query_str = g_strdup_printf("UPDATE recorded SET recgroup = \"%s\" "
   873                     "WHERE chanid = \"%d\" AND starttime = \"%s\";",
   874                      new_group, prog_info->channel_id, startts);
   875 
   876     gmyth_query_process_statement(scheduler->msqlquery, query_str);
   877 
   878     g_free (query_str);
   879     g_free (startts);
   880     
   881     return TRUE ;
   882 }
   883 
   884 
   885 
   886 /** Requests the Mysql database in the backend to remove an existing schedule.
   887  * 
   888  * @param scheduler the GMythScheduler instance.
   889  * @param schedule_id The schedule's record id to be removed
   890  * @return gboolean TRUE if success, FALSE if error
   891  */
   892 gboolean
   893 gmyth_scheduler_delete_schedule (GMythScheduler * scheduler, gint schedule_id)
   894 {
   895     MYSQL_RES *msql_res;
   896     GString *query_str = NULL;
   897 
   898     g_return_val_if_fail (scheduler != NULL, FALSE);
   899 
   900     if (scheduler->msqlquery == NULL) {
   901         g_warning("[%s] Scheduler db connection not initialized",
   902                   __FUNCTION__);
   903         return FALSE;
   904     }
   905 
   906     {
   907         /* Before adding the schedule, we need to check if program is been played */
   908         GMythProgramInfo *prog_info = NULL;
   909         ScheduleInfo *sched_info = NULL;
   910 
   911         sched_info = gmyth_scheduler_get_schedule (scheduler, schedule_id);
   912         if (sched_info != NULL) {
   913             gint recorder_num =
   914                 gmyth_scheduler_is_program_live_recorded (scheduler, sched_info,
   915                                                           &prog_info);
   916             if ((recorder_num > 0) && (prog_info != NULL)) {
   917 		gboolean ret;
   918                 gmyth_scheduler_change_record_group (scheduler, prog_info, "LiveTV");
   919 
   920                 ret = gmyth_scheduler_set_live_record (scheduler, sched_info,
   921                             recorder_num, FALSE);
   922                 gmyth_schedule_info_free (sched_info);
   923 		return ret;		
   924             } 
   925 	}
   926 			    
   927     }
   928 
   929     query_str = g_string_new("");
   930     g_string_printf(query_str,
   931                     "DELETE FROM record WHERE recordid=%d", schedule_id);
   932 
   933     msql_res =
   934         gmyth_query_process_statement(scheduler->msqlquery,
   935                                       query_str->str);
   936 
   937 
   938     mysql_free_result(msql_res);
   939     g_string_free(query_str, TRUE);
   940 
   941     // Notify the backend of the changes
   942     return update_backend(scheduler, schedule_id);
   943 }
   944 
   945 /*
   946  * Add an exception program to be removed from the schedule list, when programs
   947  * where scheduled with the GMYTH_SCHEDULE_ALL_OCCURRENCES option.
   948  * @param scheduler the GMythScheduler instance.
   949  * @param schedule_id the schedule id of the all occurrence schedule to be changed
   950  * @param exception_info the ScheduleInfo to be removed from all schedule occurrences
   951  * @return TRUE if success, FALSE if any error happens
   952  */
   953 gboolean
   954 gmyth_scheduler_add_exception (GMythScheduler *scheduler, gint schedule_id,
   955 			       ScheduleInfo *exception_info)
   956 {
   957     gboolean res;
   958 
   959     g_return_val_if_fail (scheduler != NULL, FALSE);
   960     g_return_val_if_fail (exception_info != NULL, FALSE);
   961 
   962     exception_info->parentid = schedule_id;
   963     res = gmyth_scheduler_add_schedule_full (scheduler, exception_info, GMYTH_SCHEDULE_EXCEPTION);
   964 
   965     return res;
   966 }
   967 
   968 /** Requests the Mysql database in the backend to remove an existing recorded item.
   969  * 
   970  * @param scheduler the GMythScheduler instance.
   971  * @param record_id The recorded item id to be removed
   972  * @return gboolean TRUE if success, FALSE if error
   973  */
   974 gboolean
   975 gmyth_scheduler_delete_recorded(GMythScheduler * scheduler, gint record_id)
   976 {
   977 
   978     MYSQL_RES      *msql_res;
   979 
   980     GString        *query_str = g_string_new("");
   981 
   982     assert(scheduler);
   983 
   984     if (scheduler->msqlquery == NULL) {
   985         g_warning("[%s] Scheduler db connection not initialized",
   986                   __FUNCTION__);
   987         return FALSE;
   988     }
   989     // ========================================
   990     g_string_printf(query_str,
   991                     "DELETE FROM recorded WHERE recordid=%d", record_id);
   992 
   993     // FIXME: Mythtv implementation runs also: DELETE FROM oldfind WHERE
   994     // recordid = x
   995 
   996     msql_res =
   997         gmyth_query_process_statement(scheduler->msqlquery,
   998                                       query_str->str);
   999 
  1000     mysql_free_result(msql_res);
  1001     g_string_free(query_str, TRUE);
  1002 
  1003     // Notify the backend of the changes
  1004     return update_backend(scheduler, record_id);
  1005 }
  1006 
  1007 /** Retrieves an existing recorded item information from database. The information
  1008  * is used to fill the returned GMythProgramInfo.
  1009  * 
  1010  * @param scheduler The GMythScheduler instance.
  1011  * @param channel The channel associated to the record
  1012  * @param starttime The record start time
  1013  * @return A GMythProgramInfo struct with the requested record item
  1014  * information, or NULL if error.
  1015  */
  1016 GMythProgramInfo *
  1017 gmyth_scheduler_get_recorded(GMythScheduler * scheduler,
  1018                              GString * channel, GTimeVal * starttime)
  1019 {
  1020     MYSQL_RES      *msql_res;
  1021     GMythProgramInfo *proginfo = NULL;
  1022     GString        *query_str = g_string_new("");
  1023     gchar          *time_str =
  1024         gmyth_util_time_to_string_from_time_val(starttime);
  1025 
  1026     assert(scheduler);
  1027 
  1028     gmyth_debug("[%s] channel: %s", __FUNCTION__, channel->str);
  1029 
  1030     if (scheduler->msqlquery == NULL) {
  1031         g_warning("[%s] Scheduler db connection not initialized",
  1032                   __FUNCTION__);
  1033         return NULL;
  1034     }
  1035 
  1036     g_string_printf(query_str,
  1037                     "SELECT recorded.chanid,starttime,endtime,title, "
  1038                     "subtitle,description,channel.channum, "
  1039                     "channel.callsign,channel.name,channel.commfree, "
  1040                     "channel.outputfilters,seriesid,programid,filesize, "
  1041                     "lastmodified,stars,previouslyshown,originalairdate, "
  1042                     "hostname,recordid,transcoder,playgroup, "
  1043                     "recorded.recpriority,progstart,progend,basename,recgroup "
  1044                     "FROM recorded " "LEFT JOIN channel "
  1045                     "ON recorded.chanid = channel.chanid "
  1046                     "WHERE recorded.chanid = \"%s\" "
  1047                     "AND starttime = \"%s\" ;", channel->str, time_str);
  1048 
  1049     msql_res =
  1050         gmyth_query_process_statement(scheduler->msqlquery,
  1051                                       query_str->str);
  1052 
  1053     if (msql_res /* && query.size() > 0 */ ) {
  1054         MYSQL_ROW       msql_row = mysql_fetch_row(msql_res);
  1055 
  1056         if (msql_row) {
  1057             proginfo = gmyth_program_info_new();
  1058 
  1059             proginfo->channel_id = (gint) g_ascii_strtoull (msql_row[0], NULL, 10);
  1060             proginfo->startts =
  1061                 gmyth_util_string_to_time_val(msql_row[23]);
  1062             proginfo->endts = gmyth_util_string_to_time_val(msql_row[24]);
  1063             proginfo->recstartts =
  1064                 gmyth_util_string_to_time_val(msql_row[1]);
  1065             proginfo->recendts =
  1066                 gmyth_util_string_to_time_val(msql_row[2]);
  1067             proginfo->title = g_string_new(msql_row[3]);
  1068             proginfo->subtitle = g_string_new(msql_row[4]);
  1069             proginfo->description = g_string_new(msql_row[5]);
  1070 
  1071             proginfo->chanstr = g_string_new(msql_row[6]);
  1072             proginfo->chansign = g_string_new(msql_row[7]);
  1073             proginfo->channame = g_string_new(msql_row[0]);
  1074             proginfo->chancommfree =
  1075                 (gint) g_ascii_strtoull(msql_row[9], NULL, 10);
  1076             proginfo->chanOutputFilters = g_string_new(msql_row[10]);
  1077             proginfo->seriesid = g_string_new(msql_row[11]);
  1078             proginfo->program_id = g_string_new(msql_row[12]);
  1079             proginfo->filesize = g_ascii_strtoull(msql_row[13], NULL, 10);
  1080 
  1081             proginfo->lastmodified =
  1082                 gmyth_util_string_to_time_val(msql_row[14]);
  1083             proginfo->stars = g_ascii_strtod(msql_row[15], NULL);
  1084             proginfo->repeat =
  1085                 (gint) g_ascii_strtoull(msql_row[16], NULL, 10);
  1086 
  1087             if (msql_row[17] == NULL) {
  1088                 proginfo->originalAirDate = 0;
  1089                 proginfo->hasAirDate = FALSE;
  1090             } else {
  1091                 proginfo->originalAirDate =
  1092                     gmyth_util_string_to_time_val(msql_row[17]);
  1093                 proginfo->hasAirDate = TRUE;
  1094             }
  1095 
  1096             proginfo->hostname = g_string_new(msql_row[18]);
  1097             proginfo->recordid =
  1098                 (gint) g_ascii_strtoull(msql_row[19], NULL, 10);
  1099             proginfo->transcoder =
  1100                 (gint) g_ascii_strtoull(msql_row[20], NULL, 10);
  1101             // proginfo->spread = -1;
  1102             // proginfo->programflags = proginfo->getProgramFlags();
  1103 
  1104             proginfo->recgroup = g_string_new(msql_row[26]);
  1105             proginfo->playgroup = g_string_new(msql_row[21]);
  1106             proginfo->recpriority =
  1107                 (gint) g_ascii_strtoull(msql_row[22], NULL, 10);
  1108 
  1109             proginfo->pathname = g_string_new(g_strdup(msql_row[25]));
  1110 
  1111             gmyth_debug("One program info loaded from mysql database\n");
  1112         }
  1113     }
  1114 
  1115     mysql_free_result(msql_res);
  1116     g_string_free(query_str, TRUE);
  1117     g_free(time_str);
  1118 
  1119     return proginfo;
  1120 }
  1121 
  1122 
  1123 /** Notifies the backend of an update in the db.
  1124  * 
  1125  * @param record_id the id of the modified recording.
  1126  */
  1127 // fixme: put void and discovery record_id inside
  1128 static gboolean
  1129 update_backend (GMythScheduler * scheduler, 
  1130                 gint record_id)  
  1131 {
  1132     GMythSocket    *socket;
  1133     GMythStringList *strlist = gmyth_string_list_new();
  1134     GString        *datastr = g_string_new("RESCHEDULE_RECORDINGS ");
  1135     gboolean        ret = FALSE;
  1136 
  1137     g_string_append_printf(datastr, "%d", record_id);
  1138     gmyth_string_list_append_string(strlist, datastr);
  1139 
  1140     socket = gmyth_backend_info_get_connected_socket (scheduler->backend_info);
  1141     if (socket != NULL) { 
  1142         ret = (gmyth_socket_sendreceive_stringlist(socket, strlist) > 0);
  1143         g_object_unref (socket);
  1144     } else {
  1145         g_warning("[%s] Connection to backend failed!", __FUNCTION__);
  1146     }
  1147 
  1148     g_string_free(datastr, TRUE);
  1149     g_object_unref(strlist);
  1150     return ret;
  1151 }
  1152 
  1153 void
  1154 gmyth_scheduler_recorded_info_get_preview(RecordedInfo * info,
  1155                                           GByteArray * data)
  1156 {
  1157 }
  1158 
  1159 void
  1160 gmyth_recorded_info_free(RecordedInfo * info)
  1161 {
  1162     g_return_if_fail (info != NULL);
  1163 
  1164     if (info->program_id)
  1165         g_string_free (info->program_id, TRUE);
  1166 
  1167     if (info->title != NULL)
  1168         g_string_free(info->title, TRUE);
  1169 
  1170     if (info->subtitle != NULL)
  1171         g_string_free(info->subtitle, TRUE);
  1172 
  1173     if (info->description != NULL)
  1174         g_string_free(info->description, TRUE);
  1175 
  1176     if (info->category != NULL)
  1177         g_string_free(info->category, TRUE);
  1178 
  1179     if (info->basename != NULL)
  1180         g_string_free(info->basename, TRUE);
  1181 
  1182     if (info != NULL)
  1183         g_free(info->start_time);
  1184 
  1185     if (info != NULL)
  1186         g_free(info->end_time);
  1187 
  1188     g_free(info);
  1189 }
  1190 
  1191 static void
  1192 free_recorded_info_item(gpointer data, gpointer user_data)
  1193 {
  1194     RecordedInfo   *info = (RecordedInfo *) data;
  1195 
  1196     gmyth_recorded_info_free(info);
  1197 }
  1198 
  1199 void
  1200 gmyth_recorded_info_list_free(GList * list)
  1201 {
  1202     g_return_if_fail(list != NULL);
  1203 
  1204     g_list_foreach(list, free_recorded_info_item, NULL);
  1205     g_list_free(list);
  1206 }
  1207 
  1208 void
  1209 gmyth_schedule_info_free(ScheduleInfo * info)
  1210 {
  1211 
  1212     g_return_if_fail(info != NULL);
  1213 
  1214     if (info->program_id)
  1215         g_string_free (info->program_id, TRUE);
  1216 
  1217     if (info->title != NULL)
  1218         g_string_free(info->title, TRUE);
  1219 
  1220     if (info->subtitle != NULL)
  1221         g_string_free(info->subtitle, TRUE);
  1222 
  1223     if (info->description != NULL)
  1224         g_string_free(info->description, TRUE);
  1225 
  1226     if (info->category != NULL)
  1227         g_string_free(info->category, TRUE);
  1228 
  1229     if (info != NULL)
  1230         g_free(info->start_time);
  1231 
  1232     if (info != NULL)
  1233         g_free(info->end_time);
  1234 
  1235     g_free(info);
  1236 }
  1237 
  1238 static void
  1239 free_schedule_info_item(gpointer data, gpointer user_data)
  1240 {
  1241     ScheduleInfo   *info = (ScheduleInfo *) data;
  1242 
  1243     gmyth_schedule_info_free(info);
  1244 }
  1245 
  1246 void
  1247 gmyth_schedule_info_list_free(GList * list)
  1248 {
  1249     g_return_if_fail(list != NULL);
  1250 
  1251     g_list_foreach(list, free_schedule_info_item, NULL);
  1252     g_list_free(list);
  1253 }