iPod touch用写真リネームツール New!

iPod touch(iPhoneも)の写真がメチャクチャな順番で表示される件(http://support.apple.com/kb/HT4221?viewlocale=ja_JP)の回避ツールですが、フラットに1000枚近いのが並ぶのもどうかと思い。

こんな感じに年ごとにフォルダを分けるようにしました。

「何が便利なの?」

と思われると思うのですが(笑)
僕はPicasaで写真を管理していて、これをtouchに全部突っ込もうとすると、iTunesが「RAMが足りません」とか言い出しまして。今時組み込みでもあるまいしw
それはともかく、400GByte近くある写真を全部持ち歩くのも無理があるので、星をつけてるやつだけにしようかと。
そうするとPicasaからExportするわけですが、PicasaのExportでは一つのフォルダにフラットにファイルが並ぶ、と。タチの悪いことにiTunesは「すでに知ってるファイルか?」「ファイル名は?」ということでソートするので、ファイルの順番ぐちゃぐちゃ。目的の写真にたどり着くまでえらいかかる、というかたどり着けない。
で、このツールをかけると、

  • EXIFを読んで古い順にソート
  • ファイル名は古い順に連番
  • 年ごとにフォルダを分ける

と、なります。これでtouchでも快適に写真が探せる、と。
ワークフローは

  1. Picasaでスター付き画像をすべて選択してExport(ここでtouchにサイズを合わせるとよい)
  2. touchとの同期用ディレクトリをipod photo cacheを含めてクリア。
  3. Export先のディレクトリに対して、ツールをかける
  4. touchとの同期用のディレクトリに、ツリー構造で写真が出力される
  5. iTunesの写真の共有元に4.のディレクトリを指定
  6. (゚Д゚)ウマー

と、そんな感じです。
相変わらず例外処理が甘かったりするし、環境特化なので、とりあえずはソースを置いておきます。GUIつけるのもバイナリ配布するのも簡単なんですが、要望があったらやろうかなー、くらいです。

using System;
using System.Collections;
using System.Linq;

namespace CorrectTouchPics
{
    class CorrectTouchPics
    {
        const string SRC_DIR = @"C:\Users\Hiroyuki\Desktop\スター付き画像";
        const string DST_DIR = @"C:\Users\Hiroyuki\Desktop\touch_pics";
        const string FILE_NAME_BASE = "CTP";


        static void Main(string[] args)
        {
            Console.Title = "Correct Touch Pictures";
            Console.WriteLine("---------------------------Correct Touch Pictures Start.");

            //
            //元ファイル取得
            //
            Console.Write("Searching...");
            ArrayList srcFiles = new ArrayList();
            string[] picFiles = System.IO.Directory.GetFiles(SRC_DIR, "*.jpg");

            Console.WriteLine("\t{0}files found.", picFiles.GetLength(0));

            if (picFiles.GetLength(0) == 0) 
            {
                Console.WriteLine("No picture file found.");
                goto END;
            }
                

            //
            //日付でソート
            //
            Console.Write("Getting EXIF...");

            foreach (string pic in picFiles)
            {
                string strDate = "";                //日付文字列(仮にemptyとする)

                System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(pic);                     //画像ファイルとして読み込み

                //日付を取得
                DateTime dt;
                var dateProperty = from System.Drawing.Imaging.PropertyItem dp in bmp.PropertyItems
                                   where dp.Id ==0x9003
                                   select dp.Value;

                foreach (var dp in dateProperty)    //foreachにしているが、実行回数は0or1
                {
                    strDate = System.Text.Encoding.ASCII.GetString(dp);
                }
                if (strDate.Length != 0)            //日付の文字列が取得できた
                {
                    strDate = strDate.Trim(new char[] { '\0' });    //終端の\0をとる
                    dt = DateTime.ParseExact(strDate, "yyyy:MM:dd HH:mm:ss", null);
                }
                else        //EXIFから日付が取得できなかった
                {
                    dt = DateTime.MaxValue;
                }
                srcFiles.Add( new TouchPic(pic,dt));               //ファイル情報を配列に追加
            }
            Console.WriteLine("\tdone.");
            
            //
            //ソートする
            //
            Console.Write("Sorting...");
            srcFiles.Sort();
            Console.WriteLine("\tdone.");

            //
            //年ごとにわけてコピーを行う。
            //
            if (srcFiles.Count != 0)                            //対象の写真があれば
            {
                Console.WriteLine("Exporting...");
                int year = ((TouchPic)srcFiles[0]).photoYear;   //一番古い写真の年から始める。

                while (year <= 9999)        //EXIFなしまで回す。
                {
                    int fileNum = 0;        //ファイル連番

                    string dstDir = System.IO.Path.Combine(     //出力ディレクトリ\yyyy
                        DST_DIR,
                        year.ToString("D4")
                        );

                    var files = from TouchPic tp in srcFiles    //同じ年の写真を抜き出す
                                where tp.photoYear == year
                                select tp;


                    if (files.Count() != 0)                     //該当する写真があれば
                    {
                        Console.WriteLine("\t{0}", year.ToString());
                        if(!System.IO.Directory.Exists(dstDir)) //出力ディレクトリがなければ生成する。
                        {
                            System.IO.Directory.CreateDirectory(dstDir);
                        }

                        //ファイルコピー
                        foreach (var file in files)
                        {
                            //出力ディレクトリ+ファイル名ベース+XXXXX+.jpg
                            string dstFileName = FILE_NAME_BASE + fileNum.ToString("D5") + ".jpg";
                            dstFileName = System.IO.Path.Combine(dstDir, dstFileName);
                            System.IO.File.Copy(file.srcFile, dstFileName, true);
                            fileNum++;
                        }
                    }
                    year++;
                }
            }

            //終了処理
            END:    
            Console.WriteLine("--------------Correct Touch Pictures End. Press any key.");
            ConsoleKeyInfo ki = Console.ReadKey();                          //ユーザ入力待ち
        }
    }
    class TouchPic : IComparable
    {
        public readonly string srcFile;         //コピー元ファイル
        public readonly DateTime dateTime;      //EXIF撮影日
        public readonly int photoYear;          //撮影日の「年」部分

        public TouchPic(string src, DateTime dt)
        {
            srcFile = src;
            dateTime = dt;
            photoYear = dt.Year;

        }
        public int CompareTo(object obj)
        {
            int ret;
            DateTime dtThis, dtOther;

            dtOther = ((TouchPic)obj).dateTime;
            dtThis = this.dateTime;

            if (dtThis < dtOther)
            {
                ret = -1;
            }
            else
            {
                ret = 1;
            }
            return (ret);
        }

    }
}

今回は初めてLINQを使ったので色々トラブったけれど、それなりに見られるコードになっていると思います。ソートもLINQ使えばいいようなもんですが、このへんはもう作っちゃってたのでそのまま利用してます。