Advertisement
Guest User

Synology Media Backup Script

a guest
Nov 29th, 2015
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 14.89 KB | None | 0 0
  1. <?php
  2. # Synology Media Backup Script
  3. # V0.1 - www.verhees.be
  4. class CopyTo {
  5.     var $userdir = "son";                       # user who takes the pictures
  6.    var $delete_source_after = "4 months";      # deletes files in bulk directory after ... X months
  7.    var $src = "/volume1/backup son";           # source directory (bulk directory of media)
  8.    var $dst = "/volume1/pictures";             # root directory of destination directory
  9.    var $bck = "/volume1/crons/backup";         # dir for archiving before deletion
  10.    var $err = "/volume1/crons/backup/errors";  # dir for storing files with different MD5's
  11.    var $lgs = "/volume1/crons";                # dir for logs
  12.  
  13.     # more stuff you don't touch ...
  14.    var $arr_ext = array('jpg', 'jpeg', 'mp4', 'moov', 'mov', '3gp', 'avi');
  15.     var $arr_months = array(1 => 'Januari', 2 => 'Februari', 3 => 'Maart', 4 => 'April', 5 => 'Mei', 6 => 'Juni', 7 => 'Juli', 8 => 'Augustus', 9 => 'September', 10 => 'Oktober', 11 => 'November', 12 => 'December');
  16.     var $arr_msgs = array("cnt_dirs_new" => 0, "cnt_files_copy" => 0, "files_warning" => array());
  17.     var $log = "";
  18.  
  19.     public function CopyTo() {
  20.         $this->log = "\n\n[START] - CopyTo Log : ".date("d-m-Y h:i:s");
  21.         $this->check_folders();
  22.         $this->recurse_copy();
  23.         $this->log .= "\n[STOP] - CopyTo Log : ".date("d-m-Y h:i:s");
  24.         $this->dump_log();
  25.     }
  26.  
  27.     private function check_folders() {
  28.         if(!file_exists($this->src) && !is_dir($this->src)) {
  29.             $this->log .= "\n[ERROR] source directory doesn't exists : ".$this->src;
  30.         }
  31.  
  32.         if(!file_exists($this->dst) && !is_dir($this->dst)) {
  33.             $this->log .= "\n[ERROR] destination directory doesn't exists : ".$this->dst;
  34.         }
  35.     }
  36.  
  37.     public function recurse_copy() {
  38.         $src = opendir($this->src);
  39.         $ignore = array('.', '..');
  40.  
  41.         $cnt_files = 0;
  42.         while(false !== ($file = @readdir($src)) ) {
  43.             if (!in_array($file, $ignore, TRUE)) {
  44.                 $cnt_files++;
  45.  
  46.                 # first check the extension if it's safe/supported by the script
  47.                $ext = $this->check_file_extension($file);
  48.                 if ($ext != false) {
  49.                     $filedata = $this->get_file_meta($this->src . '/' . $file, $ext);
  50.  
  51.                     # check if datetime stamp of file is present for the right destination directory
  52.                    if (is_array($filedata)) {
  53.                         $file_dst_dir = $this->dst.'/'.$filedata['dateyear'].'-'.$filedata['datemonth'].' '.$this->arr_months[(int)$filedata['datemonth']];
  54.  
  55.                         # check if destination directory exists else create it (cfr. /2015-01 Januari/ )
  56.                        $this->check_or_make_dir($file_dst_dir);
  57.  
  58.                         # check if destination USER directory exists else create it (cfr. /2015-01 Januari/tim/ )
  59.                        $file_dst_dir = $file_dst_dir.'/'.$this->userdir;
  60.                         $this->check_or_make_dir($file_dst_dir);
  61.  
  62.                         # check if destination file already exists if so generate an unique filename
  63.                        $filename_double = "";
  64.                         $filename_double = $this->check_md5($file, $ext, $file_dst_dir);
  65.  
  66.                         $destination_filename = $file; # ($filename_double ? $filename_double : $file);
  67.  
  68.                         if ($filename_double != false) {
  69.                             $this->add_log($file_dst_dir. '/' . $destination_filename, 'copy');
  70.                             copy($this->src . '/' . $file, $file_dst_dir . '/' . $destination_filename);
  71.                         }else{
  72.                              $this->add_log(".. skipped ", 'i');
  73.                         }
  74.  
  75.                         # check consistency of source and destination (md5 check) if ok delete the source
  76.                        if ($this->analyse_copy($this->src.'/'.$file, $file_dst_dir.'/'.$destination_filename, $filedata, $this->get_file_meta($file_dst_dir. '/'.$destination_filename, $ext)) == false) {
  77.                             $this->add_log("consistency error ".$file_dst_dir. '/' . $destination_filename, 'e');
  78.                         }else{
  79.                             # check if the file is ready to be deleted or to be preserved.
  80.                            if ($this->check_ready_to_delete($filedata) == true) {
  81.                                 $user_backup_dir = $this->bck.'/'.$this->userdir;
  82.  
  83.                                 $this->check_or_make_dir($user_backup_dir);
  84.  
  85.                                 if (copy($this->src.'/'.$file, $user_backup_dir.'/'.$file)) {
  86.                                     $this->add_log("archived before deletion from source : ".$this->src . '/' . $file.' [>>] '.$user_backup_dir, 'copy');
  87.                                 }
  88.  
  89.                                 unlink($this->src.'/'.$file);
  90.                                 $this->add_log("deleted from source (was saved on ".date("Y-m-d h:i:s", $filedata['datetime_saved']).") : ".$this->src . '/' . $file, 'i');
  91.                             }
  92.                         }
  93.                     }else{
  94.                         $this->add_log("no filedata available for file".$file, 'i');
  95.                     }
  96.                 }
  97.             }
  98.         }
  99.  
  100.         closedir($src);
  101.     }
  102.  
  103.     private function check_or_make_dir($dir) {
  104.         if (!@opendir($dir)) {
  105.             try {
  106.                 mkdir($dir);
  107.                 $this->add_log("new directory created : ".$dir, 'i');
  108.             } catch(Exception $e) {
  109.                 $this->add_log("failed to create the destination directory ".$dir, 'e');
  110.                 $this->dump_log();
  111.             }
  112.         }
  113.     }
  114.  
  115.     # check if the time window (to preserve deletion in the source directory) has expired
  116.    private function check_ready_to_delete($original_filedata) {
  117.         if ($this->delete_source_after) {
  118.             if (($original_filedata['datetime_saved'] < (time() - (time() - strtotime('-'.$this->delete_source_after, time()))))) {
  119.                 return true;
  120.             }
  121.         }
  122.  
  123.         return false;
  124.     }
  125.  
  126.     private function check_md5($file, $ext, $file_dst_dir) {
  127.         if (file_exists($file_dst_dir . '/' . $file)) {
  128.             $this->add_log("file already exists in destination ".$file_dst_dir. '/' . $file, 'w');
  129.             # check if files are same md5 if not generate unique filename and copy.
  130.            if (md5_file($this->src.'/'.$file) != md5_file($file_dst_dir.'/'.$file)) {
  131.                 $this->add_log("file already exists but different MD5 ", 'e');
  132.  
  133.                 $this->check_or_make_dir($this->err);
  134.  
  135.                 if (copy($this->src.'/'.$file, $this->err.'/'.$file)) {
  136.                     $this->add_log("archived in errors before deletion from source : ".$this->src . '/' . $file.' [>>] '.$this->err.'/'.$file, 'copy');
  137.                 }else{
  138.                     $this->add_log("failed to archive in errors before deletion from source : ".$this->src . '/' . $file.' [>>] '.$this->err.'/'.$file, 'error');
  139.                 }
  140.                 #return $this->generate_unique_filename($file, $ext, $file_dst_dir);
  141.            }else{
  142.                 $this->add_log("file already exists but same MD5 ", 'i');
  143.                 return false;
  144.             }
  145.         }else{
  146.             return $file;
  147.         }
  148.     }
  149.  
  150.     private function analyse_copy($source_filename, $destination_filename, $source_filedata, $destination_filedata) {
  151.         if (md5_file($source_filename) === md5_file($destination_filename)) {
  152.             return true;
  153.         }
  154.  
  155.         return false;
  156.     }
  157.  
  158.     /*
  159.     private function analyse_copy($source_filename, $destination_filename, $source_filedata, $destination_filedata) {
  160.         $cnt_values = count($source_filedata);
  161.  
  162.         $i = 0;
  163.         # first check if the technical values are the same
  164.         foreach($source_filedata as $key => $value) {
  165.             if ($value === $destination_filedata[$key]) {
  166.                 $i++;
  167.             }
  168.         }
  169.  
  170.         # if so check the md5 checksum
  171.         if ($i === $cnt_values) {
  172.             if (md5_file($source_filename) === md5_file($destination_filename)) {
  173.                 return true;
  174.             }
  175.         }
  176.  
  177.         return false;
  178.     }
  179.  
  180.     private function generate_unique_filename($file, $ext, $dir) {
  181.         $increment = 1; //start with no suffix
  182.         $file_info = pathinfo($file);
  183.  
  184.         while(file_exists($dir."/".$file_info['filename']."_(copy)_".$increment.'.'. $ext)) {
  185.             $increment++;
  186.         }
  187.  
  188.         return $file_info['filename']."_(copy)_".$increment.'.'. $ext;
  189.     } */
  190.  
  191.     private function get_file_meta($file, $ext) {
  192.         $data = $arr_stamps = array();
  193.  
  194.         if ($file) {
  195.               switch(strtolower($ext)) {
  196.                 # pictures
  197.                case 'jpg': case 'jpeg': # case 'dng': case 'gif': case 'bmp': case 'tiff': case 'png':
  198.                    try {
  199.                         $exif_data = exif_read_data($file);
  200.  
  201.                         $arr_stamps['date_modified_unix'] = (@$this->get_date_modified($file) ? (int)$this->get_date_modified($file) : '');
  202.                         $arr_stamps['date_created_unix'] = (@$this->get_date_created($file) ? (int)$this->get_date_created($file) : '');
  203.                         $arr_stamps['date_original_meta'] = (is_object($exif_data['DateTimeOriginal']) && (int)$exif_data['DateTimeOriginal']->getTimestamp() > 0 ? (int)$exif_data['DateTimeOriginal']->getTimestamp() : '');
  204.                         $arr_stamps['date_meta'] = ((int)$exif_data["FileDateTime"] > 0 ? (int)$exif_data["FileDateTime"] : '');
  205.                         $arr_stamps['date_from_filename'] = (int)$this->get_date_from_filename($file);
  206.  
  207.                         $arr_stamps = array_filter($arr_stamps);
  208.                         $date_created = min($arr_stamps);
  209.  
  210.                         $data = array(
  211.                             'datetime_saved' => $date_created,
  212.                             'datetime' => $date_created,
  213.                             'dateyear' => date("Y", $date_created),
  214.                             'datemonth' => date("m", $date_created),
  215.                             'mimetype' => $exif_data['MimeType'],
  216.                             'height'   => $exif_data['COMPUTED']['Height'],
  217.                             'width'    => $exif_data['COMPUTED']['Width'],
  218.                             'filesize' => $exif_data['FileSize'],
  219.                         );
  220.                     } catch(Exception $e) {
  221.                         $this->add_log("failed to read PICTURE EXIF meta data from file ".$file." / ".$e->getMessage(), "e");
  222.                     }
  223.                 break;
  224.  
  225.                 case 'mp4': case 'moov': case 'mov': case '3gp':
  226.                      include_once("/volume1/crons/getid3/getid3.php");
  227.                      try {
  228.                         $id3 = new getID3();
  229.                         $arr_vid = $id3->analyze($file);
  230.  
  231.                         $arr_stamps['date_modified_unix'] = (@$this->get_date_modified($file) ? (int)$this->get_date_modified($file) : '');
  232.                         $arr_stamps['date_created_unix'] = (@$this->get_date_created($file) ? (int)$this->get_date_created($file) : '');
  233.                         $arr_stamps['date_original_meta'] = ($arr_vid['quicktime']['moov']['subatoms'][0]['creation_time_unix'] ? $arr_vid['quicktime']['moov']['subatoms'][0]['creation_time_unix'] : '');
  234.  
  235.                         $arr_stamps = array_filter($arr_stamps);
  236.                         $date_created = min($arr_stamps);
  237.  
  238.                         $data = array(
  239.                             'datetime_saved' => $date_created,
  240.                             'datetime' => $date_created,
  241.                             'dateyear' => date("Y", $date_created),
  242.                             'datemonth' => date("m", $date_created),
  243.                             'mimetype' => $arr_vid['mime_type'],
  244.                             'height'   => $arr_vid['video']['resolution_y'],
  245.                             'width'    => $arr_vid['video']['resolution_x'],
  246.                             'filesize' => $arr_vid['filesize'],
  247.                         );
  248.                     } catch (Exception $e) {
  249.                         $this->add_log("failed to read VIDEO meta data from file ".$file." / ".$e->getMessage(), "e");
  250.                     }
  251.                 break;
  252.                 # videos
  253.                /*case 'avi': case 'avchd': case 'aaf': case 'mpeg': case 'mpg': case 'mpe': case 'm4v': case 'mp4': case 'flv':
  254.  
  255.                 break;*/
  256.  
  257.                 /*
  258.                 # sound
  259.                 case 'mp3': case 'm4a': case 'wma': case 'mp4': case 'aac': case 'mkv':
  260.                 break;
  261.  
  262.                 # documents
  263.                 case 'pdf':
  264.                 break;
  265.  
  266.                 # adobe
  267.                 case 'psd':
  268.                 break;
  269.                 */
  270.             }
  271.         }
  272.  
  273.         return $data;
  274.     }
  275.  
  276.     private function validate_date($date) {
  277.         $d = DateTime::createFromFormat('Ymd', $date);
  278.         return $d && $d->format('Ymd') == $date;
  279.     }
  280.  
  281.     private function get_date_modified($file) {
  282.         return filemtime($file);
  283.     }
  284.  
  285.     private function get_date_created($file) {
  286.         return filectime($file);
  287.     }
  288.  
  289.     private function get_date_from_filename($file) {
  290.         $file = basename($file);
  291.         $re = '/(\d{8})|([0-9]{4}-[0-9]{2}-[0-9]{2})|([0-9]{2}-[0-9]{2}-[0-9]{4})/';
  292.  
  293.         preg_match($re, $file, $matches);
  294.  
  295.         if ($matches[0] > 0 && $this->validate_date($matches[0])) {
  296.             return strtotime($matches[0]);
  297.         }
  298.  
  299.         return '';
  300.     }
  301.  
  302.     private function check_file_extension($file) {
  303.         if (in_array($ext = pathinfo($this->src. '/' .strtolower($file), PATHINFO_EXTENSION), $this->arr_ext)) {
  304.             return $ext;
  305.         }else{
  306.             $this->add_log("extension not valid : ". $file, "w");
  307.             array_push($this->arr_msgs['files_warning'], $file);
  308.         }
  309.  
  310.         return false;
  311.     }
  312.  
  313.     private function add_log($msg, $type = "") {
  314.         switch($type) {
  315.             case 'copy' : $type = "       [COPY] "; break;
  316.             case 'w' : $type = "(!) [WARNING] "; break;
  317.             case 'e' : $type = "(!!!) [ERROR]"; break;
  318.             case 'i' : $type = "       [INFO] "; break;
  319.             default:
  320.                 $type = "";
  321.         }
  322.  
  323.         $this->log .= "\n".$type." : ".date("d-m-Y h:s")." - ".$msg;
  324.     }
  325.  
  326.     private function dump_log() {
  327.         #echo "writing log ...";
  328.  
  329.         if (count($this->arr_msgs['files_warning']) > 0)
  330.             $this->log .= "\n===============================================\n"."WARNINGS:\n".implode($this->arr_msgs['files_warning'], "\n")."\n===============================================";
  331.  
  332.         file_put_contents($this->lgs.'/log_'.date("Ynj").'_'.$this->userdir.'_copyto.txt', $this->log, FILE_APPEND);
  333.         exit;
  334.     }
  335. }
  336.  
  337. $copyto = new CopyTo();
  338.  
  339. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement