| 1 | #!/usr/bin/perl -w |
| 2 | # |
| 3 | # Provides notification of upcoming recordings. |
| 4 | # |
| 5 | # Automatically detects database settings. |
| 6 | # |
| 7 | |
| 8 | # Includes |
| 9 | use DBI; |
| 10 | use Getopt::Long; |
| 11 | use MythTV; |
| 12 | |
| 13 | # Some variables we'll use here |
| 14 | our ($num_recordings, $heading, $plain_text, $text_format, $usage); |
| 15 | our ($hours, $minutes, $seconds, $no_conflicts_message); |
| 16 | our ($scheduled, $duplicates, $deactivated, $conflicts); |
| 17 | our ($dnum_recordings, $dheading, $dtext_format); |
| 18 | our ($dhours, $dminutes, $dseconds, $dno_conflicts_message); |
| 19 | our ($dscheduled, $dduplicates, $ddeactivated, $dconflicts); |
| 20 | our ($status_text_format, $status_value_format); |
| 21 | our ($dstatus_text_format, $dstatus_value_format); |
| 22 | |
| 23 | # Default number of upcoming recordings to show |
| 24 | $dnum_recordings = 5; |
| 25 | # Default period in which to show recordings |
| 26 | $dhours = -1; |
| 27 | $dminutes = -1; |
| 28 | $dseconds = -1; |
| 29 | # Default recording status types to show |
| 30 | $dscheduled = 1; |
| 31 | $dduplicates = 0; |
| 32 | $ddeactivated = 0; |
| 33 | $dconflicts = 1; |
| 34 | # Default status output heading |
| 35 | $dheading='Upcoming Recordings:\n'; |
| 36 | # Default format of plain-text output |
| 37 | $dtext_format='%rs\n%n/%j, %g:%i %A - %cc\n%T - %S\n%R\n\n'; |
| 38 | # Default "no conflicts" message |
| 39 | $dno_conflicts_message='No conflicts.\n'; |
| 40 | # Default format of status output display text |
| 41 | $dstatus_text_format= '<a href="#">%rs - %n/%j, %g:%i %A - %cc - '. |
| 42 | '%T - %S<br />'. |
| 43 | '<span><strong>%T</strong> %n/%j, %g:%i %A<br />'. |
| 44 | '<em>%S</em><br /><br />%R<br /></span></a><hr />'; |
| 45 | # Default format of status output value |
| 46 | $dstatus_value_format = '%n/%j, %g:%i %A - %T - %S'; |
| 47 | |
| 48 | # Provide default values for GetOptions |
| 49 | $num_recordings = $dnum_recordings; |
| 50 | $hours = $dhours; |
| 51 | $minutes = $dminutes; |
| 52 | $seconds = $dseconds; |
| 53 | $scheduled = $dscheduled; |
| 54 | $duplicates = $dduplicates; |
| 55 | $deactivated = $ddeactivated; |
| 56 | $conflicts = $dconflicts; |
| 57 | $heading = $dheading; |
| 58 | $text_format = $dtext_format; |
| 59 | $no_conflicts_message = $dno_conflicts_message; |
| 60 | $status_text_format = $dstatus_text_format; |
| 61 | $status_value_format = $dstatus_value_format; |
| 62 | |
| 63 | # Load the cli options |
| 64 | GetOptions('num_recordings|recordings=s' => \$num_recordings, |
| 65 | 'hours|o=i' => \$hours, |
| 66 | 'minutes=i' => \$minutes, |
| 67 | 'seconds|e=i' => \$seconds, |
| 68 | 'show_scheduled|_show_scheduled|scheduled|_scheduled|e!' |
| 69 | => \$scheduled, |
| 70 | 'show_duplicates|_show_duplicates|duplicates|_duplicates|p!' |
| 71 | => \$duplicates, |
| 72 | 'show_deactivated|_show_deactivated|deactivated|_deactivated|v!' |
| 73 | => \$deactivated, |
| 74 | 'show_conflicts|_show_conflicts|conflicts|_conflicts!' |
| 75 | => \$conflicts, |
| 76 | 'heading=s' => \$heading, |
| 77 | 'plain_text' => \$plain_text, |
| 78 | 'text_format=s' => \$text_format, |
| 79 | 'no_conflicts_message=s' => \$no_conflicts_message, |
| 80 | 'status_text_format=s' => \$status_text_format, |
| 81 | 'status_value_format=s' => \$status_value_format, |
| 82 | 'usage|help' => \$usage |
| 83 | ); |
| 84 | |
| 85 | # Print usage |
| 86 | if ($usage) { |
| 87 | # Make default "--show_*" options readable |
| 88 | $dscheduled = ($dscheduled ? '--show_scheduled' : |
| 89 | '--no_show_scheduled'); |
| 90 | $dduplicates = ($dduplicates ? '--show_duplicates' : |
| 91 | '--no_show_duplicates'); |
| 92 | $ddeactivated = ($ddeactivated ? '--show_deactivated' : |
| 93 | '--no_show_deactivated'); |
| 94 | $dconflicts = ($dconflicts ? '--show_conflicts' : |
| 95 | '--no_show_conflicts'); |
| 96 | print <<EOF; |
| 97 | $0 usage: |
| 98 | |
| 99 | options: |
| 100 | |
| 101 | --recordings [number of recordings] |
| 102 | |
| 103 | Outputs information on the next [number of recordings] shows to be recorded |
| 104 | by MythTV and that match the criteria specified for --scheduled, |
| 105 | --duplicates, --deactivated, and --conflicts. To output information on all |
| 106 | matching recordings, specify -1. |
| 107 | |
| 108 | default: $dnum_recordings |
| 109 | |
| 110 | --hours [number of hours] |
| 111 | |
| 112 | Outputs information on recordings starting in the next [number of hours] |
| 113 | and that match the criteria specified for --scheduled, --duplicates, |
| 114 | --deactivated, and --conflicts. This option may be specified in |
| 115 | conjunction with --minutes and --seconds. To output information on all |
| 116 | matching recordings regardless of start time, specify -1 for --hours, |
| 117 | --minutes, and --seconds. |
| 118 | |
| 119 | default: $dhours |
| 120 | |
| 121 | --minutes [number of minutes] |
| 122 | |
| 123 | Outputs information on recordings starting in the next [number of minutes] |
| 124 | and that match the criteria specified for --scheduled, --duplicates, |
| 125 | --deactivated, and --conflicts. This option may be specified in |
| 126 | conjunction with --hours and --seconds. To output information on all |
| 127 | matching recordings regardless of start time, specify -1 for --hours, |
| 128 | --minutes, and --seconds. |
| 129 | |
| 130 | default: $dminutes |
| 131 | |
| 132 | --seconds [number of seconds] |
| 133 | |
| 134 | Outputs information on recordings starting in the next [number of seconds] |
| 135 | and that match the criteria specified for --scheduled, --duplicates, |
| 136 | --deactivated, and --conflicts. This option may be specified in |
| 137 | conjunction with --hours and --minutes. To output information on all |
| 138 | matching recordings regardless of start time, specify -1 for --hours, |
| 139 | --minutes, and --seconds. |
| 140 | |
| 141 | default: $dseconds |
| 142 | |
| 143 | --show_scheduled|--no_show_scheduled |
| 144 | |
| 145 | Outputs information about scheduled recordings. Scheduled recordings are |
| 146 | those that MythTV plans to actually record. |
| 147 | |
| 148 | default: $dscheduled |
| 149 | |
| 150 | --show_duplicates|--no_show_duplicates |
| 151 | |
| 152 | Outputs information about duplicate recordings. Duplicate recordings are |
| 153 | those that will not be recorded because of the specified duplicate matching |
| 154 | policy for the rule. |
| 155 | |
| 156 | default: $dduplicates |
| 157 | |
| 158 | --show_deactivated|--no_show_deactivated |
| 159 | |
| 160 | Outputs information about deactivated recordings. Deactivated recordings |
| 161 | are those that MythTV will not record because the schedule is inactive, |
| 162 | because the showing was set to never record, because the show is being |
| 163 | recorded in an earlier or later showing, because there are too many |
| 164 | recordings or not enough disk space to allow the recording, or because |
| 165 | the show you\'ve specified for recording is not listed in the timeslot |
| 166 | specified. |
| 167 | |
| 168 | default: $ddeactivated |
| 169 | |
| 170 | --show_conflicts|--no_show_conflicts |
| 171 | |
| 172 | Outputs information about conflicts (those shows that MythTV cannot record |
| 173 | because of other higher-priority scheduled recordings). |
| 174 | |
| 175 | default: $dconflicts |
| 176 | |
| 177 | --heading [heading] |
| 178 | Output the [heading] before printing information about recordings. |
| 179 | |
| 180 | default: \'$dheading\' |
| 181 | |
| 182 | --plain_text |
| 183 | Output information in plain text format (i.e. for inclusion in an e-mail |
| 184 | notification). |
| 185 | |
| 186 | --text_format [format] |
| 187 | Use the provided [format] to display information on the recordings. The |
| 188 | format should use the same format specifiers used by mythrename.pl, but |
| 189 | may also use \\r and/or \\n for line breaks and %rs for recording status. |
| 190 | This option is ignored if --plain_text is not used. |
| 191 | |
| 192 | default: \'$dtext_format\' |
| 193 | |
| 194 | --no_conflicts_message [message] |
| 195 | Use the provided [message] to specify there are no conflicts. This option |
| 196 | is used when only information about conflicts is requested and there are |
| 197 | no conflicts. I.e. it is only used with the combination of show_* |
| 198 | options --show_conflicts, --no_show_scheduled, --no_show_deactivated, |
| 199 | and --no_show_duplicates . |
| 200 | |
| 201 | default: \'$dno_conflicts_message\' |
| 202 | |
| 203 | --help |
| 204 | |
| 205 | Show this help text. |
| 206 | |
| 207 | EOF |
| 208 | exit; |
| 209 | } |
| 210 | |
| 211 | # Determine the period of interest |
| 212 | my $now = time(); |
| 213 | my $start_before = $now; |
| 214 | $start_before = $start_before + ($hours * 3600) if ($hours > 0); |
| 215 | $start_before = $start_before + ($minutes * 60) if ($minutes > 0); |
| 216 | $start_before = $start_before + $seconds if ($seconds > 0); |
| 217 | $start_before = 0 if (!($start_before > $now)); |
| 218 | |
| 219 | # Fix the heading. |
| 220 | if (defined($plain_text)) { |
| 221 | $heading =~ s/\\r/\r/g; |
| 222 | $heading =~ s/\\n/\n/g; |
| 223 | } |
| 224 | else { |
| 225 | # Remove line break format specifiers from heading for status output |
| 226 | $heading =~ s/(\\r|\\n)//g; |
| 227 | } |
| 228 | |
| 229 | # Connect to mythbackend |
| 230 | my $Myth = new MythTV(); |
| 231 | |
| 232 | # Get the list of recordings |
| 233 | my $count = 0; |
| 234 | my %rows = $Myth->backend_rows('QUERY_GETALLPENDING', 2); |
| 235 | my $has_conflicts = $rows{'offset'}[0]; |
| 236 | if ((!$has_conflicts) && |
| 237 | (($conflicts) && |
| 238 | (!(($scheduled) || ($duplicates) || ($deactivated))))) { |
| 239 | $no_conflicts_message =~ s/\\r/\r/g; |
| 240 | $no_conflicts_message =~ s/\\n/\n/g; |
| 241 | print "$no_conflicts_message"; |
| 242 | exit 0; |
| 243 | } |
| 244 | my $num_scheduled = $rows{'offset'}[1]; |
| 245 | our $show; |
| 246 | foreach my $row (@{$rows{'rows'}}) { |
| 247 | last unless (($count < $num_recordings) || ($num_recordings < 0)); |
| 248 | $show = new MythTV::Program(@$row); |
| 249 | last if (($start_before) && ($show->{'recstartts'} > $start_before)); |
| 250 | next if ((!$scheduled) && (is_scheduled($show->{'recstatus'}))); |
| 251 | next if ((!$duplicates) && (is_duplicate($show->{'recstatus'}))); |
| 252 | next if ((!$deactivated) && (is_deactivated($show->{'recstatus'}))); |
| 253 | next if ((!$conflicts) && (is_conflict($show->{'recstatus'}))); |
| 254 | |
| 255 | # Print the recording information in the desired format |
| 256 | if (defined($plain_text)) { |
| 257 | text_print($count); |
| 258 | } |
| 259 | else { |
| 260 | status_print($count); |
| 261 | } |
| 262 | $count++; |
| 263 | } |
| 264 | |
| 265 | # Returns true if the show is scheduled to record |
| 266 | sub is_scheduled { |
| 267 | my $recstatus = (shift() or 0); |
| 268 | return (($MythTV::recstatus_willrecord == $recstatus) || |
| 269 | ($MythTV::recstatus_recorded == $recstatus) || |
| 270 | ($MythTV::recstatus_recording == $recstatus)); |
| 271 | } |
| 272 | |
| 273 | # Returns true if the show is a duplicate |
| 274 | sub is_duplicate { |
| 275 | my $recstatus = (shift() or 0); |
| 276 | return (($MythTV::recstatus_repeat == $recstatus) || |
| 277 | ($MythTV::recstatus_previousrecording == $recstatus) || |
| 278 | ($MythTV::recstatus_currentrecording == $recstatus)); |
| 279 | } |
| 280 | |
| 281 | # Returns true if the recording is deactivated |
| 282 | sub is_deactivated { |
| 283 | my $recstatus = (shift() or 0); |
| 284 | return (($MythTV::recstatus_inactive == $recstatus) || |
| 285 | ($MythTV::recstatus_toomanyrecordings == $recstatus) || |
| 286 | ($MythTV::recstatus_cancelled == $recstatus) || |
| 287 | ($MythTV::recstatus_deleted == $recstatus) || |
| 288 | ($MythTV::recstatus_aborted == $recstatus) || |
| 289 | ($MythTV::recstatus_notlisted == $recstatus) || |
| 290 | ($MythTV::recstatus_dontrecord == $recstatus) || |
| 291 | ($MythTV::recstatus_lowdiskspace == $recstatus) || |
| 292 | ($MythTV::recstatus_tunerbusy == $recstatus) || |
| 293 | ($MythTV::recstatus_neverrecord == $recstatus) || |
| 294 | ($MythTV::recstatus_earliershowing == $recstatus) || |
| 295 | ($MythTV::recstatus_latershowing == $recstatus)); |
| 296 | } |
| 297 | |
| 298 | # Returns true if the show cannot be recorded due to a conflict |
| 299 | sub is_conflict { |
| 300 | my $recstatus = (shift() or 0); |
| 301 | return ($MythTV::recstatus_conflict == $recstatus); |
| 302 | } |
| 303 | |
| 304 | # Print the output for use in the backend status page. |
| 305 | sub status_print { |
| 306 | my $count = shift; |
| 307 | my $text = $show->format_name($status_text_format, ' ', ' ', 1, 0 ,1); |
| 308 | $text =~ s/%rs/$MythTV::RecStatus_Types{$show->{'recstatus'}}/g; |
| 309 | my $value = $show->format_name($status_value_format, ' ', ' ', |
| 310 | 1, 0 ,1); |
| 311 | $value =~ s/%rs/$MythTV::RecStatus_Types{$show->{'recstatus'}}/g; |
| 312 | print("$heading<div class=\"schedule\">") if ($count == 0); |
| 313 | print("$text"); |
| 314 | print("</div>") if ($count == ($num_recordings - 1)); |
| 315 | print("[]:[]recording$count"); |
| 316 | print("[]:[]$value\n"); |
| 317 | } |
| 318 | |
| 319 | # Print the output in plain text format |
| 320 | sub text_print { |
| 321 | my $count = shift; |
| 322 | my $text = $show->format_name($text_format, ' ', ' ', 1, 0 ,1); |
| 323 | $text =~ s/%rs/$MythTV::RecStatus_Types{$show->{'recstatus'}}/g; |
| 324 | $text =~ s/\\r/\r/g; |
| 325 | $text =~ s/\\n/\n/g; |
| 326 | print("$heading") if ($count == 0); |
| 327 | print("$text"); |
| 328 | } |
| 329 | |