Project

General

Profile

Feature #869 ยป epg.c

Joshua Welch, 2012-08-09 01:35

 
1
/*
2
 *  Electronic Program Guide - Common functions
3
 *  Copyright (C) 2007 Andreas ?man
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation, either version 3 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

    
19
#include <sys/mman.h>
20
#include <sys/stat.h>
21
#include <stdio.h>
22
#include <unistd.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <regex.h>
26
#include <assert.h>
27
#include <stdint.h>
28

    
29
#include "tvheadend.h"
30
#include "channels.h"
31
#include "settings.h"
32
#include "epg.h"
33
#include "dvr/dvr.h"
34
#include "htsp.h"
35
#include "htsmsg_binary.h"
36

    
37
#define EPG_MAX_AGE 86400
38

    
39
#define EPG_GLOBAL_HASH_WIDTH 1024
40
#define EPG_GLOBAL_HASH_MASK (EPG_GLOBAL_HASH_WIDTH - 1)
41
static struct event_list epg_hash[EPG_GLOBAL_HASH_WIDTH];
42

    
43
static void epg_expire_event_from_channel(void *opauqe);
44
static void epg_ch_check_current_event(void *aux);
45
/* helper function to fuzzy compare two events */
46
static int epg_event_cmp_overlap(event_t *e1, event_t *e2);
47
static void epg_erase_duplicates(event_t *e, channel_t *ch);
48

    
49

    
50
static int
51
e_ch_cmp(const event_t *a, const event_t *b)
52
{
53
  return a->e_start - b->e_start;
54
}
55

    
56
/**
57
 *
58
 */
59
static void
60
epg_set_current(channel_t *ch, event_t *e, event_t *next)
61
{
62
  if(next == NULL && e != NULL)
63
    next = RB_NEXT(e, e_channel_link);
64

    
65
  if(ch->ch_epg_current == e && ch->ch_epg_next == next)
66
    return;
67

    
68
  ch->ch_epg_current = e;
69
  ch->ch_epg_next = next;
70
  if(e != NULL)
71
    gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
72
		   ch, MAX(e->e_stop, dispatch_clock + 1));
73
  htsp_channel_update_current(ch);
74
}
75

    
76
/**
77
 *
78
 */
79
static void
80
epg_ch_check_current_event(void *aux)
81
{
82
  channel_t *ch = aux;
83
  event_t skel, *e = ch->ch_epg_current;
84
  if(e != NULL) {
85
    if(e->e_start <= dispatch_clock && e->e_stop > dispatch_clock) {
86
      epg_set_current(ch, e, NULL);
87
      return;
88
    }
89

    
90
    if((e = RB_NEXT(e, e_channel_link)) != NULL) {
91

    
92
      if(e->e_start <= dispatch_clock && e->e_stop > dispatch_clock) {
93
	epg_set_current(ch, e, NULL);
94
	return;
95
      }
96
    }
97
  }
98

    
99
  e = epg_event_find_by_time(ch, dispatch_clock);
100
  if(e != NULL) {
101
    epg_set_current(ch, e, NULL);
102
    return;
103
  }
104

    
105
  skel.e_start = dispatch_clock;
106
  e = RB_FIND_GT(&ch->ch_epg_events, &skel, e_channel_link, e_ch_cmp);
107
  if(e != NULL) {
108
    gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
109
		   ch, MAX(e->e_start, dispatch_clock + 1));
110
    epg_set_current(ch, NULL, e);
111
  } else {
112
    epg_set_current(ch, NULL, NULL);
113
  }
114
}
115

    
116

    
117
/**
118
 *
119
 */
120
void
121
epg_event_updated(event_t *e)
122
{
123
  dvr_autorec_check_event(e);
124
}
125

    
126

    
127
/**
128
 *
129
 */
130
int
131
epg_event_set_title(event_t *e, const char *title)
132
{
133
  if(e->e_title != NULL && !strcmp(e->e_title, title))
134
    return 0;
135
  free(e->e_title);
136
  e->e_title = strdup(title);
137
  return 1;
138
}
139

    
140

    
141
/**
142
 *
143
 */
