-
-
Save btTeddy/0633b8da019b69a55e24561750d74243 to your computer and use it in GitHub Desktop.
Move directories in which all the MKV and MP4 video files have only one hard link, designed for Unraid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/php | |
| #backgroundOnly=true | |
| #arrayStarted=true | |
| #clearLog=true | |
| #noParity=true | |
| # name=tdrnlds custom mover script. Move from cache to disk | |
| # The script will search in cache drive for *.mkv, *.mp4, *.avi, for files with only one hardlink (aka original file) in $source (default: movies). | |
| # If found mixture of files in the same folder e.g *.mkv with hardlink=1 and *.mkv with hardlink=2 folder will be skiped. | |
| # Once files are found it will mark folder containing those files any additional files beside to be moved from $source to $destination until $threshold is acheived. | |
| # $threshold = max disk utilization | |
| # e.g. *.srt, *.nfo and any other files will be moved as well | |
| # Script will start moving oldest files first (modification date). | |
| # Each time after moving a folder it will check if $threshold is achieved. | |
| # It will continue moving folders (containing ONLY *.mkv, *.mp4, *.avi files with single hardlink) untill disk utilisation drops to set &threshold (50% default) or there is no more files to be moved. | |
| # To change file types, just add new extension in this line by adding: <substr( $file, -4 ) === '.extension' ||> | |
| # e.g. to add mp4v file type, change line: | |
| # return substr( $file, -4 ) === '.mkv' || substr( $file, -4 ) === '.mp4' ||substr( $file, -4 ) === '.avi'; | |
| # to: | |
| # return substr( $file, -4 ) === '.mkv' || substr( $file, -4 ) === '.mp4' ||substr( $file, -4 ) === '.avi'||substr( $file, -4 ) === '.mp4v'; | |
| <?php | |
| # Source = media library location on CACHE drive. Usually the same as plex specific library path e.g /mnt/data/media/movies /mnt/data/media/tv . | |
| # !!! Replace SHARE name <data> with a full CACHE drive name <cache1>. The name can be found in /mnt/ | |
| $source = '/mnt/cache1/media/movies'; | |
| # Destination = media library location in the array hard drive. Usually the same as plex specific library path e.g /mnt/data/media/movies | |
| # !!! Replace SHARE name <data> with a full DISK name <disk1>. The name can be found in /mnt/ | |
| $destination = '/mnt/disk1/media/movies'; | |
| # $threshold - disk utilization, once achieved, script will stop | |
| $threshold = 50; | |
| $log = true; | |
| if( ! @file_exists( $source ) ) { | |
| echo "Bad source directory\n"; | |
| echo "Usage: ./move.php SOURCE DESTINATION\n"; | |
| exit( 1 ); | |
| }; | |
| if( ! @file_exists( $destination ) ) { | |
| echo "Bad destination directory\n"; | |
| echo "Usage: ./move.php SOURCE DESTINATION\n"; | |
| exit( 1 ); | |
| }; | |
| if( parse_ini_file( '/var/local/emhttp/var.ini' )[ 'mdResyncPos' ] ) { | |
| echo "Parity check or rebuild running. Exiting, for safety.\n"; | |
| exit( 2 ); | |
| }; | |
| function getFolderTree( $root, $dir = '' ) { | |
| $tree = []; | |
| foreach( array_diff( scandir( "$root$dir" ), [ '.', '..' ] ) as $name ) { | |
| if( is_dir( "$root$dir/$name" ) ) | |
| $tree = array_merge( $tree, getFolderTree( $root, "$dir/$name" ) ); | |
| elseif( is_file( "$root$dir/$name" ) ) | |
| $tree[] = "$dir/$name"; | |
| else { | |
| echo "This is neither a file, nor a directory: $root$dir/$name\n"; | |
| exit( 1 ); | |
| }; | |
| }; | |
| return $tree; | |
| }; | |
| function getSourceTree( $root, $dir = '' ) { | |
| return array_map( function( $file ) use ( $root ) { | |
| $stat = stat( "$root/$file" ); | |
| return [ 'node' => explode( '/', $file )[ 1 ], 'nlink' => $stat[ 'nlink' ], 'mtime' => $stat[ 'mtime' ] ]; | |
| }, array_filter( getFolderTree( $root ), function( $file ) { | |
| return substr( $file, -4 ) === '.mkv' || substr( $file, -4 ) === '.mp4' ||substr( $file, -4 ) === '.avi'; | |
| } ) ); | |
| }; | |
| function sortSourceTree( $tree ) { | |
| usort( $tree, function( $a, $b ) { | |
| if( $a[ 'nlink' ] > $b[ 'nlink' ] ) | |
| return -1; | |
| if( $a[ 'nlink' ] < $b[ 'nlink' ] ) | |
| return 1; | |
| if( $a[ 'mtime' ] > $b[ 'mtime' ] ) | |
| return 1; | |
| if( $a[ 'mtime' ] < $b[ 'mtime' ] ) | |
| return -1; | |
| return 0; | |
| } ); | |
| return $tree; | |
| }; | |
| function filterSourceTree( $tree ) { | |
| for( $i = count( $tree ) - 1; $i >= 0; $i-- ) { | |
| for( $j = $i - 1; $j >= 0; $j-- ) { | |
| if( $tree[ $i ][ 'node' ] === $tree[ $j ][ 'node' ] ) { | |
| unset( $tree[ $i ] ); | |
| continue 2; | |
| }; | |
| }; | |
| if( $tree[ $i ][ 'nlink' ] > 1 ) { | |
| unset( $tree[ $i ] ); | |
| }; | |
| }; | |
| return $tree; | |
| }; | |
| function usage( $source ) { | |
| return ( disk_total_space( $source ) - disk_free_space( $source ) ) / disk_total_space( $source ) * 100; | |
| }; | |
| #======================================================================================================================= | |
| # To get full log comment line # $tree = filterSourceTree( sortSourceTree( getSourceTree( $source ) ) ); | |
| $tree = filterSourceTree( sortSourceTree( getSourceTree( $source ) ) ); | |
| # Then uncomment these lines beelow: | |
| #======= | |
| # $tree = getSourceTree( $source ); | |
| # var_dump( $tree ); | |
| # $tree = sortSourceTree( $tree ); | |
| # var_dump( $tree ); | |
| # $tree = filterSourceTree( $tree ); | |
| # var_dump( $tree ); | |
| #======================================================================================================================= | |
| while( ( $usage = usage( $source ) ) > $threshold && count( $tree ) > 0 ) { | |
| $next = array_shift( $tree ); | |
| if( $log ) echo "Copying: $source/{$next[ 'node' ]} -> $destination/{$next[ 'node' ]}\n"; | |
| exec( 'cp -a ' . escapeshellarg( "$source/{$next[ 'node' ]}" ) . ' ' . escapeshellarg( "$destination/{$next[ 'node' ]}" ), $out, $status ); | |
| if( $status === 0 ) { | |
| if( $log ) echo "Copy successful. Removing: $source/{$next[ 'node' ]}\n"; | |
| exec( 'rm -r ' . escapeshellarg( "$source/{$next[ 'node' ]}" ), $out, $status ); | |
| if( $status === 0 ) { | |
| if( $log ) echo "Remove successful. Moving on.\n"; | |
| } | |
| else { | |
| if( $log ) echo "Remove failed! Files exist in two places now. Oops. Moving on.\n"; | |
| }; | |
| } | |
| else { | |
| if( $log ) echo "Copying failed! Skipping remove, and moving on.\n"; | |
| }; | |
| }; | |
| if( $usage > $threshold ) { | |
| if( $log ) echo "Your disk is ", ceil( $usage ), "% full, but there is nothing left to move.\n"; | |
| exit( 2 ); | |
| }; | |
| if( $log ) echo "Your disk is down to ", ceil( $usage ), "% full.\n"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment