Skip to content

Instantly share code, notes, and snippets.

@earlgreyxxx
Created November 1, 2020 09:11
Show Gist options
  • Save earlgreyxxx/8f673608f33a33646b36f4f5f1fa0d8c to your computer and use it in GitHub Desktop.
Save earlgreyxxx/8f673608f33a33646b36f4f5f1fa0d8c to your computer and use it in GitHub Desktop.
/******************************************************************
使用方法:
CopyWindowsSpotLight.exe コピー元 コピー先
コピー元は 上記の %LOCALAPPDATA%..... を指定する。
コピー元のディレクトリとコピー先のディレクトリを指定して次の条件でコピーする。
1)ファイルサイズ400KB以上のファイルをJPEGファイルとしてみなす。
(本来はファイルの先頭32ビットぐらいを読んでJPEGファイルかの判別をする必要がありますが・・・)
2)ランドスケープ(幅が高さより大きい)
3)既にコピーしたしたファイルはスキップする。
4)コピーするとき 拡張子 .jpg を付けてコピーする
どのタイミングでスポットライトの画像がダウンロードされるのか分からないので、
実際にはタスクスケジューラに登録して定期的に実行させます。
コピー元はハードコードしてもいいかも・・・。
******************************************************************/
using System;
using System.Text;
using System.IO;
using System.Linq;
namespace org.ptsv.console
{
class CopySpotlight
{
// エントリポイント
static int Main(string [] args)
{
if(args.Length < 2)
{
Console.Error.WriteLine("Usage: command <source directory> <destination directory>");
return -1;
}
char[] trimChars = { '\\' , ' ' };
var src = args[0].TrimEnd(trimChars);
var dest = args[1].TrimEnd(trimChars);
if(!Directory.Exists(src) || !Directory.Exists(dest))
Console.Error.WriteLine("ディレクトリが存在しません。");
// コピー元のディレクトリのファイル一覧を取得してフィルタリングしていきます。
// 最終的に プロパティ s,d を持つ匿名クラスに変換して foreach で回していきます。
// 本来はtry-catchで囲むべきですが・・・
var enumerator = Directory
.EnumerateFiles(src)
.Where(f => (new FileInfo(f)).Length >= 400 * 1024)
.Where(IsLandscape)
.Select(f => new { s = f,d = dest + "\\" + Path.GetFileName(f) + ".jpg"});
foreach(var set in enumerator)
{
try {
File.Copy(set.s,set.d);
Console.Error.WriteLine("done copied 1 file.");
} catch (Exception e) {
Console.Error.WriteLine("skip copy since file already exists.");
}
}
return 0;
}
// 値をスワップする(マイクロソフトのドキュメントからコピペ )
// https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/generics/generic-methods
protected static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
// JPEGファイルの縦横サイズから 幅が高さより大きいものを選択できるようにする
// BitConverterってリトルエンディアン(x86系なら殆どコレだと思うけど)しか対応してないみたいですねぇ。。。
// SOFマーカーから取得できるサイズってビックエンディアンで記録されている?のでひと手間必要。ハマった。
// Swapせずそのまま得られた16bit整数を System.Net.IPAddress.HostToNetworkOrder関数で変換するって方法もある。
// どっちにしろメンドクセー。
// 追記: 単純に 2バイトから16bitを合成する関数かけばよかった。
// protected static int ConvertToUInt16(byte a,byte b)
// {
// return (a << 8) | b;
// }
protected static bool IsLandscape(string f)
{
ushort width = 0,height = 0;
using (FileStream fs = File.OpenRead(f))
{
int bufSize = 4096;
byte[] content = new byte[bufSize];
int readLen = 0;
bool isReadBreak = false;
// ファイル全てを読み込むのは効率悪いので細切れで読んでいきます。
// 大抵は先頭に記述してあると思うので一回で済むと思います。
// 追記:2020/05/04
// これだとバッファ境界付近に SOFマーカーが現れたとき、index + 5,とかindex + 8とかのアクセスでエラーで落ちる。
// 下記コードは不完全だと気付きました(遅)<img draggable="false" role="img" class="emoji" alt="💦" src="https://s.w.org/images/core/emoji/13.0.0/svg/1f4a6.svg"> やはり一旦すべてを読むか、
// SOFマーカーが見つかったらその位置から再度fs.Readしなおすか、とかにする必要がある。時間あったらまた修正しよ。
while(0 < (readLen = fs.Read(content,0,content.Length)))
{
for(int index = 0; index < content.Length; index++)
{
if (0xFF == content[index] && 0xC0 == content[index + 1])
{
// プラットフォームがリトルエンディアンの場合は、
// 5バイト目と6バイト目、7バイト目と8バイト目をスワップする
if(BitConverter.IsLittleEndian)
{
Swap<byte>(ref content[index + 5],ref content[index + 6]);
Swap<byte>(ref content[index + 7],ref content[index + 8]);
}
height = BitConverter.ToUInt16(content,index + 5);
width = BitConverter.ToUInt16(content,index + 7);
isReadBreak = true;
break;
}
}
if(isReadBreak)
break;
Array.Clear(content,0,readLen);
}
content = null;
}
return width > 0 && height > 0 && width >= height;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment