Fixed mythorphans cron job.
This commit is contained in:
parent
c2f798eed9
commit
89d38844d3
3 changed files with 306 additions and 4 deletions
294
mythtv/files/myth.find_orphans.pl
Executable file
294
mythtv/files/myth.find_orphans.pl
Executable file
|
@ -0,0 +1,294 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# check for recording anomalies -
|
||||
# based somewhat on greg froese's "myth.rebuilddatabase.pl"
|
||||
# -- Lincoln Dale <ltd@interlink.com.au>, September 2006
|
||||
# 2007-03-11: Added pretty print of unknown files vs. orphaned thumbnails.
|
||||
# (Robert Kulagowski) 2008-02-15: Added dryrun and rerecord options (David
|
||||
# George)
|
||||
|
||||
# The intent of this script is to be able to find orphaned rows in the
|
||||
# 'recorded' table (entries which don't have matching media files) and
|
||||
# orphaned media files (potentially taking up gigabytes of otherwise usable
|
||||
# disk space) which have no matching row in the 'recorded' db table.
|
||||
#
|
||||
# By default, running the script will simply return a list of problems it
|
||||
# finds. Running with --dodbdelete will remove db recorded rows for which
|
||||
# there is no matching media file. Running with --dodelete will delete
|
||||
# media files for which there is no matching db record.
|
||||
#
|
||||
# This script may be useful to fix up some orphaned db entries (causes
|
||||
# mythweb to run very slowly) as well as reclaim some disk space from some
|
||||
# orphaned media files. (in an ideal world, neither of these would ever
|
||||
# happen, but I've seen both happen in reality). This script makes it easy
|
||||
# to keep track of whether it has or hasn't happened, even if you have
|
||||
# thousands of recordings and terabytes of stored media.
|
||||
#
|
||||
# no warranties expressed or implied. if you run this and it deletes all
|
||||
# your recordings and sets mythtv to fill up all your disk space with The
|
||||
# Home Shopping Network, its entirely your fault.
|
||||
#
|
||||
# The dryrun option will allow you to see the db entries/files that will be
|
||||
# deleted without actually executing them.
|
||||
# The rerecord option is useful if you lose a hard drive in your storage
|
||||
# group to tell the scheduler to re-record the lost programs (if they happen
|
||||
# to be shown again).
|
||||
|
||||
my $progname = "myth.find_orphans.pl";
|
||||
my $revision = "0.21";
|
||||
|
||||
use DBI;
|
||||
use Sys::Hostname;
|
||||
use Getopt::Long;
|
||||
|
||||
#
|
||||
# options
|
||||
#
|
||||
|
||||
my $opt_host = hostname;
|
||||
my $opt_dbhost = $opt_host;
|
||||
my $opt_database = "mythconverg";
|
||||
my $opt_user = "mythtv";
|
||||
my $opt_pass = "mythtv";
|
||||
my $opt_ext = "{nuv,mpg,mpeg,avi}";
|
||||
my $opt_dir = "";
|
||||
my $opt_dodelete = 0;
|
||||
my $opt_dodbdelete = 0;
|
||||
my $debug = 0;
|
||||
my $opt_help = 0;
|
||||
my $opt_dryrun = 0;
|
||||
my $opt_rerecord = 0;
|
||||
|
||||
GetOptions(
|
||||
'host=s' => \$opt_host,
|
||||
'dbhost=s' => \$opt_dbhost,
|
||||
'database=s' => \$opt_database,
|
||||
'user=s' => \$opt_user,
|
||||
'pass=s' => \$opt_pass,
|
||||
'dir=s' => \$opt_dir,
|
||||
'dodelete' => \$opt_dodelete,
|
||||
'dodbdelete' => \$opt_dodbdelete,
|
||||
'dryrun' => \$opt_dryrun,
|
||||
'rerecord' => \$opt_rerecord,
|
||||
'debug+' => \$debug,
|
||||
'help' => \$opt_help,
|
||||
'h' => \$opt_help,
|
||||
'v' => \$opt_help);
|
||||
|
||||
if ($opt_help) {
|
||||
print<<EOF
|
||||
$progname (rev $revision)
|
||||
(checks MythTV recording directories for orphaned files)
|
||||
|
||||
options:
|
||||
--host=(host) MythTV backend host ($opt_host)
|
||||
--dbhost=(host) host where MySQL database for backend is ($opt_dbhost)
|
||||
--database=(dbname) MythTV database ($opt_database)
|
||||
--user=(user) MySQL MythTV database user ($opt_user)
|
||||
--pass=(pass) MySQL MythTV database password ($opt_pass)
|
||||
--dir=directories manually specify recording directories (otherwise setting is from database)
|
||||
--debug increase debug level
|
||||
--dodbdelete remove recorded db entries with no matching file (default: don't)
|
||||
--dodelete delete files with no record (default: don't)
|
||||
--dryrun display delete actions without doing them
|
||||
--rerecord set db entries to re-record missing files (requires --dodbdelete)
|
||||
|
||||
EOF
|
||||
;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#
|
||||
# go go go!
|
||||
#
|
||||
|
||||
my $valid_recordings = 0;
|
||||
my $missing_recordings = 0;
|
||||
my $errors = 0;
|
||||
my $unknown_files = 0;
|
||||
my $known_files = 0;
|
||||
my $unknown_size = 0;
|
||||
my $known_size = 0;
|
||||
my $unknown_thumbnail = 0;
|
||||
|
||||
if (!($dbh = DBI->connect("dbi:mysql:database=$opt_database:host=$opt_dbhost","$opt_user","$opt_pass"))) {
|
||||
die "Cannot connect to database $opt_database on host $opt_dbhost: $!\n";
|
||||
}
|
||||
|
||||
if ($opt_dir eq "") {
|
||||
&dir_lookup("SELECT dirname FROM storagegroup WHERE hostname=(?) AND groupname != 'DB Backups'");
|
||||
&dir_lookup("SELECT data FROM settings WHERE value='RecordFilePrefix' AND hostname=(?)");
|
||||
|
||||
printf STDERR "Recording directories ($opt_host): $opt_dir\n" if $debug;
|
||||
}
|
||||
|
||||
if ($opt_dir eq "") {
|
||||
printf "ERROR: no directory found or specified\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
foreach $d (split(/,/,$opt_dir)) {
|
||||
$d =~ s/\/$//g; # strip trailing /
|
||||
$dirs{$d}++;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# look in recorded table, make sure we can find every file ..
|
||||
#
|
||||
|
||||
my $q = "SELECT title, subtitle, description, starttime, endtime, chanid, basename FROM recorded WHERE hostname=(?) ORDER BY starttime";
|
||||
$sth = $dbh->prepare($q);
|
||||
$sth->execute($opt_host) || die "Could not execute ($q): $!\n";
|
||||
|
||||
while (my @row=$sth->fetchrow_array) {
|
||||
($title, $subtitle, $description ,$starttime, $endtime, $channel, $basename) = @row;
|
||||
|
||||
# see if we can find it...
|
||||
$loc = find_file($basename);
|
||||
if ($loc eq "") {
|
||||
printf "Missing media: %s (title:%s, start:%s)\n",$basename,$title,$starttime;
|
||||
$missing_recordings++;
|
||||
|
||||
if ($opt_dodbdelete) {
|
||||
$title =~ s/"/\\"/g;
|
||||
$subtitle =~ s/"/\\"/g;
|
||||
$description =~ s/"/\\"/g;
|
||||
my $sql = sprintf "DELETE FROM oldrecorded WHERE title LIKE \"%s\" AND subtitle LIKE \"%s\" AND description LIKE \"%s\" LIMIT 1", $title, $subtitle, $description;
|
||||
printf "unmarking program as recorded: %s\n",$sql;
|
||||
$dbh->do($sql) || die "Could not execute $sql: $!\n";
|
||||
my $sql = sprintf "DELETE FROM recorded WHERE basename LIKE \"%s\" LIMIT 1",$basename;
|
||||
printf "performing database delete: %s\n",$sql;
|
||||
if (!$opt_dryrun) {
|
||||
$dbh->do($sql) || die "Could not execute $sql: $!\n";
|
||||
}
|
||||
|
||||
if ($opt_rerecord) {
|
||||
my $sql = sprintf "UPDATE oldrecorded SET duplicate = 0 where title = \"%s\" and starttime = \"%s\" and chanid = \"%s\"",
|
||||
$title, $starttime, $channel;
|
||||
printf "updating oldrecorded: %s\n", $sql;
|
||||
if (!$opt_dryrun) {
|
||||
$dbh->do($sql) || die "Could not execute $sql: $!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$valid_recordings++;
|
||||
$seen_basename{$basename}++;
|
||||
$seen_basename{$basename.".png"}++; # thumbnail
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# look in recording directories, see if there are extra files not in database
|
||||
#
|
||||
|
||||
foreach my $this_dir (keys %dirs) {
|
||||
opendir(DIR, $this_dir) || die "cannot open directory $this_dir: $!\n";
|
||||
foreach $this_file (readdir(DIR)) {
|
||||
if (-f "$this_dir/$this_file") {
|
||||
|
||||
next if ($this_file eq "nfslockfile.lock");
|
||||
|
||||
my $this_filesize = -s "$this_dir/$this_file";
|
||||
if ($seen_basename{$this_file} == 0) {
|
||||
$sorted_filesizes{$this_filesize} .= sprintf "unknown file [%s]: %s/%s\n",pretty_filesize($this_filesize),$this_dir,$this_file;
|
||||
$unknown_size += $this_filesize;
|
||||
if (substr($this_file,-4) eq ".png") {
|
||||
$unknown_thumbnail++;
|
||||
}
|
||||
else {
|
||||
$unknown_files++;
|
||||
}
|
||||
|
||||
if ($opt_dodelete) {
|
||||
printf STDERR "deleting [%s]: %s/%s\n",pretty_filesize($this_filesize),$this_dir,$this_file;
|
||||
|
||||
if (!$opt_dryrun) {
|
||||
unlink "$this_dir/$this_file";
|
||||
|
||||
if (-f "$this_dir/$this_file") {
|
||||
$errors++;
|
||||
printf "ERROR: could not delete $this_dir/$this_file\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$known_files++;
|
||||
$known_size += $this_filesize;
|
||||
printf "KNOWN file [%s]: %s/%s\n",pretty_filesize($this_filesize),$this_dir,$this_file if $debug;
|
||||
}
|
||||
} else {
|
||||
printf "NOT A FILE: %s/%s\n",$this_dir,$this_file if $debug;
|
||||
}
|
||||
}
|
||||
closedir DIR;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# finished, report results
|
||||
#
|
||||
|
||||
foreach my $key (sort { $a <=> $b } keys %sorted_filesizes) {
|
||||
printf $sorted_filesizes{$key};
|
||||
}
|
||||
|
||||
printf "Summary:\n";
|
||||
printf " Host: %s, Directories: %s\n", $opt_host, join(" ",keys %dirs);
|
||||
printf " %d ERRORS ENCOUNTERED (see above for details)\n",$errors if ($errors > 0);
|
||||
printf " %d valid recording%s, %d missing recording%s %s\n",
|
||||
$valid_recordings, ($valid_recordings != 1 ? "s" : ""),
|
||||
$missing_recordings, ($missing_recordings != 1 ? "s" : ""),
|
||||
($missing_recordings > 0 ? ($opt_dodbdelete ? "were fixed" : "not fixed, check above is valid and use --dodbdelete to fix") : "");
|
||||
printf " %d known media files using %s\n %d orphaned thumbnails with no corresponding recording\n %d unknown files using %s %s\n",
|
||||
$known_files, pretty_filesize($known_size),
|
||||
$unknown_thumbnail,$unknown_files, pretty_filesize($unknown_size),
|
||||
($unknown_files > 0 ? ($opt_dodelete ? "were fixed" : "not fixed, check above and use --dodelete to clean up if the above output is accurate") : "");
|
||||
|
||||
exit(0);
|
||||
|
||||
###########################################################################
|
||||
# filesize bling
|
||||
|
||||
sub pretty_filesize
|
||||
{
|
||||
local($fsize) = @_;
|
||||
return sprintf "%0.1fGB",($fsize / 1000000000) if ($fsize >= 1000000000);
|
||||
return sprintf "%0.1fMB",($fsize / 1000000) if ($fsize >= 1000000);
|
||||
return sprintf "%0.1fKB",($fsize / 1000) if ($fsize >= 1000);
|
||||
return sprintf "%0.0fB",$fsize;
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# find a file in directories without globbing
|
||||
|
||||
sub find_file
|
||||
{
|
||||
local($fname) = @_;
|
||||
|
||||
foreach my $d (keys %dirs) {
|
||||
my $f = $d."/".$fname;
|
||||
if (-e $f) {
|
||||
return $f;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub dir_lookup
|
||||
{
|
||||
my $query = shift;
|
||||
|
||||
$sth = $dbh->prepare($query);
|
||||
$sth->execute($opt_host) || die "Could not execute ($dir_query)";
|
||||
while (my @row = $sth->fetchrow_array) {
|
||||
$opt_dir .= "," if ($opt_dir ne "");
|
||||
$opt_dir .= $row[0];
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
|
@ -19,7 +19,7 @@ mysql -h "${DBHostName}" -u"${DBUserName}" -p"${DBPassword}" -s \
|
|||
"${DBName}" | egrep -q "^[1-9][0-9]*\$" || exit 0
|
||||
|
||||
# find orphans and print stats if found
|
||||
perl /usr/share/doc/mythtv-docs-${MYTHVERSION}/contrib/maintenance/myth.find_orphans.pl \
|
||||
perl /usr/local/bin/myth.find_orphans.pl \
|
||||
--dbhost="${DBHostName}" \
|
||||
--database="${DBName}" \
|
||||
--user="${DBUserName}" \
|
||||
|
|
|
@ -118,11 +118,19 @@ class mythtv::backend {
|
|||
}
|
||||
|
||||
file { "/etc/cron.daily/mythorphans":
|
||||
ensure => present,
|
||||
source => "puppet:///modules/mythtv/mythorphans",
|
||||
mode => "0755",
|
||||
owner => "root",
|
||||
group => "root",
|
||||
require => File["/usr/local/bin/myth.find_orphans.pl"],
|
||||
}
|
||||
file { "/usr/local/bin/myth.find_orphans.pl":
|
||||
ensure => present,
|
||||
source => "puppet:///modules/mythtv/mythorphans",
|
||||
source => "puppet:///modules/mythtv/myth.find_orphans.pl",
|
||||
mode => "0755",
|
||||
owner => root,
|
||||
group => root,
|
||||
owner => "root",
|
||||
group => "root",
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue