Tuesday, June 4, 2013

0 Use PowerShell to organize photo and video: orag-photo.ps1

I like to organize my home pictures and videos by the year and month. Sometimes, I will organize them base on the subject . I am tired to do it manually and decide to write a PowerShell script to do.

Now, here you go:  orag-photo.ps1

image

The script does very simple tasks:

  • Scan the files and distribute the file base on the last write date property of the file.
  • If no target directory is provided, it is default as same as source directory.
  • When source directory and target directory are the same, it is running  as MOVE mode.
  • When source directory and target directory are not the same, it is running as COPY mode.
  • If the script found the target directory has the same file name, it will use MD5 check sum to compare. unless the –force flag is provided, it wont overwrite the target file whether they are the same or not.
  • By default, the script is running  as dry run mode, it wont really perform the actual work but only show you what it will do. use –doit flag to let the script perform the actual work.
  • The script will skip the directory where it has YYYYXXX, YYYY is the digital number, XXX can be any string. For example, 2013NY_FAMILY_TRIP

Here is the screen shot when it runs in dry run mode.

image

Here is the screen shot when it runs in doit flag.

image

Here is the end result of the sorting .

image

-reverse flag can move all the files back to its root but it wont delete the timestamp folder.

More example can be found with –example flag

image

Here is the source code of the script. Enjoy Smile 

<#
.SYNOPSIS
  Helps you organise your digital photos/videos into subdirectory, based on the last write date. 
.DESCRIPTION
  Helps you organise your digital photos/videos into subdirectory, based on the last write date. 
  Last Write date usually is the date taken when the file is created.
  Bsae on the date, the file will be organized into C:\targe_directory\YYYY\YYYYMM
  The script will auto skip special folder name like YYYYXX, example, 2013NYTRIP. 
  As I may organize my photo in this naming and I dont want it to be autosort. 
.NOTES
  Author: Po@sqlpanda
  File: Org-Photos.ps1
  Requires: PowerShell V2
.LINK
    WWW.SQLPANDA.COM
.EXAMPLE
  orag-photo -src C:\Users\PO\Pictures\ 
  Dry run the file processing. Give you the overview what the script will do.
.EXAMPLE
  orag-photo -src C:\Users\PO\Pictures\ -verbose
  Dry run the file processing with verbose output. Give you the overview what the script will do.
.EXAMPLE
  orag-photo -src C:\Users\PO\Pictures\ -target C:\temp -doit
  Actuall move the files and organize the in the folder. Skip if the file has the same name or MD5 check show difference.
.EXAMPLE
  orag-photo -src C:\Users\PO\Pictures\ -target C:\temp -doit
  Actuall copy the files and organize the in the folder. Skip if the file has the same name or MD5 check show difference.
.EXAMPLE
  orag-photo -src C:\Users\PO\Pictures\ -target C:\temp -doit -force
   Actuall copy the files and organize the in the folder. Overwrite all files even with the same name or MD5 check show difference.
.PARAMETER src
    Soruce directory
.PARAMETER target
    target directory. Deftaul to be the same as source
.PARAMETER doit
    Without doit flag, it will only create the report and tell you what it will do
.PARAMETER force
    Overwrite the file with the same name and MD5 checksum show the difference
#> 
 
[CmdletBinding()]
Param(
   [Parameter(Mandatory=$true)] 
    [string]$src,
    [switch]$doit,
    [string]$target,
    [switch]$force,
    [switch]$reverse
   )
  
 function getMD5{
     Param (
        $c_file
    )
    $md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
    $hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($c_file)))
    $hash
 }
 
     if ( $target ){
        
    }
    else {
        $target=$src
    }
    #todo Better error checking for compare $src and $target
    Write-Host "Target: $target"
    Write-Host "Source: $src"
    if ( (Test-path "$src") -ne $True ) {
        Write-Host "Source Dir does not exist."
        break
    }
    
    If ( $target -eq $src ) {
        Write-Host "++MOVE_MODE: source directory is same as target directory"
        $cmd_mode = 'Move-Item'
        $cmd_string = 'Move'
    }
    else {
        Write-Host "++COPY_MODE: source directory is different than target directory"
        $cmd_mode = 'Copy-Item'
        $cmd_string = 'Copy'
    }
 
$Files = Get-ChildItem -recurse "$src"
 
$num_entry_in_src = ($Files ).count
Write-Verbose "Num of Entry in the Source: $num_entry_in_src"
If ( $num_entry_in_src -eq 0){
    Write-Host "No File found in Source directory."
}
 