144
int
145
epg_event_set_desc(event_t *e, const char *desc)
146
{
147
  if(e->e_desc != NULL && strlen(e->e_desc) >= strlen(desc)) {
148
    /* The current description is longer than the one we try to set.
149
     * We assume that a longer description is better than a shorter
150
     * so we just bail out.
151
     * Typically happens when the XMLTV and DVB EPG feed differs.
152
     */
153
    return 0;
154
  }
155
  free(e->e_desc);
156
  e->e_desc = strdup(desc);
157
  return 1;
158
}
159

    
160
/**
161
 *
162
 */
163
int
164
epg_event_set_ext_desc(event_t *e, int ext_dn, const char *desc)
165
{
166
  if(e->e_ext_desc == NULL && ext_dn != 0)
167
    return 0;
168
  if(e->e_ext_desc != NULL && strstr(e->e_ext_desc, desc))
169
    return 0;
170

    
171
  int len = strlen(desc) + ( e->e_ext_desc ? strlen(e->e_ext_desc) : 0) + 1;
172
  char *tmp = (char*)malloc(len);
173

    
174
  if(e->e_ext_desc) {
175
    strcpy(tmp, e->e_ext_desc);
176
    strcat(tmp, desc);
177
    free(e->e_ext_desc);
178
  } else
179
    strcpy(tmp, desc);
180

    
181
  e->e_ext_desc = tmp;
182
  return 1;
183
}
184

    
185
/**
186
 *
187
 */
188
int
189
epg_event_set_ext_item(event_t *e, int ext_dn, const char *item)
190
{
191
  if(e->e_ext_item == NULL && ext_dn != 0)
192
    return 0;
193
  if(e->e_ext_item != NULL && strstr(e->e_ext_item, item))
194
    return 0;
195

    
196
  int len = strlen(item) + ( e->e_ext_item ? strlen(e->e_ext_item) : 0) + 1;
197
  char *tmp = (char*)malloc(len);
198

    
199
  if(e->e_ext_item) {
200
    strcpy(tmp, e->e_ext_item);
201
    strcat(tmp, item);
202
    free(e->e_ext_item);
203
  } else
204
    strcpy(tmp, item);
205

    
206
  e->e_ext_item = tmp;
207
  return 1;
208
}
209

    
210
/**
211
 *
212
 */
213
int
214
epg_event_set_ext_text(event_t *e, int ext_dn, const char *text)
215
{
216
  if(e->e_ext_text == NULL && ext_dn != 0)
217
    return 0;
218
  if(e->e_ext_text != NULL && strstr(e->e_ext_text, text))
219
    return 0;
220

    
221
  int len = strlen(text) + ( e->e_ext_text ? strlen(e->e_ext_text) : 0) + 1;
222
  char *tmp = (char*)malloc(len);
223

    
224
  if(e->e_ext_text) {
225
    strcpy(tmp, e->e_ext_text);
226
    strcat(tmp, text);
227
    free(e->e_ext_text);
228
  } else
229
    strcpy(tmp, text);
230

    
231
  e->e_ext_text = tmp;
232
  return 1;
233
}
234

    
235
/**
236
 *
237
 */
238
int
239
epg_event_set_content_type(event_t *e, uint8_t type)
240
{
241
  if(e->e_content_type == type)
242
    return 0;
243

    
244
  e->e_content_type = type;
245
  return 1;
246
}
247

    
248

    
249
/**
250
 *
251
 */
252
int
253
epg_event_set_episode(event_t *e, epg_episode_t *ee)
254
{
255
  if(e->e_episode.ee_season  == ee->ee_season &&
256
     e->e_episode.ee_episode == ee->ee_episode && 
257
     e->e_episode.ee_part    == ee->ee_part && 
258
     !strcmp(e->e_episode.ee_onscreen ?: "", ee->ee_onscreen ?: ""))
259
    return 0;
260

    
261
  e->e_episode.ee_season  = ee->ee_season;
262
  e->e_episode.ee_episode = ee->ee_episode;
263
  e->e_episode.ee_part    = ee->ee_part;
264

    
265
  tvh_str_set(&e->e_episode.ee_onscreen, ee->ee_onscreen);
266
  return 1;
267
}
268

    
269

    
270
/**
271
 *
272
 */
273
static void
274
epg_event_destroy(event_t *e)
275
{
276
  free(e->e_title);
277
  free(e->e_desc);
278
  free(e->e_ext_desc);
279
  free(e->e_ext_item);
280
  free(e->e_ext_text);
281
  free(e->e_episode.ee_onscreen);
282
  LIST_REMOVE(e, e_global_link);
283
  free(e);
284
}
285

    
286
/**
287
 *
288
 */
