1<?php
2/**
3 * This contains variables and functions related to the Program class
4 *
5 * @license     GPL
6 *
7 * @package     MythWeb
8 * @subpackage  TV
9 *
10 **/
11
12// Reasons a recording wouldn't be happening (from libs/libmythtv/programinfo.h)
13    $RecStatus_Types = array(
14                              '-8' => 'TunerBusy',
15                              '-7' => 'LowDiskSpace',
16                              '-6' => 'Cancelled',
17                              '-5' => 'Deleted',
18                              '-4' => 'Aborted',
19                              '-3' => 'Recorded',
20                              '-2' => 'Recording',
21                              '-1' => 'WillRecord',
22                                0  => 'Unknown',
23                                1  => 'DontRecord',
24                                2  => 'PreviousRecording',
25                                3  => 'CurrentRecording',
26                                4  => 'EarlierShowing',
27                                5  => 'TooManyRecordings',
28                                6  => 'NotListed',
29                                7  => 'Conflict',
30                                8  => 'LaterShowing',
31                                9  => 'Repeat',
32                                10 => 'Inactive',
33                                11 => 'NeverRecord'
34                            );
35
36    $RecStatus_Reasons = array(
37                               'TunerBusy'          => t('recstatus: tunerbusy'),
38                               'LowDiskSpace'       => t('recstatus: lowdiskspace'),
39                               'Cancelled'          => t('recstatus: cancelled'),
40                               'Deleted'            => t('recstatus: deleted'),
41                               'Aborted'            => t('recstatus: stopped'),
42                               'Recorded'           => t('recstatus: recorded'),
43                               'Recording'          => t('recstatus: recording'),
44                               'WillRecord'         => t('recstatus: willrecord'),
45                               'Unknown'            => t('recstatus: unknown'),
46                               'DontRecord'         => t('recstatus: manualoverride'),
47                               'PreviousRecording'  => t('recstatus: previousrecording'),
48                               'CurrentRecording'   => t('recstatus: currentrecording'),
49                               'EarlierShowing'     => t('recstatus: earliershowing'),
50                               'TooManyRecordings'  => t('recstatus: toomanyrecordings'),
51                               'NotListed'          => t('recstatus: notlisted'),
52                               'Conflict'           => t('recstatus: conflict'),
53                               'Repeat'             => t('recstatus: repeat'),
54                               'LaterShowing'       => t('recstatus: latershowing'),
55                               'Inactive'           => t('recstatus: inactive'),
56                               'NeverRecord'        => t('recstatus: neverrecord'),
57                            // A special category for mythweb, since this feature doesn't exist in the backend
58                               'ForceRecord'        => t('recstatus: force_record'),
59                              );
60
61/**
62 * a shortcut to load_all_program_data's single-program query
63 **/
64    function &load_one_program($start_time, $chanid, $manualid) {
65        if ($manualid)
66            $program =& load_all_program_data($start_time, $start_time, $chanid, true, 'program.manualid='.intval($manualid));
67        else
68            $program =& load_all_program_data($start_time, $start_time, $chanid, true);
69        if (!is_object($program) || strcasecmp(get_class($program), 'program'))
70            return NULL;
71        return $program;
72    }
73
74/**
75 * loads all program data for the specified time range.
76 * Set $single_program to true if you only want information about programs that
77 * start exactly at $start_time (used by program_detail.php)
78 **/
79    function &load_all_program_data($start_time, $end_time, $chanid = false, $single_program = false, $extra_query = '', $distinctTitle = false) {
80        global $db;
81    // Don't allow negative timestamps; it confuses MySQL
82        if ($start_time < 0)
83            $start_time = 0;
84        if ($end_time < 0)
85            $end_time = 0;
86    // Make a local hash of channel chanid's with references to the actual
87    // channel data (Channels are not indexed by anything in particular, so
88    // that the user can sort by chanid or channum).
89        $channel_hash = array();
90    // An array (that later gets converted to a string) containing the id's of channels we want to load
91        if ($chanid)
92            $these_channels[] = $chanid;
93        else
94            $these_channels = Channel::getChannelList();
95    // convert $these_channels into a string so it'll go straight into the query
96        if (!count($these_channels))
97            trigger_error("load_all_program_data() attempted with out any channels", FATAL);
98        $these_channels = implode(',', $these_channels);
99    // Build the sql query, and execute it
100    // The weird stuff with pr1 and pr2 is to eliminate the mutiple ratings
101    // per program which causes duplicates to be found.
102    // This code selects the "rater" with the highest name alphabetically,
103    // so for example where raters available for a program are CHVRS ClassInd and VCHIP,
104    // it selects VCHIP.
105        $query = 'SELECT DISTINCT program.*,
106                         UNIX_TIMESTAMP(program.starttime) AS starttime_unix,
107                         UNIX_TIMESTAMP(program.endtime) AS endtime_unix,
108                         IFNULL(pr1.`system`, "") AS rater,
109                         IFNULL(pr1.rating, "") AS rating,
110                         channel.callsign,
111                         channel.channum
112                  FROM program USE INDEX (id_start_end)
113                        LEFT JOIN programrating pr1
114                            on program.chanid = pr1.chanid
115                               and program.starttime = pr1.starttime
116                        LEFT OUTER JOIN programrating pr2
117                            on program.chanid = pr2.chanid
118                               and program.starttime = pr2.starttime
119                               and pr2.`system` > pr1.`system`
120                        LEFT JOIN channel on channel.chanid = program.chanid
121                        LEFT JOIN credits
122                            on program.chanid = credits.chanid
123                               and program.starttime = credits.starttime
124                       LEFT JOIN people USING (person)
125                 WHERE pr2.`system` is null and ';
126    // Only loading a single channel worth of information
127        if ($chanid > 0)
128            $query .= ' program.chanid='.$db->escape($chanid);
129    // Loading a group of channels (probably all of them)
130        else
131            $query .= ' program.chanid IN ('.$these_channels.')';
132    // Requested start time is the same as the end time - don't bother with fancy calculations
133        if ($start_time == $end_time)
134            $query .= ' AND program.starttime = FROM_UNIXTIME('.$db->escape($start_time).')';
135    // We're looking at a time range
136        else
137            $query .= ' AND (program.endtime > FROM_UNIXTIME(' .$db->escape($start_time).')'
138                     .' AND program.starttime < FROM_UNIXTIME('.$db->escape($end_time)  .')'
139                     .' AND program.starttime != program.endtime)';
140    // The extra query, if there is one
141        if ($extra_query)
142            $query .= ' AND '.$extra_query;
143    // Group and sort
144    // FIXME reenable with e.g. ANY_VALUE or rewrite to use the Service API
145    //  if (!$distinctTitle)
146    //      $query .= "\nGROUP BY channel.callsign, program.chanid, program.starttime";
147    //  else
148    //      $query .= "\nGROUP BY program.title";
149        $query .= " ORDER BY program.starttime";
150    // Limit
151        if ($single_program)
152            $query .= "\n LIMIT 1";
153    // Query
154        $sh = $db->query($query);
155    // No results
156        if ($sh->num_rows() < 1) {
157            $sh->finish();
158            return array();
159        }
160    // Build two separate queries for optimized selecting of recstatus
161        $sh2 = $db->prepare('SELECT recstatus
162                               FROM oldrecorded
163                              WHERE recstatus IN (-3, 11)
164                                    AND programid = ?
165                                    AND seriesid  = ?
166                                    AND future = 0
167                             LIMIT 1');
168        $sh3 = $db->prepare('SELECT recstatus
169                               FROM oldrecorded
170                              WHERE recstatus IN (-3, 11)
171                                    AND title       = ?
172                                    AND subtitle    = ?
173                                    AND description = ?
174                                    AND future = 0
175                             LIMIT 1');
176    // Load in all of the programs (if any?)
177        $these_programs = array();
178        $scheduledRecordings = Schedule::findScheduled();
179        while ($data = $sh->fetch_assoc()) {
180            if (!$data['chanid'])
181                continue;
182        // This program has already been loaded, and is attached to a recording schedule
183            if (!empty($data['title']) && $scheduledRecordings[$data['callsign']][$data['starttime_unix']][0]->title == $data['title']) {
184                $program =& $scheduledRecordings[$data['callsign']][$data['starttime_unix']][0];
185            // merge in data fetched from DB
186                $program->merge(new Program($data));
187            }
188        // Otherwise, create a new instance of the program
189            else {
190            // Load the recstatus now that we can use an index
191                if ($data['programid'] && $data['seriesid']) {
192                   $sh2->execute($data['programid'], $data['seriesid']);
193                   list($data['recstatus']) = $sh2->fetch_row();
194                }
195                elseif ($data['category_type'] == 'movie' || ($data['title'] && $data['subtitle'] && $data['description'])) {
196                   $sh3->execute($data['title'], $data['subtitle'], $data['description']);
197                   list($data['recstatus']) = $sh3->fetch_row();
198                }
199            // Create a new instance
200                $program =& Program::find($data);
201            }
202        // Add this program to the channel hash, etc.
203            $these_programs[]                          =& $program;
204            $channel_hash[$data['chanid']]             =  new stdClass();
205            $channel_hash[$data['chanid']]->programs[] =& $program;
206        // Cleanup
207            unset($program);
208        }
209    // Cleanup
210        $sh3->finish();
211        $sh2->finish();
212        $sh->finish();
213    // If channel-specific information was requested, return an array of those programs, or just the first/only one
214        if ($chanid && $single_program)
215            return $these_programs[0];
216    // Just in case, return an array of all programs found
217        return $these_programs;
218    }
219