gmyth/gmyth/gmyth_epg.c
author melunko
Thu Mar 13 16:29:38 2008 +0000 (2008-03-13)
branchtrunk
changeset 942 c93bfa74c71f
parent 925 4a8d56080089
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_epg.c
     5  * 
     6  * @brief <p> GMythEPG class provides access to the program and channel data
     7  * from the Electronic Program Guide (EPG) of the Mythtv backend.
     8  *
     9  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    10  * @author Leonardo Sobral Cunha <leonardo.cunha@indt.org.br>
    11  * 
    12  * This program is free software; you can redistribute it and/or modify
    13  * it under the terms of the GNU Lesser General Public License as published by
    14  * the Free Software Foundation; either version 2 of the License, or
    15  * (at your option) any later version.
    16  *
    17  * This program is distributed in the hope that it will be useful,
    18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    20  * GNU General Public License for more details.
    21  *
    22  * You should have received a copy of the GNU Lesser General Public License
    23  * along with this program; if not, write to the Free Software
    24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    25  */
    26 
    27 #ifdef HAVE_CONFIG_H
    28 #include "config.h"
    29 #endif
    30 
    31 #include <mysql/mysql.h>
    32 #include <stdlib.h>
    33 #include <string.h>
    34 #include <assert.h>
    35 
    36 #include "gmyth_epg.h"
    37 #include "gmyth_programinfo.h"
    38 #include "gmyth_util.h"
    39 #include "gmyth_file_transfer.h"
    40 #include "gmyth_debug.h"
    41 
    42 #define CONNECT_TIMEOUT 6
    43 
    44 static void     gmyth_epg_class_init(GMythEPGClass * klass);
    45 static void     gmyth_epg_init(GMythEPG * object);
    46 
    47 static void     gmyth_epg_dispose(GObject * object);
    48 static void     gmyth_epg_finalize(GObject * object);
    49 
    50 G_DEFINE_TYPE(GMythEPG, gmyth_epg, G_TYPE_OBJECT)
    51     static void     gmyth_epg_class_init(GMythEPGClass * klass)
    52 {
    53     GObjectClass   *gobject_class = G_OBJECT_CLASS(klass);
    54 
    55     gobject_class->dispose = gmyth_epg_dispose;
    56     gobject_class->finalize = gmyth_epg_finalize;
    57 }
    58 
    59 static void
    60 gmyth_epg_init(GMythEPG * gmyth_epg)
    61 {
    62 
    63 }
    64 
    65 static void
    66 gmyth_epg_dispose(GObject * object)
    67 {
    68     GMythEPG       *gmyth_epg = GMYTH_EPG(object);
    69 
    70     if (gmyth_epg->sqlquery != NULL) {
    71         g_object_unref(gmyth_epg->sqlquery);
    72         gmyth_epg->sqlquery = NULL;
    73     }
    74 
    75     G_OBJECT_CLASS(gmyth_epg_parent_class)->dispose(object);
    76 }
    77 
    78 static void
    79 gmyth_epg_finalize(GObject * object)
    80 {
    81     g_signal_handlers_destroy(object);
    82 
    83     G_OBJECT_CLASS(gmyth_epg_parent_class)->finalize(object);
    84 }
    85 
    86 /**
    87  * Creates a new instance of GMythEPG.
    88  * 
    89  * @return a new instance of GMythEPG.
    90  */
    91 GMythEPG       *
    92 gmyth_epg_new(void)
    93 {
    94     GMythEPG       *epg = GMYTH_EPG(g_object_new(GMYTH_EPG_TYPE, NULL));
    95 
    96     return epg;
    97 }
    98 
    99 /** Connects to the Mysql database in the backend. The backend address
   100  * is loaded from the GMythSettings instance.
   101  *
   102  * @param gmyth_epg the GMythEPG instance to be connected.
   103  * @return true if connection was success, false if failed.
   104  */
   105 gboolean
   106 gmyth_epg_connect(GMythEPG * gmyth_epg, GMythBackendInfo * backend_info)
   107 {
   108     g_return_val_if_fail(gmyth_epg != NULL, FALSE);
   109 
   110     if (gmyth_epg->sqlquery == NULL) {
   111         gmyth_debug("[%s] Creating gmyth_query", __FUNCTION__);
   112         gmyth_epg->sqlquery = gmyth_query_new();
   113     }
   114 
   115     if (!gmyth_query_connect_with_timeout(gmyth_epg->sqlquery,
   116                                           backend_info, CONNECT_TIMEOUT)) {
   117         gmyth_debug("[%s] Error while connecting to db", __FUNCTION__);
   118         return FALSE;
   119     }
   120 
   121     gmyth_epg->backend_info = backend_info;
   122     g_object_ref(backend_info);
   123 
   124     return TRUE;
   125 }
   126 
   127 gboolean
   128 gmyth_epg_is_connected (GMythEPG *gmyth_epg)
   129 {
   130     g_return_val_if_fail (gmyth_epg != NULL, FALSE);
   131 
   132     return gmyth_query_is_alive (gmyth_epg->sqlquery);
   133 }
   134 
   135 /** Disconnects from the Mysql database in the backend.
   136  *
   137  * @param gmyth_epg the GMythEPG instance to be disconnected
   138  * @return true if disconnection was success, false if failed.
   139  */
   140 gboolean
   141 gmyth_epg_disconnect(GMythEPG * gmyth_epg)
   142 {
   143     g_return_val_if_fail(gmyth_epg != NULL, FALSE);
   144 
   145     if (gmyth_epg->sqlquery != NULL) {
   146         gmyth_query_disconnect(gmyth_epg->sqlquery);
   147         g_object_unref(gmyth_epg->sqlquery);
   148         gmyth_epg->sqlquery = NULL;
   149     }
   150 
   151     if (gmyth_epg->backend_info != NULL) {
   152         g_object_unref(gmyth_epg->backend_info);
   153         gmyth_epg->backend_info = NULL;
   154     }
   155 
   156     return TRUE;
   157 }
   158 
   159 /** Retrieves the available list of channels from the backend Mysql database.
   160  * 
   161  * @param gmyth_epg the GMythEPG instance.
   162  * @param glist_ptr the GSList pointer to be filled with the loaded list address.
   163  * @return The amount of channels retrieved from database,  or -1 if error.
   164  */
   165 gint
   166 gmyth_epg_get_channel_list(GMythEPG * gmyth_epg, GList ** glist_ptr)
   167 {
   168     MYSQL_RES      *msql_res;
   169 
   170     g_return_val_if_fail(gmyth_epg != NULL, -1);
   171 
   172     msql_res = gmyth_query_process_statement(gmyth_epg->sqlquery,
   173                                              "SELECT chanid, channum, name, icon FROM channel;");
   174 
   175     (*glist_ptr) = NULL;
   176 
   177     if (msql_res == NULL) {
   178         gmyth_debug("[%s] msql query returned NULL MYSQL_RES",
   179                     __FUNCTION__);
   180         return -1;
   181     } else {
   182         MYSQL_ROW       row;
   183         GMythChannelInfo *channel_info;
   184 
   185         while ((row = mysql_fetch_row(msql_res)) != NULL) {
   186 
   187             channel_info = g_new0(GMythChannelInfo, 1);
   188             channel_info->channel_ID =
   189                 (gint) g_ascii_strtoull(row[0], NULL, 10);
   190             channel_info->channel_num = g_string_new(row[1]);
   191             channel_info->channel_name = g_string_new(row[2]);
   192             channel_info->channel_icon = g_string_new(row[3]);
   193 #ifdef GMYTH_USE_DEBUG
   194             gmyth_channel_info_print(channel_info);
   195 #endif
   196             (*glist_ptr) = g_list_append((*glist_ptr), channel_info);
   197         }
   198     }
   199     mysql_free_result(msql_res);
   200 
   201     return (!(*glist_ptr)) ? 0 : g_list_length(*glist_ptr);
   202 }
   203 
   204 GMythChannelInfo *
   205 gmyth_epg_get_channel_info(GMythEPG * gmyth_epg, gint channel_id)
   206 {
   207     GMythChannelInfo *channel_info = NULL;
   208     MYSQL_RES      *msql_res;
   209     gchar          *query_str;
   210 
   211     g_return_val_if_fail(gmyth_epg != NULL, NULL);
   212 
   213     query_str =
   214         g_strdup_printf
   215         ("SELECT channum, name, icon FROM channel WHERE chanid=%d;",
   216          channel_id);
   217     msql_res =
   218         gmyth_query_process_statement(gmyth_epg->sqlquery, query_str);
   219 
   220     if (msql_res == NULL) {
   221         gmyth_debug("[%s] msql query returned NULL MYSQL_RES",
   222                     __FUNCTION__);
   223         return NULL;
   224     } else {
   225         MYSQL_ROW       row;
   226 
   227         if ((row = mysql_fetch_row(msql_res)) != NULL) {
   228 
   229             channel_info = g_new0(GMythChannelInfo, 1);
   230             channel_info->channel_ID = channel_id;
   231             channel_info->channel_num = g_string_new(row[0]);
   232             channel_info->channel_name = g_string_new(row[1]);
   233             channel_info->channel_icon = g_string_new(row[2]);
   234 #ifdef GMYTH_USE_DEBUG
   235             gmyth_channel_info_print(channel_info);
   236 #endif
   237         }
   238     }
   239     mysql_free_result(msql_res);
   240 
   241     return channel_info;
   242 }
   243 
   244 /** 
   245  * Retrieves the available list of channels from the backend Mysql database.
   246  * 
   247  * @param gmyth_epg the GMythEPG instance.
   248  * @param proglist the GSList pointer to be filled with the loaded list.
   249  * @param chan_num the channel num on which to search for program.
   250  * @param starttime the start time to search for programs.
   251  * @param endtime the end time to search for programs.
   252  * @return The amount of channels retrieved from database, or -1 if error.
   253  */
   254 gint
   255 gmyth_epg_get_program_list(GMythEPG * gmyth_epg, GList ** proglist,
   256                            const gint chan_num, GTimeVal * starttime,
   257                            GTimeVal * endtime)
   258 {
   259 
   260     gchar          *startts =
   261         gmyth_util_time_to_string_from_time_val(starttime);
   262     gchar          *endts =
   263         gmyth_util_time_to_string_from_time_val(endtime);
   264     MYSQL_ROW       row;
   265     GString        *querystr;
   266 
   267     assert(gmyth_epg);
   268 
   269     querystr =
   270         g_string_new
   271         ("SELECT DISTINCT program.chanid, program.starttime, program.endtime, "
   272          "    program.title, program.subtitle, program.description, "
   273          "    program.category, channel.channum, channel.callsign, "
   274          "    channel.name, program.previouslyshown, channel.commfree, "
   275          "    channel.outputfilters, program.seriesid, program.programid, "
   276          "    program.airdate, program.stars, program.originalairdate, "
   277          "    program.category_type, record.recordid, "
   278          "    oldrecstatus.rectype, oldrecstatus.recstatus, "
   279          "    oldrecstatus.findid "
   280          "FROM program "
   281          "LEFT JOIN channel ON program.chanid = channel.chanid "
   282          "LEFT JOIN record ON "
   283          "    program.chanid = record.chanid AND "
   284          "    DATE (program.starttime) = record.startdate AND "
   285          "    TIME (program.starttime) = record.starttime AND "
   286          "    DATE (program.endtime) = record.enddate AND "
   287          "    TIME (program.endtime) = record.endtime "
   288          "LEFT JOIN oldrecorded AS oldrecstatus ON "
   289          "    program.title = oldrecstatus.title AND "
   290          "    channel.callsign = oldrecstatus.station AND "
   291          "    program.starttime = oldrecstatus.starttime ");
   292 
   293     g_string_append_printf(querystr,
   294                            "WHERE program.chanid = %d "
   295                            "  AND program.endtime >= '%s' "
   296                            "  AND program.starttime <= '%s' "
   297                            "  AND program.manualid = 0 ", chan_num,
   298                            startts, endts);
   299 
   300     if (!g_strrstr(querystr->str, " GROUP BY "))
   301         querystr = g_string_append(querystr,
   302                                    " GROUP BY program.starttime, channel.channum, "
   303                                    "  channel.callsign, program.title ");
   304 
   305     if (!g_strrstr(querystr->str, " LIMIT "))
   306         querystr = g_string_append(querystr, " LIMIT 1000 ");
   307 
   308     MYSQL_RES      *res_set =
   309         gmyth_query_process_statement(gmyth_epg->sqlquery, querystr->str);
   310 
   311     if (res_set == NULL) {
   312         gmyth_debug("[%s] msql query returned NULL MYSQL_RES",
   313                     __FUNCTION__);
   314         return -1;
   315     }
   316 
   317     *proglist = NULL;
   318     while ((row = mysql_fetch_row(res_set)) != NULL) {
   319 
   320         GMythProgramInfo *p = gmyth_program_info_new();
   321 
   322         p->channel_id = (int) g_ascii_strtoull (row[0], NULL, 10);
   323 
   324         p->startts = gmyth_util_string_to_time_val(row[1]);
   325         p->endts = gmyth_util_string_to_time_val(row[2]);
   326 
   327         p->recstartts = g_new0(GTimeVal, 1);
   328         p->recstartts->tv_sec = p->startts->tv_sec;
   329         p->recstartts->tv_usec = p->startts->tv_usec;
   330 
   331         p->recendts = g_new0(GTimeVal, 1);
   332         p->recendts->tv_sec = p->endts->tv_sec;
   333         p->recendts->tv_usec = p->endts->tv_usec;
   334 
   335         p->lastmodified = g_new0(GTimeVal, 1);
   336         p->lastmodified->tv_sec = p->startts->tv_sec;
   337         p->lastmodified->tv_usec = p->startts->tv_usec;
   338 
   339 
   340         p->title = g_string_new(row[3]);
   341         p->subtitle = g_string_new(row[4]);
   342         p->description = g_string_new(row[5]);
   343         p->category = g_string_new(row[6]);
   344         p->chanstr = g_string_new(row[7]);
   345         p->chansign = g_string_new(row[8]);
   346         p->channame = g_string_new(row[9]);
   347         p->repeat = g_ascii_strtoull(row[10], NULL, 10);
   348         p->chancommfree = g_ascii_strtoull(row[11], NULL, 10);
   349         p->chanOutputFilters = g_string_new(row[12]);
   350         p->seriesid = g_string_new(row[13]);
   351         p->program_id = g_string_new(row[14]);
   352         p->year = g_string_new(row[15]);
   353         p->stars = g_ascii_strtod(row[16], NULL);
   354 
   355         if (!row[17] || !strcmp(row[17], "")) {
   356             p->originalAirDate = 0;
   357             p->hasAirDate = FALSE;
   358         } else {
   359             p->originalAirDate = gmyth_util_string_to_time_val(row[17]);
   360             p->hasAirDate = TRUE;
   361         }
   362 
   363         p->catType = g_string_new(row[18]);
   364         if (row[19] != NULL)
   365             p->recordid =  g_ascii_strtoull(row[19], NULL, 10);
   366 
   367         *proglist = g_list_append(*proglist, p);
   368 
   369 #ifdef GMYTH_USE_DEBUG
   370         gmyth_program_info_print(p);
   371 #endif
   372     }
   373 
   374     /*
   375      * deallocate 
   376      */
   377     mysql_free_result(res_set);
   378     g_string_free(querystr, TRUE);
   379 
   380     return g_list_length (*proglist);
   381 }
   382 
   383 gboolean
   384 gmyth_epg_channel_has_icon(GMythEPG * gmyth_epg,
   385                            GMythChannelInfo * channel_info)
   386 {
   387     gboolean        res = FALSE;
   388 
   389     g_return_val_if_fail(gmyth_epg != NULL, FALSE);
   390     g_return_val_if_fail(channel_info != NULL, FALSE);
   391 
   392     if (channel_info->channel_icon != NULL) {
   393         res = gmyth_util_file_exists(gmyth_epg->backend_info,
   394                                      channel_info->channel_icon->str);
   395     }
   396 
   397     return res;
   398 
   399 }
   400 
   401 /**
   402  * 
   403  * @param data the data pointer to be filled with icon binary data. It must be freed by the calling function.
   404  * @return TRUE if success, FALSE if any error happens.
   405  */
   406 gboolean
   407 gmyth_epg_channel_get_icon(GMythEPG * gmyth_epg,
   408                            GMythChannelInfo * channel_info, guint8 ** data,
   409                            guint * length)
   410 {
   411     gboolean res = FALSE;
   412 
   413     g_return_val_if_fail(gmyth_epg != NULL, FALSE);
   414     g_return_val_if_fail(channel_info != NULL, FALSE);
   415 
   416     if (gmyth_epg_channel_has_icon(gmyth_epg, channel_info)) {
   417         GMythFileTransfer *transfer =
   418             gmyth_file_transfer_new(gmyth_epg->backend_info);
   419 
   420         GMythFileReadResult gmyth_res;
   421         GByteArray *icon_data;
   422         guint64 icon_length = 0;
   423 
   424         res = gmyth_file_transfer_open(transfer,
   425                                        channel_info->channel_icon->str);
   426         if (!res) {
   427             gmyth_debug("Channel icon could not be opened");
   428             return FALSE;
   429         }
   430 
   431         icon_length = gmyth_file_transfer_get_filesize(transfer);
   432         if (icon_length <= 0) {
   433             gmyth_debug("Channel icon file size is zero or negative");
   434             return FALSE;
   435         }
   436 
   437         icon_data = g_byte_array_new();
   438         gmyth_res = gmyth_file_transfer_read(transfer,
   439                                              icon_data,
   440                                              icon_length, FALSE);
   441         if (gmyth_res == GMYTH_FILE_READ_EOF) {
   442             *length = icon_length;
   443             *data = icon_data->data;
   444             g_byte_array_free(icon_data, FALSE);
   445             res = TRUE;
   446         } else {
   447             *length = 0;
   448             *data = NULL;
   449             g_byte_array_free(icon_data, TRUE);
   450         }
   451     }
   452 
   453     return res;
   454 }