289
static void
290
epg_event_unref(event_t *e)
291
{
292
  if(e->e_refcount > 1) {
293
    e->e_refcount--;
294
    return;
295
  }
296
  assert(e->e_refcount == 1);
297
  epg_event_destroy(e);
298
}
299

    
300
/**
301
 *
302
 */
303
static void
304
epg_remove_event_from_channel(channel_t *ch, event_t *e)
305
{
306
  int wasfirst = e == RB_FIRST(&ch->ch_epg_events);
307
  event_t *n = RB_NEXT(e, e_channel_link);
308

    
309
  assert(e->e_channel == ch);
310

    
311
  RB_REMOVE(&ch->ch_epg_events, e, e_channel_link);
312
  e->e_channel = NULL;
313
  epg_event_unref(e);
314

    
315
  if(ch->ch_epg_current == e) {
316
    epg_set_current(ch, NULL, n);
317

    
318
    if(n != NULL)
319
      gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
320
		     ch, n->e_start);
321
  } else if(ch->ch_epg_next == e) {
322
    epg_set_current(ch, ch->ch_epg_current, NULL);
323
  }
324

    
325
  if(wasfirst && (e = RB_FIRST(&ch->ch_epg_events)) != NULL) {
326
    gtimer_arm_abs(&ch->ch_epg_timer_head, epg_expire_event_from_channel,
327
		   ch, e->e_stop);
328
  }
329
}
330

    
331

    
332
/**
333
 *
334
 */
335
event_t *
336
epg_event_create(channel_t *ch, time_t start, time_t stop, int dvb_id,
337
		 int *created)
338
{
339
  static event_t *skel;
340
  event_t *e;
341
  static int tally;
342

    
343
  if(created != NULL)
344
    *created = 0;
345

    
346
  if((stop - start) > 11 * 3600)
347
    return NULL;
348

    
349
  if(stop <= start)
350
    return NULL;
351

    
352
  lock_assert(&global_lock);
353

    
354
  if(skel == NULL)
355
    skel = calloc(1, sizeof(event_t));
356

    
357
  skel->e_start = start;
358
  
359
  e = RB_INSERT_SORTED(&ch->ch_epg_events, skel, e_channel_link, e_ch_cmp);
360
  if(e == NULL) {
361
    /* New entry was inserted */
362

    
363
    if(created != NULL)
364
      *created = 1;
365

    
366
    e = skel;
367
    skel = NULL;
368

    
369
    e->e_id = ++tally;
370
    e->e_stop = stop;
371
    e->e_dvb_id = dvb_id;
372

    
373
    LIST_INSERT_HEAD(&epg_hash[e->e_id & EPG_GLOBAL_HASH_MASK], e,
374
		     e_global_link);
375

    
376
    e->e_refcount = 1;
377
    e->e_channel = ch;
378

    
379
    if(e == RB_FIRST(&ch->ch_epg_events)) {
380
      /* First in temporal order, arm expiration timer */
381

    
382
      gtimer_arm_abs(&ch->ch_epg_timer_head, epg_expire_event_from_channel,
383
		     ch, e->e_stop);
384
    }
385

    
386

    
387
    
388
    if(ch->ch_epg_timer_current.gti_callback == NULL ||
389
       start < ch->ch_epg_timer_current.gti_expire) {
390

    
391
      gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
392
		     ch, e->e_start);
393
    }
394
  } else {
395
    /* Already exist */
396

    
397
    if(stop > e->e_stop) {
398
      /* We allow the event to extend in time */
399
#if 0
400
      printf("Event %s on %s extended stop time\n", e->e_title,
401
	     e->e_channel->ch_name);
402
      printf("Previous %s", ctime(&e->e_stop));
403
      printf("     New %s", ctime(&stop));
404
#endif
405
      e->e_stop = stop;
406

    
407
      if(e == ch->ch_epg_current) {
408
	gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
409
		       ch, e->e_start);
410
      }
411
    }
412
  }
413

    
414
  epg_erase_duplicates(e, ch);
415
  return e;
