diff -r 000000000000 -r 6d5596b9eb95 branches/gmyth-0.1b/src/gmyth_scheduler.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/branches/gmyth-0.1b/src/gmyth_scheduler.c Wed Feb 14 21:28:49 2007 +0000 @@ -0,0 +1,667 @@ +/** + * GMyth Library + * + * @file gmyth/gmyth_scheduler.c + * + * @brief

The scheduler encapsulates all functions for browsing, scheduling + * and modifying the recorded content. + * + * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia. + * @author Alexsandro Jose Virginio dos Santos + * + *//* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "gmyth_scheduler.h" +#include "gmyth_util.h" +#include "gmyth_query.h" +#include "gmyth_socket.h" +#include "gmyth_debug.h" + +static void gmyth_scheduler_class_init (GMythSchedulerClass *klass); +static void gmyth_scheduler_init (GMythScheduler *object); + +static void gmyth_scheduler_dispose (GObject *object); +static void gmyth_scheduler_finalize (GObject *object); + +static gint get_record_id_from_database (GMythScheduler *scheduler); +static void update_backend (GMythScheduler *scheduler, gint record_id); + +G_DEFINE_TYPE(GMythScheduler, gmyth_scheduler, G_TYPE_OBJECT) + +static void +gmyth_scheduler_class_init (GMythSchedulerClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->dispose = gmyth_scheduler_dispose; + gobject_class->finalize = gmyth_scheduler_finalize; +} + +static void +gmyth_scheduler_init (GMythScheduler *sched) +{ + sched->recordid =0; + sched->type = 0; + sched->search = 0; + sched->profile = g_string_new(""); + + sched->dupin = 0; + sched->dupmethod = 0; + sched->autoexpire = 0; + sched->autotranscode = 0; + sched->transcoder = 0; + + sched->autocommflag = 0; + sched->autouserjob1 = 0; + sched->autouserjob2 = 0; + sched->autouserjob3 = 0; + sched->autouserjob4 = 0; + + sched->startoffset = 0; + sched->endoffset = 0; + sched->maxepisodes = 0; + sched->maxnewest = 0; + + sched->recpriority = 0; + sched->recgroup = 0; + sched->playgroup = 0; + + sched->prefinput = 0; + sched->inactive = 0; + + sched->searchType = g_string_new(""); + sched->searchForWhat = g_string_new(""); + + sched->msqlquery = gmyth_query_new (); +} + +static void +gmyth_scheduler_dispose (GObject *object) +{ + GMythScheduler *scheduler = GMYTH_SCHEDULER (object); + + if (scheduler->backend_info) { + g_object_unref (scheduler->backend_info); + scheduler->backend_info = NULL; + } + + G_OBJECT_CLASS (gmyth_scheduler_parent_class)->dispose (object); +} + +static void +gmyth_scheduler_finalize (GObject *object) +{ + g_signal_handlers_destroy (object); + + G_OBJECT_CLASS (gmyth_scheduler_parent_class)->finalize (object); +} + +/** Creates a new instance of GMythScheduler. + * + * @return a new instance of GMythScheduler. + */ +GMythScheduler* +gmyth_scheduler_new () +{ + GMythScheduler *scheduler = + GMYTH_SCHEDULER (g_object_new(GMYTH_SCHEDULER_TYPE, NULL)); + + return scheduler; +} + +gboolean +gmyth_scheduler_connect (GMythScheduler *scheduler, GMythBackendInfo *backend_info) +{ + return gmyth_scheduler_connect_with_timeout (scheduler, backend_info, 0); +} + +/** Connects to the Mysql database in the backend. The backend address + * is loaded from the GMythSettings instance. + * + * @param scheduler the GMythScheduler instance to be connected. + * @return true if connection was success, false if failed. + */ +gboolean +gmyth_scheduler_connect_with_timeout (GMythScheduler *scheduler, + GMythBackendInfo *backend_info, guint timeout) +{ + assert(scheduler); + g_return_val_if_fail (backend_info != NULL, FALSE); + + g_object_ref (backend_info); + scheduler->backend_info = backend_info; + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] GMythScheduler db initializing", __FUNCTION__); + scheduler->msqlquery = gmyth_query_new (); + } + + if (!gmyth_query_connect_with_timeout (scheduler->msqlquery, + scheduler->backend_info, timeout)) { + g_warning ("[%s] Error while connecting to db", __FUNCTION__); + return FALSE; + } + + return TRUE; +} + +/** Disconnects from the Mysql database in the backend. + * + * @param scheduler the GMythScheduler instance to be disconnected + * @return true if disconnection was success, false if failed. + */ +gboolean +gmyth_scheduler_disconnect (GMythScheduler *scheduler) +{ + assert(scheduler); + + if (scheduler->msqlquery != NULL) { + gmyth_query_disconnect (scheduler->msqlquery); + g_object_unref (scheduler->msqlquery); + } + + return TRUE; +} + +/** Retrieves from the backend Mysql database the list of recording schedules. + * + * @param scheduler The GMythScheduler instance. + * @param schedule_list the GList pointer to be filled with the loaded list of ScheduleInfo items. + * @return The amount of schedules retrieved from database, or -1 if error. + */ +gint +gmyth_scheduler_get_schedule_list ( GMythScheduler *scheduler, GList **schedule_list) +{ + ScheduleInfo *schedule; + MYSQL_RES *msql_res; + GString *query_str = g_string_new (""); + gchar *date_time = NULL; + + assert(scheduler); + + g_string_printf (query_str, + "SELECT recordid,programid,chanid,starttime,startdate," + "endtime,enddate,title,subtitle,description,category FROM record;"); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return -1; + } + msql_res = gmyth_query_process_statement (scheduler->msqlquery, query_str->str); + + if (msql_res == NULL) { + g_warning ("DB retrieval of schedule list failed"); + return -1; + } else { + MYSQL_ROW row; + *schedule_list = NULL; + + while((row = mysql_fetch_row (msql_res)) != NULL) { + schedule = g_new0(ScheduleInfo, 1); + + schedule->record_id = g_ascii_strtoull (row[0], NULL, 10); + schedule->program_id = g_ascii_strtoull (row[1], NULL, 10); + schedule->channel_id = g_ascii_strtoull (row[2], NULL, 10); + + /* generate a time_t from a time and a date db field */ + g_sprintf (date_time, "%sT%s", row[4], row[3]); + + schedule->start_time = gmyth_util_string_to_time_val (date_time); + + /* generate a time_t from a time and a date db field */ + g_sprintf (date_time, "%sT%s", row[6], row[5]); + + schedule->end_time = gmyth_util_string_to_time_val (date_time); + + schedule->title = g_string_new (row[7]); + schedule->subtitle = g_string_new (row[8]); + schedule->description = g_string_new (row[9]); + schedule->category = g_string_new (row[10]); + + (*schedule_list) = g_list_append (*(schedule_list), schedule); + } + } + + mysql_free_result (msql_res); + g_string_free(query_str, TRUE); + g_free(date_time); + + return (*schedule_list == NULL) ? 0 : g_list_length (*schedule_list); +} + +/** Retrieves from the backend Mysql database the list of recorded programs. + * + * @param scheduler The GMythScheduler instance. + * @param recorded_list the GList pointer to be filled with the loaded RecordInfo items. + * @return The amount of recorded retrieved from database, or -1 if error. + */ +gint +gmyth_scheduler_get_recorded_list (GMythScheduler *scheduler, GList **recorded_list) +{ + RecordedInfo *record; + MYSQL_RES *msql_res; + GString *query_str = g_string_new (""); + + assert(scheduler); + + g_string_printf (query_str, + "SELECT recordid,programid,chanid,starttime,progstart," + "endtime,progend,title,subtitle,description,category,filesize,basename FROM recorded WHERE recgroup != 'LiveTV'"); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return -1; + } + + msql_res = gmyth_query_process_statement (scheduler->msqlquery, query_str->str); + + if (msql_res == NULL) { + g_warning ("DB retrieval of recording list failed"); + return -1; + } else { + MYSQL_ROW row; + *recorded_list = NULL; + + while((row = mysql_fetch_row (msql_res))!=NULL){ + record = g_new0(RecordedInfo, 1); + + record->record_id = (guint) g_ascii_strtoull (row[0], NULL, 10); + record->program_id = (guint) g_ascii_strtoull (row[1], NULL, 10); + record->channel_id = (guint) g_ascii_strtoull (row[2], NULL, 10); + + record->start_time = gmyth_util_string_to_time_val (row[3]); + record->end_time = gmyth_util_string_to_time_val (row[5]); + + record->title = g_string_new (row[7]); + record->subtitle = g_string_new (row[8]); + record->description = g_string_new (row[9]); + record->category = g_string_new (row[10]); + record->filesize = g_ascii_strtoull (row[11], NULL, 10); + record->basename = g_string_new (row[12]); + + *recorded_list = g_list_append (*recorded_list, record); + } + } + + mysql_free_result (msql_res); + g_string_free(query_str, TRUE); + + return (*recorded_list == NULL) ? 0 : g_list_length (*recorded_list); +} + +/** Requests the Mysql database in the backend to add a new schedule. + * + * @param scheduler the GMythScheduler instance. + * @param schedule_info the ScheduleInfo with recording schedule information + * to be added. record_id = -1 to add a new schedule, otherwise this + * function will update the schedule in the db + * @return gboolean returns FALSE if some error occurs, TRUE otherwise + */ +gboolean +gmyth_scheduler_add_schedule (GMythScheduler *scheduler, + ScheduleInfo *schedule_info) +{ + //GTimeVal *start_tm; + //GTimeVal *end_tm; + + MYSQL_RES *msql_res; + GString *query_str = g_string_new (""); + + gchar *date_time = NULL; + + assert(scheduler); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return FALSE; + } + + //TODO: verify if this funtion realy does what it should do! + g_string_printf (query_str, "REPLACE INTO record " + "(recordid, type, chanid, starttime, " + "startdate, endtime, enddate, title," + "profile, recpriority, maxnewest, inactive, " + "maxepisodes, autoexpire, startoffset, endoffset, " + "recgroup, dupmethod, dupin, station, " + "autocommflag, findday, findtime, findid, " + "search, autotranscode, transcoder, tsdefault, " + "autouserjob1, autouserjob2, autouserjob3, autouserjob4) " + " values ( %d, 1, %d, \"%s\"," //recordid, type, chanid, starttime + " \"%s\", \"%s\", \"%s\", \"%s\"," + //startdate, endtime, enddate, title + "DEFAULT, 0, 0, 0, " //profile, recpriority, maxnewest, inactive + "0, 1, 0, 0, " //maxepisodes, autoexpire, startoffset, endoffset + "DEFAULT, 6, 15, %d, " //recgroup, dupmethod, dupin, station + "1, %d, \"%s\", %d, " //autocommflag, findday, findtime, findid + "5, 0, 29, 1, " //search, autotranscode, transcoder, tsdefault + "0, 0, 0, 0 );", //autouserjob1, autouserjob2, autouserjob3, autouserjob4 + schedule_info->record_id, schedule_info->channel_id, + gmyth_util_time_to_string_only_time( schedule_info->start_time ), + gmyth_util_time_to_string_only_date( schedule_info->start_time ), + gmyth_util_time_to_string_only_time( schedule_info->end_time ), + gmyth_util_time_to_string_only_date( schedule_info->end_time ), + schedule_info->title->str, //title + schedule_info->channel_id,//station + (gmyth_util_time_val_to_date( schedule_info->start_time ))->tm_wday, //findday + gmyth_util_time_to_string_only_time( schedule_info->start_time ), //findtime + (gint)(schedule_info->start_time->tv_sec/60/60/24 + 719528)//findid + ); + + gmyth_debug ( "Sending query to MySQL = %s.", query_str->str ); + + msql_res = gmyth_query_process_statement (scheduler->msqlquery, query_str->str); + + /* FIXME: currently no way to detect db error in UPDATE, REPLACES! + if (msql_res == NULL) { + g_warning ("DB retrieval of recording list failed"); + return -1; + }*/ + + /* TODO: verify record_id = -1 semantics */ + if (schedule_info->record_id <= 0) + schedule_info->record_id = get_record_id_from_database(scheduler); + + /* Notify the backend of changes */ + update_backend(scheduler, schedule_info->record_id); + + /* free allocated memory */ + mysql_free_result (msql_res); + g_string_free(query_str, TRUE); + + return 1; +} + +/** Requests the Mysql database in the backend to remove an existing schedule. + * + * @param scheduler the GMythScheduler instance. + * @param record_id The schedule's record id to be removed + * @return gboolean TRUE if success, FALSE if error + */ +gboolean +gmyth_scheduler_delete_schedule (GMythScheduler *scheduler, gint record_id) +{ + + MYSQL_RES *msql_res; + GString *query_str = g_string_new (""); + + assert(scheduler); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return FALSE; + } + + //======================================== + g_string_printf (query_str, + "DELETE FROM record WHERE recordid=%d", record_id); + + msql_res = gmyth_query_process_statement (scheduler->msqlquery, query_str->str); + + if (msql_res == NULL) { + g_warning ("[%s] Error while trying to delete a schedule in the database", __FUNCTION__); + return FALSE; + } + + update_backend(scheduler, record_id);// Notify the backend of the changes + + mysql_free_result (msql_res); + g_string_free(query_str, TRUE); + + return TRUE; +} + +/** Requests the Mysql database in the backend to remove an existing recorded item. + * + * @param scheduler the GMythScheduler instance. + * @param record_id The recorded item id to be removed + * @return gboolean TRUE if success, FALSE if error + */ +gboolean +gmyth_scheduler_delete_recorded (GMythScheduler *scheduler, gint record_id) +{ + + MYSQL_RES *msql_res; + + GString *query_str = g_string_new (""); + + assert(scheduler); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return FALSE; + } + + //======================================== + g_string_printf (query_str, + "DELETE FROM recorded WHERE recordid=%d", record_id); + + msql_res = gmyth_query_process_statement (scheduler->msqlquery, query_str->str); + + update_backend(scheduler, record_id);// Notify the backend of the changes + + mysql_free_result (msql_res); + g_string_free(query_str, TRUE); + + return TRUE; +} + +/** Retrieves an existing recorded item information from database. The information + * is used to fill the returned GMythProgramInfo. + * + * @param scheduler The GMythScheduler instance. + * @param channel The channel associated to the record + * @param starttime The record start time + * @return A GMythProgramInfo struct with the requested record item + * information, or NULL if error. + */ +GMythProgramInfo* +gmyth_scheduler_get_recorded (GMythScheduler *scheduler, + GString *channel, GTimeVal* starttime) +{ + MYSQL_RES *msql_res; + GMythProgramInfo *proginfo = NULL; + GString *query_str = g_string_new(""); + gchar *time_str = gmyth_util_time_to_string_from_time_val (starttime); + + assert(scheduler); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return NULL; + } + + g_string_printf (query_str, "SELECT recorded.chanid,starttime,endtime,title, " + "subtitle,description,channel.channum, " + "channel.callsign,channel.name,channel.commfree, " + "channel.outputfilters,seriesid,programid,filesize, " + "lastmodified,stars,previouslyshown,originalairdate, " + "hostname,recordid,transcoder,playgroup, " + "recorded.recpriority,progstart,progend,basename,recgroup " + "FROM recorded " + "LEFT JOIN channel " + "ON recorded.chanid = channel.chanid " + "WHERE recorded.chanid = \"%s\" " + "AND starttime = \"%s\" ;", + channel->str, time_str); + + msql_res = gmyth_query_process_statement (scheduler->msqlquery, query_str->str); + + if (msql_res /*&& query.size() > 0*/) { + + MYSQL_ROW msql_row = mysql_fetch_row (msql_res); + if (msql_row) { + + proginfo = gmyth_program_info_new(); + + proginfo->chanid = g_string_new (msql_row[0]); + proginfo->startts = gmyth_util_string_to_time_val (msql_row[23]); + proginfo->endts = gmyth_util_string_to_time_val (msql_row[24]); + proginfo->recstartts = gmyth_util_string_to_time_val (msql_row[1]); + proginfo->recendts = gmyth_util_string_to_time_val (msql_row[2]); + proginfo->title = g_string_new (msql_row[3]); + proginfo->subtitle = g_string_new (msql_row[4]); + proginfo->description = g_string_new (msql_row[5]); + + proginfo->chanstr = g_string_new (msql_row[6]); + proginfo->chansign = g_string_new (msql_row[7]); + proginfo->channame = g_string_new (msql_row[0]); + proginfo->chancommfree = g_ascii_strtoull (msql_row[9], NULL, 10); + proginfo->chanOutputFilters = g_string_new (msql_row[10]); + proginfo->seriesid = g_string_new (msql_row[11]); + proginfo->programid = g_string_new (msql_row[12]); + proginfo->filesize = g_ascii_strtoull (msql_row[13], NULL, 10); + + proginfo->lastmodified = gmyth_util_string_to_time_val (msql_row[14]); + + proginfo->stars = g_ascii_strtod (msql_row[15], NULL); + proginfo->repeat = g_ascii_strtoull (msql_row[16], NULL, 10); + + if (msql_row[17] == NULL) { + proginfo->originalAirDate = 0; + proginfo->hasAirDate = FALSE; + } else { + proginfo->originalAirDate = gmyth_util_string_to_time_val (msql_row[17]); + proginfo->hasAirDate = TRUE; + } + + proginfo->hostname = g_string_new (msql_row[18]); + proginfo->recordid = g_ascii_strtoull (msql_row[19], NULL, 10); + proginfo->transcoder = g_ascii_strtoull (msql_row[20], NULL, 10); + + //proginfo->spread = -1; + //proginfo->programflags = proginfo->getProgramFlags(); + + proginfo->recgroup = g_string_new (msql_row[26]); + proginfo->playgroup = g_string_new (msql_row[21]); + proginfo->recpriority = g_ascii_strtoull (msql_row[22], NULL, 10); + + proginfo->pathname = g_string_new (msql_row[25]); + + gmyth_debug ("One program info loaded from mysql database\n"); + } + } + + mysql_free_result (msql_res); + g_string_free(query_str, TRUE); + g_free(time_str); + + return proginfo; +} + +/** Retrieves the next record id. + * + * @param scheduler The GMythScheduler instance. + * @return gint record_id if success, -1 otherwise + */ +static gint +get_record_id_from_database (GMythScheduler *scheduler) +{ + gint record_id; + + assert(scheduler); + + if (scheduler->msqlquery == NULL) { + g_warning ("[%s] Scheduler db connection not initialized", __FUNCTION__); + return 0; + } + + record_id = mysql_insert_id (scheduler->msqlquery->conn); + + return record_id; +} + +/** Notifies the backend of an update in the db. + * + * @param record_id the id of the modified recording. + */ +static void +update_backend(GMythScheduler *scheduler, gint record_id)//fixme: put void and discovery record_id inside +{ + GMythSocket *socket; + GMythStringList *strlist = gmyth_string_list_new (); + GString *datastr = g_string_new ("RESCHEDULE_RECORDINGS "); + + g_string_append_printf (datastr, "%d", record_id); + gmyth_string_list_append_string (strlist, datastr); + + socket = gmyth_socket_new (); + if (gmyth_socket_connect (socket, scheduler->backend_info->hostname, + scheduler->backend_info->port)) { + gmyth_socket_sendreceive_stringlist (socket, strlist); + } else { + g_warning ("[%s] Connection to backend failed!", __FUNCTION__); + } + + g_string_free(datastr, TRUE); + g_object_unref(strlist); +} + +void +gmyth_scheduler_recorded_info_get_preview (RecordedInfo *info, GByteArray* data) +{ +} + +void +gmyth_scheduler_recorded_info_free (RecordedInfo *info) +{ + if (info->title != NULL) + g_string_free (info->title, TRUE); + + if (info->subtitle != NULL) + g_string_free (info->subtitle, TRUE); + + if (info->description != NULL) + g_string_free (info->description, TRUE); + + if (info->category != NULL) + g_string_free (info->category, TRUE); + + if (info->basename != NULL) + g_string_free (info->basename, TRUE); + + g_free (info); +} + +void +gmyth_scheduler_schedule_info_free (ScheduleInfo *info) +{ + if (info->title != NULL) + g_string_free (info->title, TRUE); + + if (info->subtitle != NULL) + g_string_free (info->subtitle, TRUE); + + if (info->description != NULL) + g_string_free (info->description, TRUE); + + if (info->category != NULL) + g_string_free (info->category, TRUE); + + g_free (info); +} +