$exclude_foders = @()
$want_files =@()
foreach ($file in $Files) 
{
    #TODO add progress bar
    #TODO move flag to write-verbose
    $full_name = $file.fullname
    if ( $file.PSIsContainer -eq $true ){
        write-verbose "$full_name is directory"
    }
    else {
        # check special name directory
        $file_dir_name = $file.Directory.Name
        $file_full_dir_name = $file.DirectoryName
        if ( $file_dir_name -match '^\d\d\d\d[a-zA-Z_]'){
            Write-verbose "DIR: $file_full_dir_name"
            Write-verbose "File directory match exclude naming convention"
            $exclude_foders += ,$file_full_dir_name
            continue
        }
        
        $last_writeTime = $file.LastWriteTime
   
        If ($last_writeTime.day -lt 10){
            $strDay = '0' +  [string] $last_writeTime.day
        }
        else {
            $strDay = [string] $last_writeTime.day
        }
  
    $strYear = [string] $last_writeTime.year
        If ( $last_writeTime.month -lt 10){
            $strMonth = '0' + [string] $last_writeTime.month
        }
        else {
            $strMonth = [string] $last_writeTime.month
        }
    $DateTaken = $strYear + $strMonth + $strDay
    $MonthTaken = $strYear + $strMonth
    $file | Add-Member –MemberType NoteProperty –Name myDataTaken –Value $DateTaken
    $file | Add-Member –MemberType NoteProperty –Name myMonth –Value $MonthTaken
    $file | Add-Member –MemberType NoteProperty –Name myYear –Value $strYear
    
    $myDataTaken = $file.myDataTaken
    Write-verbose "$full_name -> DayTaken: $myDataTaken,MonthTaken:$MonthTaken,YearTaken:$strYear   "
    $want_files += ,$file
    } 
}
if ( $doit -eq $true ){        
    $flag= '++DOIT:'
}
else {
    $flag = '++DRYRUN'
}
 
 
    # Create the folder 
$folder_create_arrary = @()
 
$file_new_arrary = @()    
$file_md5_diff_overwrite_arrary = @()
$file_md5_diff_non_overwrite_arrary = @()
$file_md5_same_overwrite_array= @()
$file_md5_same_non_overwrite_arrary = @()
 
    foreach ($file in $want_files) {
        $month_dir = $file.myMonth
        $year_dir = $file.myYear
        If ( $reverse -eq $true) {
            $full_new_dir=$target
        }
        else {
            $full_new_dir = "$target\$year_dir\$month_dir"
        }
        $full_path = $file.fullname
        $TimeStamp = $file.myDataTaken
        $short_name = $file.name
        $new_file_path = "$full_new_dir\$short_name"
        if ( (Test-path $full_new_dir) -eq $true ){
            Write-host "$full_new_dir already exist."
        }
        else {
            # create dir
            $folder_create_arrary += ,$full_new_dir
            if ( $doit -eq $true ){
                write-host "++DOIT: $full_new_dir does not exist . creating ..."
                mkdir "$full_new_dir" | out-null
                
            }
            else {
                Write-host "$flag $full_new_dir does not exist . creating ..."
            }
        }
        
        If ( (test-path $new_file_path) -eq $true ){
        # Same file name exist
            $target_md5 = getMD5("$new_file_path")
            $src_md5 = getMD5("$full_path")
        
            if ( "$src_md5" -eq "$target_md5"){
                # MD5 same
                If ( $force -eq $true){
                    # OverWrite FIle 
                    $file_md5_same_overwrite_array += ,$file
                    Write-host "$flag $short_name($TimeStamp) is the same between source and target. Force flag in place."
                    if ( $doit -eq $true ){        
                        & $cmd_mode "$full_path" "$full_new_dir" -force
                    }
                }
                else {
                    Write-host "I am here"
                    $file_md5_same_non_overwrite_array += ,$file
                    Write-host "$flag $short_name is the same between source and target. No action taken."
                }
            }
            else {
                # MD5 Diff
                Write-Verbose "SRC: $src_md5"
                Write-Verbose "TAR: $target_md5"
                If ( $force -eq $true){
                    $file_md5_diff_overwrite_arrary += ,$file
                    Write-host "$flag $short_name($TimeStamp) is different between soruce and target."
                    if ( $doit -eq $true ){        
                        & $cmd_mode "$full_path" "$full_new_dir"
                    }
                }
                else {
                    $file_md5_diff_non_overwrite_arrary += ,$file
                    Write-host "$flag $short_name($TimeStamp) is different between soruce and target."
                }
                            
            }
        }
        else {
        # new files
            $file_new_arrary += ,$file
            if ( $doit -eq $true ){        
                Write-host "$flag $cmd_string $full_path($TimeStamp) to $full_new_dir"
                & $cmd_mode "$full_path" "$full_new_dir"
            }
            else {
                Write-host "$flag $cmd_string $full_path($TimeStamp) to $full_new_dir"
            }
        }
    }
    
# Summary Report
Write-Host ''
Write-Host '################################################'
Write-Host "###########     Summery Report        ##########"
Write-Host '################################################'
if ($reverse -eq $true){
    Write-Host "*** Reverse flag is enable. Pull files back to the $target as flat."
}
Write-Host "* Target: $target"
Write-Host "* Source: $src"
Write-Host "* Command Mode: $cmd_mode"
Write-Host "* Flag: $flag"
Write-Host "* Numer of the Folders created            :"  ($folder_create_arrary |  Sort-Object -Unique).count
Write-Host "* Numer of the New Files                  :"  ($file_new_arrary ).count
Write-Host "* Numer of the same files found(overwrite):"  ($file_md5_same_overwrite_array ).count
Write-Host "* Numer of the same files found(  Keep   ):"  ($file_md5_same_non_overwrite_array ).count
 
#$file_md5_same_non_overwrite_array 
 
Write-Host "* Numer of the diff files found(overwrite):"  ($file_md5_diff_overwrite_array ).count
Write-Host "* Numer of the diff files found(  Keep   ):"  ($file_md5_diff_non_overwrite_array ).count
Write-Host ''
Write-Host "* Numer of the Skip Dir for name exclusion:"  ($exclude_foders |  Sort-Object -Unique).count
$exclude_foders |  Sort-Object -Unique

0 comments:

Post a Comment

 

SQL Panda Copyright © 2011 - |- Template created by O Pregador - |- Powered by Blogger Templates