416
}
417

    
418
static void
419
epg_erase_duplicates(event_t *e, channel_t *ch) {
420

    
421
  event_t *p, *n;
422
  int dvb_id = e->e_dvb_id;
423

    
424
  if(dvb_id != -1) {
425
    /* Erase any close events with the same DVB event id or are very similar*/
426

    
427
    if((p = RB_PREV(e, e_channel_link)) != NULL) {
428
      if(p->e_dvb_id == dvb_id || epg_event_cmp_overlap(p, e)) {
429
        tvhlog(LOG_DEBUG, "epg",
430
               "Removing overlapping event instance %s from EPG", p->e_title);
431
        dvr_event_replaced(p, e);
432
	epg_remove_event_from_channel(ch, p);
433
      } else if((p = RB_PREV(p, e_channel_link)) != NULL) {
434
	if(p->e_dvb_id == dvb_id || epg_event_cmp_overlap(p, e)) {
435
          tvhlog(LOG_DEBUG, "epg",
436
                 "Removing overlapping event instance %s from EPG", p->e_title);
437
          dvr_event_replaced(p, e);
438
	  epg_remove_event_from_channel(ch, p);
439
        }
440
      }
441
    }
442

    
443
    if((n = RB_NEXT(e, e_channel_link)) != NULL) {
444
      if(n->e_dvb_id == dvb_id || epg_event_cmp_overlap(n, e)) {
445
        tvhlog(LOG_DEBUG, "epg",
446
               "Removing overlapping event instance %s from EPG", n->e_title);
447
        dvr_event_replaced(n, e);
448
	epg_remove_event_from_channel(ch, n);
449
      } else if((n = RB_NEXT(n, e_channel_link)) != NULL) {
450
	if(n->e_dvb_id == dvb_id || epg_event_cmp_overlap(n, e)) {
451
          tvhlog(LOG_DEBUG, "epg",
452
                 "Removing overlapping event instance %s from EPG", n->e_title);
453
          dvr_event_replaced(n, e);
454
	  epg_remove_event_from_channel(ch, n);
455
        }   
456
      }
457
    }
458
  }
459
  
460
}
461

    
462
static int
463
epg_event_cmp_overlap(event_t *e1, event_t *e2)
464
{
465

    
466
  int dur_a, dur_b, mindur;
467
    
468
  if ((e1->e_title == NULL) || (e2->e_title == NULL))
469
    return 0;
470
    
471
  dur_a = e1->e_stop - e1->e_start;
472
  dur_b = e2->e_stop - e2->e_start;
473
  mindur = dur_a < dur_b ? dur_a : dur_b;
474
    
475
  if ((abs(e1->e_start - e2->e_start) < mindur) && (abs(e1->e_stop - e2->e_stop) < mindur)) {
476
    return 1;
477
  }
478

    
479
  return 0;
480
}
481

    
482
/**
483
 *
484
 */
485
event_t *
486
epg_event_find_by_time(channel_t *ch, time_t t)
487
{
488
  event_t skel, *e;
489

    
490
  skel.e_start = t;
491
  e = RB_FIND_LE(&ch->ch_epg_events, &skel, e_channel_link, e_ch_cmp);
492
  if(e == NULL || e->e_stop < t)
493
    return NULL;
494
  return e;
495
}
496

    
497

    
498
/**
499
 *
500
 */
501
event_t *
502
epg_event_find_by_id(int eventid)
503
{
504
  event_t *e;
505

    
506
  LIST_FOREACH(e, &epg_hash[eventid & EPG_GLOBAL_HASH_MASK], e_global_link)
507
    if(e->e_id == eventid)
508
      break;
509
  return e;
510
}
511

    
512

    
513

    
514
/**
515
 *
516
 */
517
static void
518
epg_expire_event_from_channel(void *opaque)
519
{
520
  channel_t *ch = opaque;
521
  event_t *e = RB_FIRST(&ch->ch_epg_events);
522
  epg_remove_event_from_channel(ch, e);
523
}
524

    
525

    
526
/**
527
 *
528
 */
529
void
530
epg_unlink_from_channel(channel_t *ch)
531
{
532
  event_t *e;
533

    
534
  while((e = ch->ch_epg_events.root) != NULL)
535
    epg_remove_event_from_channel(ch, e);
536

    
537
  gtimer_disarm(&ch->ch_epg_timer_head);
538
  gtimer_disarm(&ch->ch_epg_timer_current);
539

    
540
}
541

    
542

    
543

    
544
/**
545
 * EPG content group
546
 *
547
 * Based on the content types defined in EN 300 468
548
 */
