00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <config.h>
00023 #ifdef HAVE_SCHEDULER
00024
00025 #include <iostream>
00026 #include <math.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <unistd.h>
00030 #include <dirent.h>
00031 #include <sys/ioctl.h>
00032 #include <sys/stat.h>
00033 #include <errno.h>
00034 #include <fcntl.h>
00035 #include <sys/soundcard.h>
00036 #include <string.h>
00037 #include <signal.h>
00038 #include <sys/types.h>
00039 #include <sys/wait.h>
00040 #include <termios.h>
00041 #include <stdarg.h>
00042
00043
00044
00045 #include <jutils.h>
00046 #include <audioproc.h>
00047 #include <jmixer.h>
00048 #include <playlist.h>
00049 #include <inchannels.h>
00050
00051 #ifdef HAVE_VORBIS
00052 #include <out_vorbis.h>
00053 #endif
00054
00055 #ifdef HAVE_LAME
00056 #include <out_lame.h>
00057 #endif
00058
00059 #include <glib.h>
00060
00061 #include <radiosched.h>
00062
00063
00064
00065 Basic_scheduler *rscheduler = NULL;
00066
00067 static char m_sched_file[FILENAME_MAX+1] = {0};
00068
00069
00070
00071 #define SF_S "sched"
00072 #define SFI_S "sched:item"
00073 # define SFIA_S "start"
00074 # define SFIA_E "end"
00075 # define SFIA_WKD "weekday" //0-6
00076 # define SUN "Sun" //0
00077 # define MON "Mon"
00078 # define TUE "Tue"
00079 # define WEN "Wen"
00080 # define THU "Thu"
00081 # define FRI "Fri"
00082 # define SAT "Sat"
00083 # define SFIA_DAY "day" //1-31
00084 # define SFIA_MTH "month" //1-12
00085 # define JAN "Jan"
00086 # define FEB "Feb"
00087 # define MAR "Mar"
00088 # define APR "Apr"
00089 # define MAI "Mai"
00090 # define JUN "Jun"
00091 # define JUL "Jul"
00092 # define AUG "Aug"
00093 # define SEP "Sep"
00094 # define OCT "Oct"
00095 # define NOV "Nov"
00096 # define DEC "Dec"
00097 # define SFIA_SRC "source"
00098 # define SFIA_CMNT "comment"
00099
00100
00101 const char *
00102 sched_file_path(void)
00103 {
00104 if (*m_sched_file) return m_sched_file;
00105
00106 char *home = getenv("HOME");
00107 if (home) {
00108 snprintf(m_sched_file, FILENAME_MAX, "%s/.muse/" SCHEDFILE, home);
00109 }
00110
00111 return m_sched_file;
00112 }
00113
00114
00115 static const char *
00116 _wkd(const char *d)
00117 {
00118 if (0==strcmp(d, SUN) || 0==strcmp(d, "0")) return "0";
00119 if (0==strcmp(d, MON) || 0==strcmp(d, "1")) return "1";
00120 if (0==strcmp(d, TUE) || 0==strcmp(d, "2")) return "2";
00121 if (0==strcmp(d, WEN) || 0==strcmp(d, "3")) return "3";
00122 if (0==strcmp(d, THU) || 0==strcmp(d, "4")) return "4";
00123 if (0==strcmp(d, FRI) || 0==strcmp(d, "5")) return "5";
00124 if (0==strcmp(d, SAT) || 0==strcmp(d, "6")) return "6";
00125 return NULL;
00126 }
00127
00128 static const char *
00129 _mth(const char *d)
00130 {
00131 if (0==strcmp(d, JAN) || 0==strcmp(d, "1")) return "1";
00132 if (0==strcmp(d, FEB) || 0==strcmp(d, "2")) return "2";
00133 if (0==strcmp(d, MAR) || 0==strcmp(d, "3")) return "3";
00134 if (0==strcmp(d, APR) || 0==strcmp(d, "4")) return "4";
00135 if (0==strcmp(d, MAI) || 0==strcmp(d, "5")) return "5";
00136 if (0==strcmp(d, JUN) || 0==strcmp(d, "6")) return "6";
00137 if (0==strcmp(d, JUL) || 0==strcmp(d, "7")) return "7";
00138 if (0==strcmp(d, AUG) || 0==strcmp(d, "8")) return "8";
00139 if (0==strcmp(d, SEP) || 0==strcmp(d, "9")) return "9";
00140 if (0==strcmp(d, OCT) || 0==strcmp(d, "10")) return "10";
00141 if (0==strcmp(d, NOV) || 0==strcmp(d, "11")) return "11";
00142 if (0==strcmp(d, DEC) || 0==strcmp(d, "12")) return "12";
00143 return NULL;
00144 }
00145
00146
00147
00148 static const char *
00149 _token(const char *str, char *buf, int len)
00150 {
00151 const char *digits = NULL;
00152 char *b = buf;
00153
00154 memset(buf, '\0', len);
00155 len--;
00156 while (*str!='\0' && *str!='-' && *str!=',' && *str!=':' && len-->0) {
00157 *buf++ = *str++;
00158 }
00159 buf = b;
00160 func("radioscheduler: _token: %s\n", buf);
00161
00162 digits=_wkd(buf);
00163 if(digits) strcpy(buf, digits);
00164
00165 digits=_mth(buf);
00166 if(digits) strcpy(buf, digits);
00167 func("radioscheduler: _token: %s str '%s'\n", buf, str);
00168 return str;
00169 }
00170
00171
00172 static inline int
00173 _number(const char *str)
00174 {
00175 register int n;
00176 register char c;
00177
00178 n = 0;
00179 while ((c = *str++) && (c >= '0') && (c <= '9')) n = (n * 10) + c - '0';
00180 return n;
00181 }
00182
00183
00184 Basic_scheduler::Basic_scheduler( const void *sched ) {
00185 func("Basic_scheduler::Basic_scheduler()\n");
00186
00187 prev_time = (time_t) 0;
00188
00189 playlist = new Playlist();
00190
00191 on = false;
00192 running = false;
00193 quit = true;
00194
00195 playing = NULL;
00196
00197 channel = new Channel();
00198 channel->lock();
00199 channel->start();
00200 channel->wait();
00201 channel->unlock();
00202
00203 _schedule = sched;
00204 mixer = NULL;
00205
00206 _thread_init();
00207 }
00208
00209 Basic_scheduler::~Basic_scheduler() {
00210 func("Basic_scheduler::~Basic_scheduler()\n");
00211
00212 if (channel) {
00213 stop_channel();
00214 channel->quit = true;
00215
00216 jsleep(0,50);
00217 delete channel; channel = NULL;
00218 }
00219
00220
00221
00222
00223 quit = true;
00224
00225 while(running) jsleep(0,20);
00226
00227 delete playlist;
00228
00229 _thread_destroy();
00230 }
00231
00232
00233 void Basic_scheduler::_thread_init() {
00234
00235 func("Basic_scheduler::thread_init()\n");
00236 if(pthread_mutex_init (&_mutex,NULL) == -1)
00237 error("error initializing POSIX thread mutex");
00238 if(pthread_cond_init (&_cond, NULL) == -1)
00239 error("error initializing POSIX thread condition");
00240 if(pthread_attr_init (&_attr) == -1)
00241 error("error initializing POSIX thread attribute");
00242
00243
00244
00245 pthread_attr_setdetachstate(&_attr,PTHREAD_CREATE_DETACHED);
00246 }
00247
00248 void Basic_scheduler::_thread_destroy() {
00249
00250
00251 while(running) {
00252 signal();
00253 lock(); unlock();
00254 }
00255
00256 if(pthread_mutex_destroy(&_mutex) == -1)
00257 error("error destroying POSIX thread mutex");
00258 if(pthread_cond_destroy(&_cond) == -1)
00259 error("error destroying POSIX thread condition");
00260 if(pthread_attr_destroy(&_attr) == -1)
00261 error("error destroying POSIX thread attribute");
00262 }
00263
00264
00265 void Basic_scheduler::run() {
00266
00267
00268
00269 time_t clk;
00270 unsigned left;
00271
00272 func("Basic_scheduler::run()\n");
00273 lock();
00274 running = true;
00275 unlock();
00276 signal();
00277
00278 quit = false;
00279
00280 while(!quit) {
00281 if(on) {
00282 on_wakeup();
00283 (void) time(&clk);
00284 left = (unsigned) (60 - clk % 60);
00285 while (left--) {
00286 jsleep(1,0);
00287
00288 if (playing && !play_code) {
00289 notice("Scheduler reloading %s", playing->path);
00290 play_code = start_channel(playing);
00291 }
00292 if (quit) break;
00293 }
00294 } else {
00295
00296 jsleep(1,0);
00297 }
00298 }
00299 running = false;
00300 }
00301
00302
00303 bool Basic_scheduler::play() {
00304 if(on) return(true);
00305
00306 if(!running) {
00307 error("%i:%s %s channel thread not launched",
00308 __LINE__,__FILE__,__FUNCTION__);
00309 return(false);
00310 }
00311 channel->play();
00312
00313 on = true;
00314 return(on);
00315 }
00316
00317 bool Basic_scheduler::stop() {
00318
00319 stop_channel();
00320 jsleep(0,50);
00321 on = false;
00322 return(!on);
00323 }
00324
00325
00326 void Basic_scheduler::dump() {
00327 if (!playlist) return;
00328
00329 for (int i=1; i<=playlist->len(); i++ ) {
00330 Url *p = (Url*)playlist->pick(i);
00331 func( "schedule_record: \n"
00332 " Min: %s, Hr: %s, Day: %s, Mon: %s, WDay: %s \n"
00333 " MnPlay %d,\n"
00334 " Url %s, commnent '%s'\n",
00335 p->mn, p->hr, p->day, p->mon, p->wkd,
00336 p->mnplay,
00337 p->path, p->comment
00338 );
00339 }
00340 }
00341
00342
00343 void Basic_scheduler::on_wakeup( void )
00344 {
00345 register struct tm *tm;
00346 time_t cur_time;
00347
00348
00349 func("Basic_scheduler::on_wakeup\n");
00350
00351 if (playing) {
00352 playing->mnleft--;
00353 if (playing->mnleft <= 0) {
00354 stop_channel();
00355 }
00356 }
00357
00358 load_schedule();
00359
00360 time(&cur_time);
00361 tm = localtime(&cur_time);
00362
00363 for (int i=1; i<=playlist->len(); i++) {
00364 Url *this_entry = (Url*)playlist->pick(i);
00365 if ( match(this_entry->mn, tm->tm_min)
00366 && match(this_entry->hr, tm->tm_hour)
00367 && match(this_entry->day, tm->tm_mday)
00368 && match(this_entry->mon, tm->tm_mon + 1)
00369 && match(this_entry->wkd, tm->tm_wday)) {
00370 notice("==> Basic_scheduler: %02d/%02d-%02d:%02d %s",
00371 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
00372 this_entry->path);
00373 play_code = start_channel(this_entry);
00374 }
00375 }
00376 }
00377
00378
00379 bool Basic_scheduler::start_inner_channel( Url *rec )
00380 {
00381 stop_channel();
00382 func("Basic_scheduler::start_channel '%s'\r\n", rec->path);
00383 playing = rec;
00384 playing->mnleft = playing->mnplay;
00385 channel->playlist->addurl( playing->path );
00386
00387
00388 channel->playlist->sel(1);
00389 playing->sel(true);
00390
00391 return channel->play();
00392
00393 }
00394
00395
00396 void Basic_scheduler::stop_inner_channel(void)
00397 {
00398 func("Basic_scheduler::stop_channel\r\n");
00399 channel->lock();
00400 channel->stop();
00401 channel->unlock();
00402 channel->lock();
00403 channel->playlist->cleanup();
00404 channel->clean();
00405 channel->unlock();
00406 channel->report();
00407 }
00408
00409 #define MUSE_URL "muse://channel"
00410 bool Basic_scheduler::start_mixer_channel( Url *rec )
00411 {
00412 char *p;
00413 int chan;
00414 bool ret = false;
00415
00416 if (!rec || !rec->path || !mixer) return false;
00417
00418 p = strstr(rec->path,MUSE_URL);
00419 if (!p) return false;
00420 p += strlen(MUSE_URL);
00421 chan = atoi(p) - 1 ;
00422 notice("Basic_scheduler::start_mixer_channel %d", chan);
00423
00424 if (chan>=0 && chan<MAX_CHANNELS) {
00425 mixer->set_channel(chan, 1);
00426 ret = mixer->play_channel(chan);
00427 }
00428 return ret;
00429 }
00430
00431 void Basic_scheduler::stop_mixer_channel( Url *rec )
00432 {
00433 char *p;
00434 int chan;
00435
00436 if (!rec || !rec->path || !mixer) return;
00437
00438 p = strstr(rec->path,MUSE_URL);
00439 if (!p) return;
00440 p += strlen(MUSE_URL);
00441 chan = atoi(p) -1 ;
00442 notice("Basic_scheduler::stop_mixer_channel %d", chan);
00443
00444 if (chan>0 &&chan<MAX_CHANNELS) {
00445 mixer->stop_channel(chan);
00446 }
00447 }
00448
00449 bool Basic_scheduler::start_channel( Url *rec )
00450 {
00451 bool ret = false;
00452 if (strstr(rec->path,MUSE_URL)) {
00453 ret = start_mixer_channel(rec);
00454 } else {
00455 ret = start_inner_channel(rec);
00456 }
00457 return ret;
00458 }
00459
00460 void Basic_scheduler::stop_channel(void)
00461 {
00462 if (playing && strstr(playing->path,MUSE_URL)) {
00463 stop_mixer_channel(playing);
00464 } else {
00465 stop_inner_channel();
00466 }
00467 playing = NULL;
00468 }
00469
00470 bool Basic_scheduler::match( const char *left, int right )
00471 {
00472 register int n;
00473
00474 char tok[16];
00475
00476
00477 if (!left) return false;
00478
00479 if (!strcmp(left, "*") || !strcmp(left, "any")) return true;
00480
00481 left = _token(left, tok, 16);
00482 n = _number(tok);
00483
00484 switch (*left) {
00485 case '\0':
00486 return( right == n );
00487
00488 break;
00489 case ',':
00490 if (right == n) return true;
00491 do {
00492 n = 0;
00493 left = _token(++left, tok, 16);
00494 n = _number(tok);
00495 if (right == n) return(1);
00496 } while (*left == ',');
00497 return false;
00498
00499 break;
00500 case '-':
00501 if (right < n) return(0);
00502 n = 0;
00503 left = _token(++left, tok, 16);
00504 n = _number(tok);
00505 return( right <= n );
00506
00507 break;
00508 default:
00509 break;
00510 }
00511 return false;
00512 }
00513
00514
00515
00516 const char empty_text_sched_file[] =
00517 "#\n"
00518 "# $HOME/.muse/schedule\n"
00519 "# Mn Hr Day Mth WDay MnPlay Url comment\n"
00520 "#\n"
00521 "# Sample: start at every minute\n"
00522 "#* * * * * 2 http://wwww.none.com:888/x.ogg comment starts here \n"
00523 "\n"
00524 "\n"
00525 ;
00526
00527
00528 void Scheduler_text::on_load_schedule() {
00529 int len, pos;
00530 FILE *cfp;
00531 struct stat buf;
00532 const char *cronf = (const char *)_schedule;
00533
00534 func("Scheduler_text::on_load_schedule \n");
00535 if (!_schedule) return;
00536
00537 if (stat(cronf, &buf)) {
00538 if (prev_time == (time_t) 0) warning("Can't stat crontab %s\n", cronf);
00539 prev_time = (time_t) 0;
00540 return;
00541 }
00542
00543 if (buf.st_mtime <= prev_time) return;
00544
00545 if ((cfp = fopen(cronf, "r")) == NULL) {
00546 if (prev_time == (time_t) 0) warning("Can't open crontab %s\n", cronf);
00547 prev_time = (time_t) 0;
00548 return;
00549 }
00550 prev_time = buf.st_mtime;
00551 len = pos = 0;
00552 playlist->cleanup();
00553
00554 stop_channel();
00555
00556 while (fgets(&crontab[pos], CRONSIZE - pos, cfp) != NULL) {
00557 if ( crontab[pos] == '#' || crontab[pos] == '\n'
00558 || crontab[pos] == '\r')
00559 continue;
00560 len = strlen(&crontab[pos]);
00561 if (crontab[pos + len - 1] == '\n') {
00562 len--;
00563 crontab[pos + len] = '\0';
00564 }
00565
00566 addentry(&crontab[pos]);
00567
00568 pos += ++len;
00569 if (pos >= CRONSIZE) break;
00570 }
00571 (void) fclose(cfp);
00572
00573
00574
00575
00576
00577 dump();
00578 }
00579
00580
00581
00582
00583 void Scheduler_text::addentry( char *line )
00584 {
00585 const char *mn, *hr, *day, *mon, *wkd, *cmnplay, *path, *comment;
00586
00587 mn = strtok(line, TSEPARATOR);
00588 hr = strtok( (char *)NULL, TSEPARATOR);
00589 day = strtok( (char *)NULL, TSEPARATOR);
00590 mon = strtok( (char *)NULL, TSEPARATOR);
00591 wkd = strtok( (char *)NULL, TSEPARATOR);
00592 cmnplay= strtok( (char *)NULL, TSEPARATOR);
00593 path = strtok( (char *)NULL, TSEPARATOR);
00594 if (path) {
00595 comment= strchr(path, '\0') + 1;
00596 playlist->addurl(path, mn, hr, day, mon, wkd, cmnplay, comment);
00597 }
00598 }
00599
00600
00601
00602
00603 const char empty_xml_sched_file_header[] =
00604 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \n"
00605 "<sched xmlns:sched=\"/\">\n"
00606 "<!--\n"
00607 " $HOME/.muse/schedule \n"
00608 " Attributes: \n"
00609 " start=\"hour:min\" in 24-hours format \n"
00610 " end=\"hour:min\" \n"
00611 " weekday=0-6 | Sun(0), Mon, Tue, Wen, Thu, Fri, Sat \n"
00612 " day=1-31 \n"
00613 " month=1-12 | Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Oct,Nov,Dec \n"
00614 " Wildcards: \n"
00615 " * ex: start=\"*:*\" \n"
00616 " periods: Sun-Sat \n"
00617 " enumerations: Jan,Feb,Dec \n"
00618 " Source: file.ogg, file.mp3, http://source, muse://channelX with X=1..6\n"
00619 " Example: \n"
00620 " <sched:item start=\"10:00\" end=\"13:00\" weekday=\"Mon-Fri\" day=\"*\" month=\"*\" source=\"http://aud-one.kpfa.org:8090/kpfa.mp3\" comment=\"KPFA Democracy Now\" /> \n"
00621 "-->\n"
00622 "\n"
00623 ;
00624
00625 const char empty_xml_sched_file_footer[] =
00626 "</sched>"
00627 "\n"
00628 ;
00629
00630 typedef struct _xml_user_data {
00631 sched_rec_callb callb;
00632 void *udata;
00633 sched_rec *sr;
00634 } xml_user_data;
00635
00636
00637 int
00638 create_xml_schedule_file(void)
00639 {
00640 gchar *home, *dir;
00641 int ret;
00642 int fd;
00643
00644 if(!(home = getenv("HOME"))) {
00645 error("no $HOME found");
00646 return FALSE;
00647 }
00648
00649 dir = g_strconcat(home, "/.muse", NULL);
00650 if(!g_file_test(dir, (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
00651 if((fd = creat(dir, S_IRWXU)) < 0) {
00652 error("error during $HOME./muse directory creation");
00653 return FALSE;
00654 }
00655 close(fd);
00656 }
00657 g_free(dir);
00658
00659 if(g_file_test(sched_file_path(), (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) {
00660 return TRUE;
00661 }
00662
00663 notice("create_xml_schedule_file '%s'", m_sched_file);
00664 ret = write_xml_schedule_file( "" );
00665 return ret;
00666 }
00667
00668 char *
00669 format_xml_sched_rec(const sched_rec *rec)
00670 {
00671 gchar *ret = g_strconcat(
00672 "<" SFI_S,
00673 " " SFIA_SRC "=\"", rec->src, "\"",
00674 " " SFIA_CMNT "=\"", rec->comment, "\"",
00675 " " SFIA_WKD "=\"", rec->wkd, "\"",
00676 " " SFIA_S "=\"", rec->stime, "\"",
00677 " " SFIA_E "=\"", rec->etime, "\"",
00678 " " SFIA_DAY "=\"*\"",
00679 " " SFIA_MTH "=\"*\"",
00680 "/>\n", NULL
00681 );
00682 return ret;
00683
00684 }
00685
00686 int
00687 write_xml_schedule_file(const char *content)
00688 {
00689 FILE *f;
00690
00691 if(!(f = fopen(sched_file_path(), "w"))) {
00692 error("problem opening " SCHEDFILE " for writing");
00693 return FALSE;
00694 }
00695
00696 fputs(empty_xml_sched_file_header, f);
00697 fputs(content, f);
00698 fputs(empty_xml_sched_file_footer, f);
00699
00700 fclose(f);
00701
00702 return TRUE;
00703 }
00704
00705 static void
00706 start_e(GMarkupParseContext *c, const gchar *ename,
00707 const gchar **anames, const gchar **avalues,
00708 gpointer udata, GError **err)
00709 {
00710 xml_user_data *ud = (xml_user_data*)udata;
00711
00712
00713
00714
00715
00716
00717 if (0==strcmp(ename, SFI_S)) {
00718 for (int i=0; anames[i]; i++) {
00719 if (0==strcmp(anames[i], SFIA_CMNT)) ud->sr->comment = avalues[i];
00720 if (0==strcmp(anames[i], SFIA_SRC)) ud->sr->src = avalues[i];
00721 if (0==strcmp(anames[i], SFIA_MTH)) ud->sr->month = avalues[i];
00722 if (0==strcmp(anames[i], SFIA_DAY)) ud->sr->day = avalues[i];
00723 if (0==strcmp(anames[i], SFIA_WKD)) ud->sr->wkd = avalues[i];
00724 if (0==strcmp(anames[i], SFIA_S)) ud->sr->stime = avalues[i];
00725 if (0==strcmp(anames[i], SFIA_E)) ud->sr->etime = avalues[i];
00726 }
00727
00728 if (!ud->sr->comment) ud->sr->comment = "";
00729 if (!ud->sr->src) ud->sr->src = "";
00730 if (!ud->sr->month) ud->sr->month = "";
00731 if (!ud->sr->day) ud->sr->day = "";
00732 if (!ud->sr->wkd) ud->sr->wkd = "";
00733 if (!ud->sr->stime) ud->sr->stime = "";
00734 if (!ud->sr->etime) ud->sr->etime = "";
00735 (*ud->callb)(ud->udata, ud->sr);
00736
00737 ud->sr->comment = NULL;
00738 ud->sr->src = NULL;
00739 ud->sr->month = NULL;
00740 ud->sr->day = NULL;
00741 ud->sr->wkd = NULL;
00742 ud->sr->stime = NULL;
00743 ud->sr->etime = NULL;
00744 }
00745 }
00746
00747
00748 static void
00749 end_e(GMarkupParseContext *c, const gchar *ename,
00750 gpointer udata, GError **err)
00751 {
00752
00753 }
00754
00755 static void
00756 on_err(GMarkupParseContext *c, GError *err, gpointer udata)
00757 {
00758 notice("<on_err>");
00759 }
00760
00761 int
00762 parse_xml_sched_file( sched_rec_callb callb, void *udata, sched_rec *sr )
00763 {
00764 gchar *buf=NULL;
00765 guint len;
00766 xml_user_data calldata = {callb, udata, sr};
00767
00768 g_file_get_contents(sched_file_path(), &buf, &len, NULL);
00769 if (len>0) {
00770 GMarkupParser xp = {start_e, end_e, NULL, NULL, on_err};
00771 GError *err = NULL;
00772 GMarkupParseContext *parser = NULL;
00773 parser = g_markup_parse_context_new(&xp, (GMarkupParseFlags)0, &calldata, NULL);
00774 if (!g_markup_parse_context_parse(parser, buf, len, &err)) {
00775 error("parse_xml_sched_file g_markup_parse_context_parse error: %s\n", err->message);
00776 }
00777 g_markup_parse_context_end_parse(parser, &err);
00778 g_markup_parse_context_free(parser);
00779 if (err) {
00780 warning("parse_xml_sched_file: '%s'", err->message);
00781
00782 }
00783 }
00784
00785 g_free(buf);
00786 return TRUE;
00787 }
00788
00789 void Scheduler_xml::on_load_schedule() {
00790 sched_rec sr = {0};
00791 struct stat buf;
00792 const char *cronf = (const char *)_schedule;
00793
00794 func("Scheduler_xml::on_load_schedule \n");
00795 if (!_schedule) return;
00796
00797 if (stat(cronf, &buf)) {
00798 if (prev_time == (time_t) 0) warning("Can't stat crontab %s\n", cronf);
00799 prev_time = (time_t) 0;
00800 return;
00801 }
00802
00803 if (buf.st_mtime <= prev_time) return;
00804 prev_time = buf.st_mtime;
00805
00806 playlist->cleanup();
00807 stop_channel();
00808 parse_xml_sched_file( addentry, this, &sr );
00809
00810 dump();
00811 }
00812
00813
00814
00815
00816 int Scheduler_xml::addentry( void *instance, sched_rec *sr )
00817 {
00818 char mn[16]={0}, hr[16]={0}, cmnplay[16]={0};
00819 int emn, smn, ehr, shr, mnplay;
00820 char tok[16];
00821 const char *p;
00822
00823
00824 p = _token(sr->stime, tok, 16);
00825 strcpy(hr, tok);
00826 shr = _number(tok);
00827
00828 p = _token(++p, tok, 16);
00829 strcpy(mn, tok);
00830 smn = _number(tok);
00831
00832 p = _token(sr->etime, tok, 16);
00833 ehr = _number(tok);
00834
00835 p = _token(++p, tok, 16);
00836 emn = _number(tok);
00837
00838
00839 if (*mn=='*') {
00840 mnplay = 1;
00841 } else if (*hr=='*') {
00842 mnplay = 60;
00843 } else {
00844 mnplay = 60*(ehr-shr) + (emn-smn);
00845 }
00846 sprintf(cmnplay, "%d", mnplay);
00847
00848 if (sr->src) {
00849 ((Scheduler_xml*)instance)->playlist->addurl(sr->src, mn, hr, sr->day,
00850 sr->month, sr->wkd, cmnplay, sr->comment);
00851 }
00852 return 1;
00853 }
00854
00855 #endif