diff options
-rwxr-xr-x | videomv.pl | 397 |
1 files changed, 261 insertions, 136 deletions
@@ -7,24 +7,25 @@ # See http://sam.zoy.org/wtfpl/COPYING for more details. -$VERSION = "0.1, 08 August 2011"; +$VERSION = "0.1, 10 August 2011"; -use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat - bundling auto_version auto_help); +use Getopt::Long qw/:config posix_default no_ignore_case gnu_compat + bundling auto_version auto_help/; use Pod::Usage; use DBI; use File::Basename; +use File::Spec::Functions qw /catfile catdir splitdir updir/; +use File::Copy; +use Cwd qw /realpath/; +use Env qw /HOME/; use strict; ################################################################################ -# This path has to be absolute! -my $video = "/home/guilhem/video"; +# Configuration +my $symlinks = catdir($HOME,'video','MOVIES'); # Symlinks folder +my $directors = catdir($HOME,'video','DIRECTORS'); # Directors folder -my $symlinks = "MOVIES"; # Symlinks folder in $video -my $directors = "DIRECTORS"; # Directors folder in $video - -# Database configuration my $driver = "mysql"; my $database = "videodb"; my $hostname = "127.0.0.1"; @@ -41,7 +42,8 @@ videomv.pl - TODO =head1 SYNOPSIS -B<videomv.pl> [B<--sort>] I<path/to/oldfile> [I<.../>{I<MOVIES>,I<DIRECTORS/director/>}I</>[I<newfile>]] +B<videomv.pl> [B<--sort>] +I<path/to/oldfile> [I<.../>{I<MOVIES>,I<DIRECTORS/director/>}I</>[I<newfile>]] video |- ... @@ -71,14 +73,15 @@ actions will be performed: =over 4 -=item * The filename in the database will be updated, -=item * the old target will be renamed to I<../DIRECTORS/director/newfile>, +=item * The old target will be renamed to I<../DIRECTORS/director/newfile>, -=item * the old symlink will be deleted, and +=item * the old symlink will be deleted, =item * a new symlink I<.../MOVIES/newfile> -> I<../DIRECTORS/director/newfile> -will be created. +will be created, and + +=item * the filename in the database will be updated. =back @@ -97,16 +100,16 @@ Otherwise, the following actions will be performed: =over 4 -=item * The filename in the database will be updated if I<oldfile><>I<newfile>, - -=item * the old target will be moved to I<../DIRECTORS/newdirector/newfile>, +=item * The old target will be moved to I<../DIRECTORS/newdirector/newfile>, (or to I<../DIRECTORS/newdirector/oldfile> if I<newfile> was not given), -=item * the old symlink will be deleted, and +=item * the old symlink will be deleted, =item * a new symlink I<.../MOVIES/newfile> -> I<../DIRECTORS/newdirector/newfile> (or -> I<../DIRECTORS/newdirector/oldfile> if I<newfile> was not given) -will be created. +will be created, and + +=item * the filename in the database will be updated if I<oldfile><>I<newfile>. =back @@ -120,15 +123,15 @@ actions will be performed: =over 4 -=item * The filename in the database will be updated, - -=item * the old target I<../DIRECTORS/director/oldfile> will be renamed to +=item * The old target I<../DIRECTORS/director/oldfile> will be renamed to I<../DIRECTORS/director/newfile>, -=item * the old symlink will be deleted, and +=item * the old symlink will be deleted, =item * a new symlink I<.../MOVIES/newfile> -> I<../DIRECTORS/director/newfile> -will be created. +will be created, and + +=item * the filename in the database will be updated. =back @@ -144,59 +147,62 @@ Otherwise, the following actions will be performed: =over 4 -=item * The filename in the database will be updated if I<oldfile><>I<newfile>, - -=item * the old target I<../DIRECTORS/olddirector/oldfile> will be moved to +=item * The old target I<../DIRECTORS/olddirector/oldfile> will be moved to I<../DIRECTORS/newdirector/newfile>, -=item * the old symlink will be deleted, and +=item * the old symlink will be deleted, =item * a new symlink I<.../MOVIES/newfile> -> I<../DIRECTORS/newdirector/newfile> -will be created. +will be created, and + +=item * the filename in the database will be updated if I<oldfile><>I<newfile>. =back =item B<videomv.pl> B<--sort> I<path/to/oldfile> [I<.../MOVIES/newfile>] -Where I<path/to/> is neither of I<.../MOVIES/> nor I<.../DIRECTORS/*/>. +Where I<path/to/> is neither of I<.../MOVIES/> nor I<.../DIRECTORS/*/>, +and I<oldfile> is a regular file. The director will looked for in the database, and and error will be raised if no entry is found. Otherwise, the following actions will be performed: =over 4 -=item * The filename in the database will be updated if I<oldfile><>I<newfile>, - -=item * the old file will be moved to I<../DIRECTORS/director/newfile> +=item * The old file will be moved to I<../DIRECTORS/director/newfile> (or to I<../DIRECTORS/director/oldfile> if the second argument was not given), =item * a symlink I<.../MOVIES/newfile> -> I<../DIRECTORS/director/newfile> (or I<.../MOVIES/oldfile> -> I<../DIRECTORS/director/oldfile> if the second -argument was not given) will be created. +argument was not given) will be created, and + +=item * the filename in the database will be updated if I<oldfile><>I<newfile>. =back -Note that the B<--sort> has to set for this to work; if not, an error +Note that the B<--sort> has to be set for this to work; if not, an error will be raised. =item B<videomv.pl> I<path/to/oldfile> I<.../DIRECTOR/director/>[I<newfile>] -Where I<path/to/> is neither of I<.../MOVIES/> nor I<.../DIRECTORS/*/>. +Where I<path/to/> is neither of I<.../MOVIES/> nor I<.../DIRECTORS/*/>, +and I<oldfile> is a regular file. The following actions will be performed: =over 4 -=item * The filename in the database will be updated if I<oldfile><>I<newfile>, - -=item * the old file will be moved to I<../DIRECTORS/director/newfile> +=item * The old file will be moved to I<../DIRECTORS/director/newfile> (or to I<../DIRECTORS/director/oldfile> if I<newfile> was not given), =item * a symlink I<.../MOVIES/newfile> -> I<../DIRECTORS/director/newfile> (or I<.../MOVIES/oldfile> -> I<../DIRECTORS/director/oldfile> if the second -argument was not given) will be created. +argument was not given) will be created, + +=item * the filename in the database will be updated if I<oldfile><>I<newfile>. + =back @@ -211,6 +217,10 @@ argument was not given) will be created. TODO +=item B<--fail> + +TODO + =back =head1 EXIT STATUS @@ -229,12 +239,11 @@ Copyright 2011 Guilhem Moulin. See the source for copying conditions. ################################################################################ -my $man; -my $quiet; - +my $sort; # Get options -GetOptions( "q|quiet" => sub { open LOG, '>', '/dev/null' +GetOptions( "sort" => \$sort, + "q|quiet" => sub { open LOG, '>', '/dev/null' or die "Cannot open `/dev/null': $!" }, "man" => sub { pod2usage(-exitstatus => 0, -verbose => 2) } ) @@ -242,12 +251,28 @@ GetOptions( "q|quiet" => sub { open LOG, '>', '/dev/null' pod2usage(2) if ($#ARGV < 0 or $#ARGV > 1); *LOG = *STDERR unless defined (fileno LOG); +my ($old_path,$new_path) = @ARGV; +$new_path = $symlinks unless defined $new_path; + +die "Error: `" .$old_path. "' does not exist\n" + unless (-l $old_path or -f $old_path); -# File to rename/sort, with its path -my $old_file = $ARGV[0]; +$new_path = catfile ($new_path, basename($old_path)) + if -d realpath($new_path); +die "Error: `" .$new_path. "' exists\n" + if (-l $new_path or -f $new_path); -# Basenames of files to rename to / sort -my ($old_filename, $new_filename) = map {basename($_)} @ARGV; +my $old_filename = basename($old_path); +my $new_filename = basename($new_path); + +# Get the real paths, {..,symlink}-collapse and so on +my $real_old_path = catfile (realpath (dirname($old_path)), $old_filename); +my $real_new_path = catfile (realpath (dirname($new_path)), $new_filename); + +my $real_symlinks = realpath($symlinks); +my @real_symlinks = splitdir($real_symlinks); +my $real_directors = realpath($directors); +my @real_directors = splitdir($real_directors); # Connect to database @@ -263,120 +288,183 @@ my $RES = WHERE filename = ?", 'id', undef, $old_filename ) - or die "Can't select: " . $dbh->errstr; - + or die "Can't select: " .$dbh->errstr. "\n"; my $nRES = scalar (keys %$RES); -die "Error: no entry entry found in the database.\n" if $nRES == 0; -die "Error: $nRES > 1 entries found in the database.\n" if $nRES > 1; +# The ID of the first movie found in the database, if any +my $id = each %$RES; +my $r; +my ($old_director, $new_director); +my ($old_symlink, $new_symlink, $new_target, $old_target); -# The *unique* ID of the movie found in the database -my $id = each %$RES; +if (&is_symlink($real_new_path)) { + # The destination is under $symlinks -print LOG "One entry found: " . - $RES->{$id}->{director} . " - " . $RES->{$id}->{title} . "\n"; + if (defined $sort) { + # Find the new director on the database -if (defined $new_filename) { -# print LOG "Update filename for " . -# $RES->{$id}->{director} . " - " . $RES->{$id}->{title} . -# ":\n"; -# print LOG "`" . $old_filename . "' -> `" . $new_filename, "'\n"; + # Ensure there is exactly one entry in the db + die "Error: No entry found in the database while the flag `--sort' was set.\n" + if $nRES == 0; + die "Error: Multiples entries found in the database while the flag `--sort' was set.\n" + if $nRES > 1; - &rename ($id, $old_filename, $new_filename); -} -else { - # TODO: this might not be a valid dirname! - my $dirname = $directors .'/'. $RES->{$id}->{director}; -# print LOG "Sort " . -# $RES->{$id}->{director} . " - " . $RES->{$id}->{title} . ":\n"; -# print LOG "`" . $old_filename . "' -> `" . $dirname . "'\n"; + $new_director = $RES->{$id}->{director}; + $new_director =~ s/ ?: ?/ -/; + $new_director =~ tr@/@_@; + $new_director =~ s/[[:cntrl:]]//; + + # Create directory if it doesn't exist + my $new_dirname = catfile ($directors, $new_director); + unless (-d $new_dirname) { + print STDERR "Directory `$new_dirname' does not exist. "; + until (-d $new_dirname) { + print STDERR "Should I create it? (Y/n) "; + my $a = lc <STDIN>; + chomp $a; + if ($a eq 'y' or $a eq '') { + mkdir $new_dirname + or die "Error: Cannot mkdir `$new_dirname': $!\n"; + } + elsif ($a eq 'n') { + exit 0; + } + } + } + } - &arrange ($old_file, $dirname); -} - -# Disconnect -$dbh->disconnect(); + $new_symlink = $new_path; + if (&is_symlink($real_old_path)) { -################################################################################ + $old_symlink = $old_path; -# TODO: find the target from the database instead (option) -sub rename { - my ($id, $old_filename, $new_filename) = @_; - my $r = 0; - - print LOG "Updating database... "; - my $rv = $dbh->do ( "UPDATE $videodata SET filename = ? WHERE id = ?", - undef, $new_filename, $id - ); - &ack (\$r, $rv); - - my $old_symlink = $video .'/'. $symlinks .'/'. $old_filename; - my $new_symlink = $video .'/'. $symlinks .'/'. $new_filename; - - unless (-l $old_symlink) { - print LOG "Warning: `$old_symlink' does not exist!\n"; - return 1; + # Source is presumably a symlink: ensure it is + die "Error: `" .$old_path. "' is expected to be a symlink.\n" + unless -l $real_old_path; + + $old_target = realpath ($old_path); + die "Error: `" .$old_path. "' is expected to target to `" + .catfile(updir(),'DIRECTORS','*',$old_filename). "'.\n" + unless (defined $new_director + || &is_director(\$new_director, $old_target)); + $new_target = catfile ($directors, $new_director, $new_filename); } - chdir $video .'/'. $symlinks - or die "Can't cd to `$video/$symlinks': $!"; - - # Find the previous directory of the file TODO - my $old_target = readlink $old_symlink; - my $new_target = dirname($old_target) .'/'. $new_filename; - - print LOG "Renaming file... "; - &ack (\$r, rename $old_target, $new_target); + elsif (&is_director(\$old_director, $real_old_path)) { - print LOG "Creating new symlink... "; - &ack (\$r, symlink $new_symlink, $new_target); - - print LOG "Deleting old symlink... "; - &ack (\$r, unlink $old_symlink); - - return $r; + $old_symlink = catfile ($symlinks, $old_filename); + die "Error: `" .$old_symlink. "' is expected to be a symlink.\n" + unless -l $old_symlink; + die "Error: `" .$old_symlink. "' is expected to target to `" + .catfile(updir(),'DIRECTORS',$old_director,$old_filename) + ."'.\n" + unless realpath($old_symlink) eq realpath($old_path); + + $old_target = $old_path; + + $new_director = $old_director unless defined $new_director; + $new_target = catfile ($directors, $new_director, $new_filename); + } + + else { + $old_target = $old_path; + die "Error: `" .$old_path. "' is expected to be a regular file.\n" + unless -f $old_path; + die "Error: Dunno where to put this file. Try the `--sort' flag.\n" + unless defined $new_director; + + $new_target = catfile ($directors, $new_director, $new_filename); + } } +elsif (&is_director(\$new_director, $real_new_path)) { -# TODO: if the second argument is a directory, don't update database but -# only symlinks (and mv the file in the dir) + # The destination is under $directors/* -sub arrange { - my ($old_filename, $director) = @_; - my $r; + $new_target = $new_path; + $new_symlink = catfile ($symlinks, $new_filename); - my $new_dirname = $video .'/'. $director; + if (&is_symlink($real_old_path)) { - unless (-d $new_dirname) { - print STDERR "Directory `$new_dirname' does not exist. "; - until (-d $new_dirname) { - print STDERR "Should I create it? (Y/n) "; - my $a = lc <STDIN>; - chomp $a; - if ($a eq 'y' or $a eq '') { - mkdir $new_dirname or die "Cannot mkdir `$new_dirname': $!"; - } - elsif ($a eq 'n') { - exit 0; - } - } - - # TODO - print "\n"; + # Source is presumably a symlink: ensure it is + die "Error: `" .$old_path. "' is expected to be a symlink.\n" + unless -l $real_old_path; + + $old_symlink = $old_path; + $old_target = realpath ($old_path); } - print LOG "Moving file... "; - &ack (\$r, CORE::rename $old_filename, $new_dirname); + elsif (&is_director(\$old_director, $real_old_path)) { - print LOG "Creating symlink... "; - &ack (\$r, symlink '../'. $director .'/'. basename($old_filename), - $video .'/'. $symlinks); + $old_symlink = catfile ($symlinks, $old_filename); + die "Error: `" .$old_symlink. "' is expected to be a symlink.\n" + unless -l $old_symlink; + die "Error: `" .$old_symlink. "' is expected to target to `" + .catfile(updir(),'DIRECTORS',$old_director,$old_filename) + ."'.\n" + unless realpath($old_symlink) eq realpath($old_path); - return $r; + $old_target = $old_path; + } + + else { + $old_target = $old_path; + die "Error: `" .$old_path. "' is expected to be a regular file.\n" + unless -f $old_path; + } } +else { + die "Error: destination has to be in `" + .$symlinks. "' or in `" .catfile($directors,'*'). "'.\n" + ."See `" .$0. " --man' for details\n"; +} + + + +if (dirname (realpath $old_target) eq dirname (realpath $new_target)) { + unless ($old_filename eq $new_filename) { + print LOG "Renaming target... "; + &ack (\$r, rename $old_target, $new_target); + } +} +else { + print LOG "Moving target... "; + &ack (\$r, move $old_target, $new_target); +} +if (defined $old_symlink) { + print LOG "Deleting old symlink... "; + &ack (\$r, unlink $old_symlink); +} + +print LOG "Creating new symlink... "; +&ack (\$r, symlink catfile(updir(),'DIRECTORS',$new_director,$new_filename), + $new_symlink); + +unless ($old_filename eq $new_filename) { + print LOG "Updating database... "; + if ($nRES == 0) { + print LOG "failed!: no entry found.\n"; + } + elsif ($nRES > 1) { + print LOG "failed!: multiple entries found.\n"; + } + else { + my $rv = $dbh->do ( "UPDATE $videodata SET filename = ? WHERE id = ?", + undef, $new_filename, $id + ); + &ack (\$r, $rv); + } +} + +# Disconnect +$dbh->disconnect(); + + + +################################################################################ @@ -390,3 +478,40 @@ sub ack { $$r = 1; } } + + +# Check wether a path starts with $movies +sub is_symlink { + my @path = splitdir ($_[0]); + my @real_symlinks = @real_symlinks; #local copy + + while ($#real_symlinks>=0 && $#path>=0 && $real_symlinks[0] eq $path[0]) { + shift @real_symlinks; + shift @path; + }; + + if ($#real_symlinks<0 && $#path==0) { + return 1; # That's a "symlink" + } else { + return 0; # That's not a "symlink" + } +} + +# Check wether a path starts with $director/dir, and put `dir' in the +# first argument if that's the case +sub is_director { + my @path = splitdir ($_[1]); + my @real_directors = @real_directors; #local copy + + while ($#real_directors>=0 && $#path>=0 && $real_directors[0] eq $path[0]) { + shift @real_directors; + shift @path; + }; + + if ($#real_directors<0 && $#path>=0) { + ${$_[0]} = $path[0] if defined $_[0]; + return 1; # That's a "director" + } else { + return 0; # That's not a "director" + } +} |