549
static const char *groupnames[16] = {
550
   [1] = "Movie / Drama",
551
   [2] = "News / Current affairs",
552
   [3] = "Show / Games",
553
   [4] = "Sports",
554
   [5] = "Children's / Youth",
555
   [6] = "Music",
556
   [7] = "Art / Culture",
557
   [8] = "Social / Political issues / Economics",
558
   [9] = "Education / Science / Factual topics",
559
   [10] = "Leisure hobbies",
560
   [11] = "Special characteristics",
561
};
562

    
563
static const char *groupdefinition[0xFF] = {
564
    /* ox1Y are Movie/Drama */
565
    [0x10] = "Movie / Drama",
566
    [0x11] = "Detective / Thriller",
567
    [0x12] = "Adventure / Western / War",
568
    [0x13] = "science fiction/fantasy/horror",
569
    [0x14] = "comedy",
570
    [0x15] = "soap/melodrama/folkloric",
571
    [0x16] = "romance",
572
    [0x17] = "serious/classical/religious/historical movie/drama",
573
    [0x18] = "Drama",
574
    [0x19] = "film",
575
    [0x1A] = "movie",
576
    [0x1B] = "comedy drama",
577
    [0x1C] = "sitcom",
578
    [0x1D] = "entertainment",
579
    /* 0x2Y: News / Current Affairs */
580
    [0x20] = "News / Current Affairs",
581
    [0x21] = "news/weather report",
582
    [0x22] = "news magazine",
583
    [0x23] = "documentary",
584
    [0x24] = "discussion/interview/debate",
585
    [0x25] = "news",
586
    [0x26] = "current Affairs",
587
    [0x27] = "news and current affairs",
588
    /*0x3Y:  Show/Game show */
589
    [0x30] = "show/game show",
590
    [0x31] = "game show/quiz/contest",
591
    [0x32] = "variety show",
592
    [0x33] = "talk show",
593
    [0x34] = "game Show",
594
    /*0x4Y: Sports */
595
    [0x40] = "sports",
596
    [0x41] = "special events",
597
    [0x42] = "sports magazines",
598
    [0x43] = "football/soccer",
599
    [0x44] = "tennis/squash",
600
    [0x45] = "team sports",
601
    [0x46] = "athletics",
602
    [0x47] = "motor sport",
603
    [0x48] = "water sport",
604
    [0x49] = "winter sports",
605
    [0x4A] = "equestrian",
606
    [0x4B] = "martial sports",
607
    [0x4C] = "Sport",
608
    /*0x5Y: Children's/Youth programmes */
609
    [0x50] = "children's/youth programmes",
610
    [0x51] = "pre-school children's programmes",
611
    [0x52] = "entertainment programmes for 6 to 14",
612
    [0x53] = "entertainment programmes for 10 to 16",
613
    [0x54] = "informational/educational/school programmes",
614
    [0x55] = "cartoons/puppets",
615
    [0x56] = "children",
616
    [0x57] = "animation",
617
    /*0x6Y: Music/Ballet/Dance */
618
    [0x60] = "music/ballet/dance",
619
    [0x61] = "rock/pop ",
620
    [0x62] = "serious music/classical music",
621
    [0x63] = "folk/traditional music",
622
    [0x64] = "jazz",
623
    [0x65] = "musical/opera",
624
    [0x66] = "ballet",
625
    [0x67] = "music",
626
    [0x68] = "music and arts",
627
    /*0x7Y: Arts/Culture */
628
    [0x70] = "arts/culture",
629
    [0x71] = "performing arts",
630
    [0x72] = "fine arts",
631
    [0x73] = "religion",
632
    [0x74] = "popular culture/traditional arts",
633
    [0x75] = "literature",
634
    [0x76] = "film/cinema",
635
    [0x77] = "experimental film/video",
636
    [0x78] = "broadcasting/press",
637
    [0x79] = "new media",
638
    [0x7A] = "arts/culture magazines",
639
    [0x7B] = "fashion",
640
    [0x7C] = "arts and culture",
641
    /*0x8Y: Social/Political issues/Economics */
642
    [0x80] = "social/political issues/economics",
643
    [0x81] = "magazines/reports/documentary",
644
    [0x82] = "economics/social advisory",
645
    [0x83] = "remarkable people",
646
    [0x84] = "Discussion/Debate",
647
    [0x85] = "Reality",
648
    [0x86] = "Soap",
649
    /*0x8Y: Education/ Science/Factual topics */
650
    [0x90] = "education/science/factual topics",
651
    [0x91] = "nature/animals/environment",
652
    [0x92] = "technology/natural sciences",
653
    [0x93] = "medicine/physiology/psychology",
654
    [0x94] = "foreign countries/expeditions",
655
    [0x95] = "social/spiritual sciences",
656
    [0x96] = "further education",
657
    [0x97] = "languages",
658
    [0x98] = "Education",
659
    [0x99] = "sci-fi",
660
    [0x9A] = "drama documentary",
661
    [0x9B] = "documentary",
662
    [0x9C] = "factual",
663
    [0x9D] = "science",
664
    [0x9E] = "religion",
665
    /*0xA0: Leisure hobbies */
666
    [0xA0] = "leisure hobbies",
667
    [0xA1] = "tourism/travel",
668
    [0xA2] = "handicraft",
669
    [0xA3] = "motoring",
670
    [0xA4] = "fitness & health",
671
    [0xA5] = "cooking",
672
    [0xA6] = "advertisement/shopping",
673
    [0xA8] = "gardening",
674
    [0xA9] = "transport",
675
    [0xAA] = "cookery",
676
    [0xAB] = "nature",
677
    [0xAC] = "health",
678
    [0xAD] = "home and property",
679
    [0xAE] = "travel",
680
    [0xAF] = "FOOD",
681
    /*0xB0: Special Characteristics */
682
    [0xB0] = "original language",
683
    [0xB1] = "black & white",
684
    [0xB2] = "unpublished",
685
    [0xB3] = "live broadcast",
686
    [0xB4] = "special characteristics",
687
    [0xB5] = "interests",
688
};
689

    
690

    
691

    
692
/**
693
 *
694
 */
