1 | #!/usr/bin/perl -w
|
---|
2 | # Name: mythical
|
---|
3 | # Version: 0.1
|
---|
4 | # Release: 1
|
---|
5 | # Summary: Generate an iCalendar file of upcoming MythTV recordings
|
---|
6 | # License: GPL - use at your own risk etc.
|
---|
7 | # Copyright: (C) 2008 - David Greenhouse <daveg at dgnode dot screaming dot net>
|
---|
8 | # Description:
|
---|
9 | # Similar to mythtv/contrib/misc_status_info/myth_upcoming_recordings.pl
|
---|
10 | # but generates an iCalendar (RFC2445) file for use with desktop calendar
|
---|
11 | # applications such as KOrganizer.
|
---|
12 | # Why?
|
---|
13 | # The information is already available within MythTV frontend and MythWeb but is
|
---|
14 | # a natural fit for a desktop calendar application. With the data in iCalendar (RFC2445)
|
---|
15 | # format it becomes very portable either by direct access or via synchronisation such
|
---|
16 | # as SyncML. May even get my Palm to run as a IR remote and synchronise the recording
|
---|
17 | # schedule via OBEX!
|
---|
18 | # Usage:
|
---|
19 | # # MythTV class gripes about UPnP missing so redirect stderr if run by cron etc.
|
---|
20 | # mythical.pl 2>/dev/null >~/mythical.ics
|
---|
21 | # TODO:
|
---|
22 | # UTF-8.
|
---|
23 | # Run-time options.
|
---|
24 | # Timezones.
|
---|
25 | # Testing for daylight saving etc.
|
---|
26 | # Suggestions?
|
---|
27 | # Further Development?
|
---|
28 | # Could be merged with 'myth_upcoming_recordings.pl'.
|
---|
29 | # Functionality could be included in MythWeb to provide a live webCal interface.
|
---|
30 | # Use Data::iCal (not in Fedora 8 yet).
|
---|
31 | # Suggestions?
|
---|
32 | # Revision History:
|
---|
33 | # * 2008-05-16 0.1-1 David Greenhouse <daveg at dgnode dot screaming dot net>
|
---|
34 | # - Initial creation.
|
---|
35 |
|
---|
36 | use strict;
|
---|
37 | use warnings;
|
---|
38 | use MythTV;
|
---|
39 |
|
---|
40 | #program version
|
---|
41 | my $VERSION="0.1";
|
---|
42 |
|
---|
43 | # Connect to the MythTV backend
|
---|
44 | my $myth = new MythTV();
|
---|
45 | # Get a list of upcoming recordings
|
---|
46 | my %rows = $myth->backend_rows('QUERY_GETALLPENDING', 2);
|
---|
47 | my $row;
|
---|
48 | my $show;
|
---|
49 | # iCalendar wrapper
|
---|
50 | format_value('BEGIN', 'VCALENDAR');
|
---|
51 | format_value('VERSION', '2.0');
|
---|
52 | format_value('PROGID', "-//MythTV Recording Schedule//NONSGML mythical $VERSION//EN");
|
---|
53 | # Content
|
---|
54 | foreach $row (@{$rows{'rows'}}) {
|
---|
55 | $show = new MythTV::Program(@$row);
|
---|
56 | # Selection criteria...
|
---|
57 | if (include_this($show)) {
|
---|
58 | format_event($show);
|
---|
59 | }
|
---|
60 | }
|
---|
61 | format_value('END', 'VCALENDAR');
|
---|
62 |
|
---|
63 | # Test to include the given programme or not.
|
---|
64 | # May need some more work, run-time options etc.
|
---|
65 | sub include_this {
|
---|
66 | my $prog = shift;
|
---|
67 | return $prog->{'recstatus'} == $MythTV::recstatus_willrecord;
|
---|
68 | }
|
---|
69 |
|
---|
70 | # Output an iCalendar entry for the given programme.
|
---|
71 | sub format_event {
|
---|
72 | my $prog = shift;
|
---|
73 | format_value('BEGIN', 'VEVENT');
|
---|
74 | format_value('DTSTAMP', format_date(time()));
|
---|
75 | format_value('UID', format_uid($prog));
|
---|
76 | format_value('DTSTART', format_date($prog->{'starttime'}));
|
---|
77 | format_value('DTEND', format_date($prog->{'endtime'}));
|
---|
78 | format_value('LAST-MODIFIED', format_date($prog->{'lastmodified'}));
|
---|
79 | if ($prog->{'subtitle'} ne 'Untitled') {
|
---|
80 | format_value('SUMMARY', $prog->{'title'} . ' - "' . $prog->{'subtitle'} . '"');
|
---|
81 | } else {
|
---|
82 | format_value('SUMMARY', $prog->{'title'});
|
---|
83 | }
|
---|
84 | format_value('DESCRIPTION', $prog->{'description'});
|
---|
85 | format_value('LOCATION', $prog->{'channel'}{'name'});
|
---|
86 | format_value('CATEGORIES', $prog->{'category'});
|
---|
87 | format_value('TRANSP', $prog->{'recstatus'} == $MythTV::recstatus_willrecord ? 'OPAQUE' : 'TRANSPARENT');
|
---|
88 | format_value('STATUS', $prog->{'recstatus'} == $MythTV::recstatus_willrecord ? 'CONFIRMED' : 'TENTATIVE');
|
---|
89 | format_value('PRIORITY', format_priority($prog));
|
---|
90 | format_value('X-MYTHTV-RECSTATUS', $MythTV::RecStatus_Types{$prog->{'recstatus'}});
|
---|
91 | format_value('END', 'VEVENT');
|
---|
92 | }
|
---|
93 |
|
---|
94 | # Format an entry line with the given tag and value.
|
---|
95 | # Escapes and wraps, terminate with CR-NL.
|
---|
96 | sub format_value {
|
---|
97 | my $tag = shift;
|
---|
98 | my $value = shift;
|
---|
99 | # Escapes
|
---|
100 | $value =~ s/;/\\;/g;
|
---|
101 | $value =~ s/,/\\,/g;
|
---|
102 | $value =~ s/"/\\"/g;
|
---|
103 | # Wrap at col 76
|
---|
104 | my $text = $tag . ":" . $value;
|
---|
105 | while (length($text) > 76) {
|
---|
106 | print substr($text, 0, 76) . "\r\n";
|
---|
107 | $text = ' ' . substr($text, 76);
|
---|
108 | }
|
---|
109 | if ($text ne ' ') {
|
---|
110 | print $text . "\r\n";
|
---|
111 | }
|
---|
112 | }
|
---|
113 |
|
---|
114 | # Format a date/time value as an iCalendar Date/Time
|
---|
115 | sub format_date {
|
---|
116 | my $localtime = shift;
|
---|
117 | my ($ts,$tm,$th,$dd,$dm,$dy) = localtime($localtime);
|
---|
118 | # YYYYMMDDThhmmss
|
---|
119 | return sprintf "%4.4d%2.2d%2.2dT%2.2d%2.2d%2.2d", $dy + 1900, $dm + 1, $dd, $th, $tm, $ts;
|
---|
120 | }
|
---|
121 |
|
---|
122 | # Generate a unique identifier for the given programme.
|
---|
123 | # chanid_starttime@hostname
|
---|
124 | sub format_uid {
|
---|
125 | my $prog = shift;
|
---|
126 | my ($ts,$tm,$th,$dd,$dm,$dy) = localtime($prog->{'starttime'});
|
---|
127 | return sprintf "%4.4s_%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d@%s", $prog->{'chanid'}, $dy + 1900, $dm + 1, $dd, $th, $tm, $ts, $prog->{'hostname'};
|
---|
128 | }
|
---|
129 |
|
---|
130 | # Simple classification of recording priority in to three levels.
|
---|
131 | sub format_priority {
|
---|
132 | my $prog = shift;
|
---|
133 | my $pri = $prog->{'recpriority'};
|
---|
134 | if ($pri < 0) {
|
---|
135 | return 'LOW';
|
---|
136 | } elsif ($pri > 0) {
|
---|
137 | return 'HIGH';
|
---|
138 | }
|
---|
139 | return 'MEDIUM';
|
---|
140 | }
|
---|