Ticket #2363: check_recordings.pl

File check_recordings.pl, 6.1 KB (added by ltd@…, 18 years ago)

contrib/check_recordings.pl

Line 
1#!/usr/bin/perl
2
3# check for recording anomolies -
4#  Lincoln Dale <ltd@interlink.com.au>, September 2006
5#   - checks for entries in 'recorded' table that cannot be found in RecordFilePrefix directories
6#   - files in recording directories that aren't in the 'recorded' table
7#   based somewhat on greg froese's "myth.rebuilddatabase.pl"
8
9my $progname = "check_recordings.pl";
10my $revision = "0.10";
11
12use DBI;
13use Sys::Hostname;
14use Getopt::Long;
15
16#
17# options
18#
19
20my $opt_host =          hostname;
21my $opt_dbhost =        $opt_host;
22my $opt_database =      "mythconverg";
23my $opt_user =          "mythtv";
24my $opt_pass =          "mythtv";
25my $opt_ext =           "{nuv,mpg,mpeg,avi}";
26my $opt_dir =           "";
27my $opt_dodelete =      0;
28my $opt_dodbdelete =    0;
29my $debug =             0;
30my $opt_help =          0;
31
32GetOptions(
33        'host=s'        => \$opt_host,
34        'dbhost=s'      => \$opt_dbhost,
35        'database=s'    => \$opt_database,
36        'user=s'        => \$opt_user,
37        'pass=s'        => \$opt_pass,
38        'dir=s'         => \$opt_dir,
39        'dodelete'      => \$opt_dodelete,
40        'dodbdelete'    => \$opt_dodbdelete,
41        'debug+'        => \$debug,
42        'help'          => \$opt_help,
43        'h'             => \$opt_help,
44        'v'             => \$opt_help);
45
46if ($opt_help) {
47        print<<EOF
48$progname (rev $revision)
49(checks myth recordings directory for anomalies)
50
51options:
52        --host=(host)           myth backend host ($opt_host)
53        --dbhost=(host)         host where mysql database for backend is ($opt_dbhost)
54        --database=(dbname)     myth database ($opt_database)
55        --user=(user)           mysql mythtv database user ($opt_user)
56        --pass=(pass)           mysql mythtv database password ($opt_pass)
57        --dir=directories       manually specify recording directories (otherwise setting is from database)
58        --debug                 increase debug level
59        --dodbdelete            remove recorded db entries with no matching file (default: don't)
60        --dodelete              delete files with no record (default: don't)
61
62EOF
63;
64        exit(0);
65}
66
67#
68# go go go!
69#
70
71my $valid_recordings = 0;
72my $missing_recordings = 0;
73my $errors = 0;
74my $unknown_files = 0;
75my $known_files = 0;
76my $unknown_size = 0;
77my $known_size = 0;
78
79if (!($dbh = DBI->connect("dbi:mysql:database=$opt_database:host=$opt_dbhost","$opt_user","$opt_pass"))) {
80        die "Cannot connect to database $opt_database on host $opt_dbhost: $!\n";
81}
82
83if ($opt_dir eq "") {
84        my $dir_query = "SELECT data FROM settings WHERE value='RecordFilePrefix' AND hostname=(?)";
85        $sth = $dbh->prepare($dir_query);
86        $sth->execute($opt_host) || die "Could not execute ($dir_query)";
87        if (my @row = $sth->fetchrow_array) {
88                $opt_dir = $row[0];
89        }
90        printf STDERR "Recording directories ($opt_host): $opt_dir\n" if $debug;
91}
92
93if ($opt_dir eq "") {
94        printf "ERROR: no directory found or specified\n";
95        exit 1;
96}
97
98foreach $d (split(/,/,$opt_dir)) {
99        push @dirs,$d;
100}
101
102
103#
104# look in recorded table, make sure we can find every file ..
105#
106
107my $q = "SELECT title, subtitle, starttime, endtime, chanid, basename FROM recorded ORDER BY starttime";
108$sth = $dbh->prepare($q);
109$sth->execute || die "Could not execute ($q): $!\n";
110
111while (my @row=$sth->fetchrow_array) {
112        ($title, $subtitle, $starttime, $endtime, $channel, $basename) = @row;
113
114        # see if we can find it...
115        $loc = find_file($basename);
116        if ($loc eq "") {
117                printf "Missing recording: %s\n",$basename;
118                $missing_recordings++;
119
120                if ($do_dbdelete) {
121                        my $sql = sprintf "DELETE FROM recorded WHERE basename LIKE \"%s\" LIMIT 1",$basename;
122                        printf STDERR "performing database delete: %s\n",$sql;
123                        $dbh->do($sql) || die "Could not execute $sql: $!\n";
124                }
125        } else {
126                $valid_recordings++;
127                $seen_basename{$basename}++;
128                $seen_basename{$basename.".png"}++; # thumbnail
129        }
130}
131
132#
133# look in recording directories, see if there are extra files not in database
134#
135
136foreach my $this_dir (@dirs) {
137        opendir(DIR, $this_dir) || die "cannot open directory $this_dir: $!\n";
138        foreach $this_file (readdir(DIR)) {
139                if (-f "$this_dir/$this_file") {
140
141                        next if ($this_file eq "nfslockfile.lock");
142
143                        my $this_filesize = -s "$this_dir/$this_file";
144                        if ($seen_basename{$this_file} == 0) {
145                                $sorted_filesizes{$this_filesize} .= sprintf "unknown file [%s]: %s/%s\n",pretty_filesize($this_filesize),$this_dir,$this_file;
146                                $unknown_size += $this_filesize;
147                                $unknown_files++;
148
149                                if ($opt_dodelete) {
150                                        printf STDERR "deleting  [%s]:  %s/%s\n",pretty_filesize($this_filesize),$this_dir,$this_file;
151                                        unlink "$this_dir/$this_file";
152
153                                        if (-f "$this_dir/$this_file") {
154                                                $errors++;
155                                                printf "ERROR: could not delete $this_dir/$this_file\n";
156                                        }
157                                }
158                        } else {
159                                $known_files++;
160                                $known_size += $this_filesize;
161                                printf "KNOWN file [%s]: %s/%s\n",pretty_filesize($this_filesize),$this_dir,$this_file if $debug;
162                        }
163                } else {
164                        printf "NOT A FILE: %s/%s\n",$this_dir,$this_file if $debug;
165                }
166        }
167        closedir DIR;
168}
169
170
171#
172# finished, report results
173#
174
175foreach my $key (sort { $a <=> $b } keys %sorted_filesizes) {
176        printf $sorted_filesizes{$key};
177}
178
179printf "Summary:\n";
180printf "  %d ERRORS ENCOUNTERED (see above for details)\n",$errors if ($errors > 0);
181printf "  %d valid recordings, %d missing recordings %s\n", $valid_recordings, $missing_recordings, ($missing_recordings > 0 ? ($opt_dodbdelete ? " were fixed" : " not fixed, check above and use --dodbdelete to fix") : "");
182printf "  %d known files using %s, %d unknown files using %s %s\n", $known_files, pretty_filesize($known_size), $unknown_files, pretty_filesize($unknown_size), ($unknown_files > 0 ? ($opt_dodelete ? " were fixed" : " not fixed, check above and use --dodelete to fix if the above output is accurate") : "");
183
184exit(0);
185
186###########################################################################
187# filesize bling
188
189sub pretty_filesize
190{
191        local($fsize) = @_;
192        return sprintf "%5.1fGB",($fsize / 1000000000) if ($fsize >= 1000000000);
193        return sprintf "%5.1fMB",($fsize / 1000000) if ($fsize >= 1000000);
194        return sprintf "%5.1fKB",($fsize / 1000) if ($fsize >= 1000);
195        return sprintf "%6.0fB",$fsize;
196}
197
198###########################################################################
199# find a file in directories without globbing
200
201sub find_file
202{
203        local($fname) = @_;
204
205        foreach my $d (@dirs) {
206                my $f = $d."/".$fname;
207                if (-e $f) {
208                        return $f;
209                }
210        }
211        return;
212}
213
214###########################################################################
215
216