695
const char *
696
epg_content_group_get_name(uint8_t id)
697
{
698
  return id < 16 ? groupnames[id] : NULL;
699
}
700

    
701
/**
702
 *
703
 */
704
uint8_t
705
epg_content_group_find_by_name(const char *name)
706
{
707
      unsigned int i;
708
      for(i = 0; i < 0xFF; i++) {
709
         if(groupdefinition[i] != NULL && !strcasecmp(name, groupdefinition[i])){
710
             int b = (i >> 4) & 0xF;
711
             return b;
712
         }
713
      }
714
      return 0;
715
}
716
/**
717
 *
718
 */
719
static int
720
epg_event_create_by_msg(htsmsg_t *c, time_t now)
721
{
722
 channel_t *ch;
723
  event_t *e = NULL;
724
  uint32_t ch_id = 0;
725
  uint32_t e_start = 0;
726
  uint32_t e_stop = 0;
727
  int e_dvb_id = 0, v;
728
  const char *s;
729

    
730
  // Now create the event
731
  if(htsmsg_get_u32(c, "ch_id", &ch_id))
732
    return 0;
733

    
734
  if((ch = channel_find_by_identifier(ch_id)) == NULL)
735
    return 0;
736

    
737
  if(htsmsg_get_u32(c, "start", &e_start))
738
    return 0;
739

    
740
  if(htsmsg_get_u32(c, "stop", &e_stop))
741
    return 0;
742

    
743
  if(e_stop < now)
744
    return 0;
745

    
746
  if(htsmsg_get_s32(c, "dvb_id", &e_dvb_id))
747
    e_dvb_id = -1;
748

    
749
  e = epg_event_create(ch, e_start, e_stop, e_dvb_id, NULL);
750

    
751
  if((s = htsmsg_get_str(c, "title")) != NULL)
752
    epg_event_set_title(e, s);
753

    
754
  if((s = htsmsg_get_str(c, "desc")) != NULL)
755
    epg_event_set_desc(e, s);
756

    
757
  if(!htsmsg_get_s32(c, "season", &v))
758
    e->e_episode.ee_season = v;
759

    
760
  if(!htsmsg_get_s32(c, "episode", &v))
761
    e->e_episode.ee_episode = v;
762

    
763
  if(!htsmsg_get_s32(c, "part", &v))
764
    e->e_episode.ee_part = v;
765

    
766
  if((s = htsmsg_get_str(c, "epname")) != NULL)
767
    tvh_str_set(&e->e_episode.ee_onscreen, s);
768

    
769
  return 1;
770
}
771

    
772

    
773
/**
774
 *
775
 */
776
static void
777
epg_load(void)
778
{
779
  struct stat st;
780
  int fd = hts_settings_open_file(0, "epgdb");
781
  time_t now;
782
  int created = 0;
783

    
784
  time(&now);
785

    
786
  if(fd == -1)
787
    return;
788

    
789
  if(fstat(fd, &st)) {
790
    close(fd);
791
    return;
792
  }
793
  uint8_t *mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
794
  if(mem == MAP_FAILED) {
795
    close(fd);
796
    return;
797
  }
798
  const uint8_t *rp = mem;
799
  size_t remain = st.st_size;
800

    
801
  while(remain > 4) {
802
    int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3];
803
    remain -= 4;
804
    rp += 4;
805

    
806
    if(msglen > remain) {
807
      tvhlog(LOG_ERR, "EPG", "Malformed EPG database, skipping some data");
808
      break;
809
    }
810
    htsmsg_t *m = htsmsg_binary_deserialize(rp, msglen, NULL);
811

    
812
    created += epg_event_create_by_msg(m, now);
813

    
814
    htsmsg_destroy(m);
815
    rp += msglen;
816
    remain -= msglen;
817
  }
818

    
819
  munmap(mem, st.st_size);
820
  close(fd);
821
  tvhlog(LOG_NOTICE, "EPG", "Injected %d event from disk database", created);
822
}
823

    
824
/**
825
 *
826
 */
827
void
828
epg_init(void)
829
{
830
  channel_t *ch;
831

    
832
  epg_load();
833
  
834
  RB_FOREACH(ch, &channel_name_tree, ch_name_link)
835
    epg_ch_check_current_event(ch);
836
}
837

    
838

    
839
/**
840
 * Save the epg on disk
841
 */ 
842
void
843
epg_save(void)
844
{
845
  event_t *e;
846
  int num_saved = 0;
847
  size_t msglen;
848
  void *msgdata;
849
  channel_t *ch;
850

    
851
  int fd = hts_settings_open_file(1, "epgdb");
852

    
853
  RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
854
    RB_FOREACH(e, &ch->ch_epg_events, e_channel_link) {
855
      if(!e->e_start || !e->e_stop)
856
	continue;
857

    
858
      htsmsg_t *m = htsmsg_create_map();
859
      htsmsg_add_u32(m, "ch_id", ch->ch_id);
860
      htsmsg_add_u32(m, "start", e->e_start);
861
      htsmsg_add_u32(m, "stop", e->e_stop);
862
      if(e->e_title != NULL)
863
	htsmsg_add_str(m, "title", e->e_title);
864
      if(e->e_desc != NULL)
865
	htsmsg_add_str(m, "desc", e->e_desc);
866
      if(e->e_dvb_id)
867
	htsmsg_add_u32(m, "dvb_id", e->e_dvb_id);
868

    
869
      if(e->e_episode.ee_season)
870
	htsmsg_add_u32(m, "season", e->e_episode.ee_season);
871

    
872
      if(e->e_episode.ee_episode)
873
	htsmsg_add_u32(m, "episode", e->e_episode.ee_episode);
874

    
875
      if(e->e_episode.ee_part)
876
	htsmsg_add_u32(m, "part", e->e_episode.ee_part);
877

    
878
      if(e->e_episode.ee_onscreen)
879
	htsmsg_add_str(m, "epname", e->e_episode.ee_onscreen);
880

    
881

    
882
      int r = htsmsg_binary_serialize(m, &msgdata, &msglen, 0x10000);
883
      htsmsg_destroy(m);
884

    
885
      if(!r) {
886
	ssize_t written = write(fd, msgdata, msglen);
887
	int err = errno;
888
	free(msgdata);
889
	if(written != msglen) {
890
	  tvhlog(LOG_DEBUG, "epg", "Failed to store EPG on disk -- %s",
891
		 strerror(err));
892
	  close(fd);
893
	  hts_settings_remove("epgdb");
894
	  return;
895
	}
896
      }
897
      num_saved++;
898
    }
899
  }
900
  close(fd);
901
  tvhlog(LOG_DEBUG, "EPG", "Stored EPG data for %d events on disk", num_saved);
902
}
903

    
904

    
905
/**
906
 *
907
 */
908
static void
909
eqr_add(epg_query_result_t *eqr, event_t *e, regex_t *preg, time_t now)
910
{
911
  if(e->e_title == NULL)
912
    return;
913

    
914
  if(preg != NULL && regexec(preg, e->e_title, 0, NULL, 0))
915
    return;
916

    
917
  if(e->e_stop < now)
918
    return; /* Already passed */
919

    
920
  if(eqr->eqr_entries == eqr->eqr_alloced) {
921
    /* Need to alloc more space */
922

    
923
    eqr->eqr_alloced = MAX(100, eqr->eqr_alloced * 2);
924
    eqr->eqr_array = realloc(eqr->eqr_array, 
925
			     eqr->eqr_alloced * sizeof(event_t *));
926
  }
927
  eqr->eqr_array[eqr->eqr_entries++] = e;
928
  e->e_refcount++;
929
}
930

    
931
/**
932
 *
933
 */
934
static void
935
epg_query_add_channel(epg_query_result_t *eqr, channel_t *ch,
936
		      uint8_t content_type, regex_t *preg, time_t now)
937
{
938
  event_t *e;
939

    
940
  if(content_type == 0) {
941
    RB_FOREACH(e, &ch->ch_epg_events, e_channel_link)
942
      eqr_add(eqr, e, preg, now);
943
  } else {
944
    RB_FOREACH(e, &ch->ch_epg_events, e_channel_link)
945
      if(content_type == e->e_content_type)
946
	eqr_add(eqr, e, preg, now);
947
  }
948
}
949

    
950
/**
951
 *
952
 */
953
void
954
epg_query0(epg_query_result_t *eqr, channel_t *ch, channel_tag_t *ct,
955
           uint8_t content_type, const char *title)
956
{
957
  channel_tag_mapping_t *ctm;
958
  time_t now;
959
  regex_t preg0, *preg;
960

    
961
  lock_assert(&global_lock);
962
  memset(eqr, 0, sizeof(epg_query_result_t));
963
  time(&now);
964

    
965
  if(title != NULL) {
966
    if(regcomp(&preg0, title, REG_ICASE | REG_EXTENDED | REG_NOSUB))
967
      return;
968
    preg = &preg0;
969
  } else {
970
    preg = NULL;
971
  }
972

    
973
  if(ch != NULL && ct == NULL) {
974
    epg_query_add_channel(eqr, ch, content_type, preg, now);
975
    return;
976
  }
977
  
978
  if(ct != NULL) {
979
    LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link)
980
      if(ch == NULL || ctm->ctm_channel == ch)
981
	epg_query_add_channel(eqr, ctm->ctm_channel, content_type, preg, now);
982
    return;
983
  }
984

    
985
  RB_FOREACH(ch, &channel_name_tree, ch_name_link)
986
    epg_query_add_channel(eqr, ch, content_type, preg, now);
987
}
988

    
989
/**
990
 *
991
 */
992
void
993
epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
994
	  const char *contentgroup, const char *title)
995
{
996
  channel_t *ch = channel ? channel_find_by_name(channel, 0, 0) : NULL;
997
  channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL;
998
  uint8_t content_type = contentgroup ? 
999
    epg_content_group_find_by_name(contentgroup) : 0;
1000
  epg_query0(eqr, ch, ct, content_type, title);
1001
}
1002

    
1003
/**
1004
 *
1005
 */
1006
void
1007
epg_query_free(epg_query_result_t *eqr)
1008
{
1009
  int i;
1010

    
1011
  for(i = 0; i < eqr->eqr_entries; i++)
1012
    epg_event_unref(eqr->eqr_array[i]);
1013
  free(eqr->eqr_array);
1014
}
1015

    
1016
/**
1017
 * Sorting functions
1018
 */
1019
static int
1020
epg_sort_start_ascending(const void *A, const void *B)
1021
{
1022
  event_t *a = *(event_t **)A;
1023
  event_t *b = *(event_t **)B;
1024
  return a->e_start - b->e_start;
1025
}
1026

    
1027
/**
1028
 *
1029
 */
1030
void
1031
epg_query_sort(epg_query_result_t *eqr)
1032
{
1033
  int (*sf)(const void *a, const void *b);
1034

    
1035
  sf = epg_sort_start_ascending;
1036

    
1037
  qsort(eqr->eqr_array, eqr->eqr_entries, sizeof(event_t *), sf);
1038
}
    (1-1/1)