(Linux)画像管理ソフトdigiKamのSQLiteデータベースのインポート、エクスポート


戻る一つ前のメニューに戻る

ここに掲載しているPerlスクリプトは動作状況を確認するためのもので、十分なエラー処理(不用意な入力データの検証等)を行なっていません。このページのスクリプトはGNU GPLフリーソフトウエアとして利用(改変、再配布等)出来ます。

目次

コメント一覧の操作に関連するテーブル構造

.schema Albums
CREATE TABLE Albums
 (id INTEGER PRIMARY KEY,
  albumRoot INTEGER NOT NULL,
  relativePath TEXT NOT NULL,
  DATE DATE,
  caption TEXT,
  collection TEXT,
  icon INTEGER,
  UNIQUE(albumRoot, relativePath)
);
.schema Images
CREATE TABLE Images
 (id INTEGER PRIMARY KEY,
  album INTEGER,
  name TEXT NOT NULL,
  STATUS INTEGER NOT NULL,
  category INTEGER NOT NULL,
  modificationDate DATETIME,
  fileSize INTEGER,
  uniqueHash TEXT,
  UNIQUE (album, name)
);
.schema ImageComments
CREATE TABLE ImageComments
 (id INTEGER PRIMARY KEY,
  imageid INTEGER,
  TYPE INTEGER,
  LANGUAGE TEXT,
  author TEXT,
  DATE DATETIME,
  comment TEXT,
  UNIQUE(imageid, TYPE, LANGUAGE, author)
);

後掲のスクリプトで扱われるCSVファイル例

JP-olwp1.jpg,"安芸の宮島 (Torii Gate of Itsukushima Jinja Shrine)"
JP-olwp2.jpg,"桜 (Cherry Tree, Japan)"
JP-olwp3.jpg,"清流 (River in Akita Prefecture, Japan)"
JP-olwp4.jpg,"庭園 (Waterfall in Tokyo Garden, Japan)"
JP-olwp5.jpg,"樹氷 (Snow at Zao Park, Japan)"

コメント一覧をCSVファイルにエクスポート

#!/usr/bin/perl
 
# GNU GPL Free Software. (C) 2010 INOUE Hirokazu
# このファイルは UTF-8 で保存すること. save this file in UTF-8
 
use strict;
use warnings;
 
use DBI;
use Text::CSV_XS;
 
my $flagDebug = 0;	# 詳細表示する場合は 1 を代入する
 
my $database = './digikam4.db';		# digiKam のデータベースファイル
my $data_source = "dbi:SQLite:dbname=$database";
my $dbh = undef;
my $sth = undef;
my $strQuery = undef;	# DBIのSQL prepare用
my $nSelectAlbum = 0;	# 選択されたAlbumsテーブルのid
my @arrPaths = undef;	# AlbumsテーブルのクエリでrelativePathの結果配列を格納する
my $arrRefPathsWoComment = undef;	# export対象でコメント無しの(id,ファイル名,コメント)配列
my $arrRefPathsWComment = undef;	# export対象でコメント有りの(id,ファイル名,コメント)配列
my $nTargetFilesWoComment = undef;	# export対象でコメント無しの件数(ファイル数)
my $nTargetFilesWComment = undef;	# export対象でコメント有りの件数(ファイル数)
my $csv = undef;	# CSVファイル作成用
 
my $i = 0;
my $strTemp = undef;
 
print("digiKamの特定ディレクトリの画像とそのコメント一覧をCSVにエクスポートします\n\n");
 
eval{
 
	$dbh = DBI->connect($data_source) or die("DBI error : fail to open SQLite db (db file = $database)\n");
 
 
	### ディレクトリ一覧を表示して、対象フォルダをユーザに選択させる
	$strQuery = "select id,relativePath from Albums;";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute() or die("$sth->errstr \n");
 
	print("ディレクトリ一覧\nno.  ディレクトリ名\n----+-----------------\n");
	while(@arrPaths = $sth->fetchrow_array())
	{
		printf("%03d : %s\n", $arrPaths[0], $arrPaths[1]);
	}
 
	print("コメントを書き込むディレクトリの no. を指定してください : ");
 
	$strTemp = <STDIN>;
	chomp($strTemp);
	$nSelectAlbum = $strTemp;
	printf("select : %03d\n", $nSelectAlbum);
 
 
	### ユーザが選択したディレクトリが存在するか確認し、ディレクトリ名を画面表示する
	$strQuery = "select id,relativePath from Albums where id=?;";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute($nSelectAlbum) or die("$sth->errstr \n");
 
	if(@arrPaths = $sth->fetchrow_array())
	{
		if($sth->rows() != 1)
		{
			$dbh->disconnect();
			die("ディレクトリの選択が間違っています\n");
		}
		printf("対象ディレクトリ : %s (Albums.id=%d)\n", $arrPaths[1], $nSelectAlbum);
	}
	else
	{
		$dbh->disconnect();
		die("ディレクトリの選択が間違っています\n");
	}
 
 
	### コメントが無いファイル一覧を配列に格納する
	$strQuery = "select Images.id, Images.name from Albums,Images on Albums.id=Images.album where Albums.id=? and Images.id not in (select Images.id from Albums,Images,ImageComments on Albums.id=Images.album and ImageComments.Imageid=Images.id where Albums.id=? and ImageComments.type=1);";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute($nSelectAlbum,$nSelectAlbum) or die("$sth->errstr \n");
	$arrRefPathsWoComment = $sth->fetchall_arrayref();
	$nTargetFilesWoComment = $sth->rows();
 
 
	### コメントが有るファイル一覧を配列に格納する
	$strQuery = "select Images.id, Images.name, ImageComments.comment from Albums,Images,ImageComments on Albums.id=Images.album and ImageComments.Imageid=Images.id where Albums.id=? and ImageComments.type=1;";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute($nSelectAlbum) or die("$sth->errstr \n");
	$arrRefPathsWComment = $sth->fetchall_arrayref();
	$nTargetFilesWComment = $sth->rows();
 
	### 見つかったexport対象ファイルの数を画面表示
	printf("ファイル数 = %d (うちコメント有り %d 個, コメント無し %d 個)\n", $nTargetFilesWComment + $nTargetFilesWoComment, $nTargetFilesWComment, $nTargetFilesWoComment);
 
 
	### 見つかったexport対象ファイルの一覧を画面表示
	if($flagDebug != 0)
	{
		print("対象ファイル一覧\nid   ファイル名            コメント\n----+---------------------+----\n");
		for($i=0; $i<$nTargetFilesWComment; $i++)
		{
			printf("%03d  %-20s  %s\n", $arrRefPathsWComment->[$i]->[0], $arrRefPathsWComment->[$i]->[1], substr($arrRefPathsWComment->[$i]->[2],0,32));
		}
		for($i=0; $i<$nTargetFilesWoComment; $i++)
		{
			printf("%03d  %-20s <コメント無し>\n", $arrRefPathsWoComment->[$i]->[0], $arrRefPathsWoComment->[$i]->[1]);
		}
	}
 
	$dbh->disconnect();
 
	### エクスポートするかどうか確認し、ファイル名を入力させる
	print("以上の内容をエクスポートしますか? (Y) : ");
	$strTemp = <STDIN>;
	chomp($strTemp);
	if(uc($strTemp) ne 'Y')
	{
		print("ファイルを出力せず、終了しました\n");
		exit();
	}
 
	print("出力するファイル名を入力 : ");
	$strTemp = <STDIN>;
	chomp($strTemp);
	if($strTemp eq '')
	{
		print("ファイル名が入力されなかったため、終了しました\n");
		exit();
	}
 
	### 出力用ファイルを開く
	open(FH_OUT, ">$strTemp") or die("ファイル $strTemp に書き込めません");
 
	$csv = Text::CSV_XS->new({binary=>1});	# 日本語の場合は binary を ON にする
 
	### コメント有りのCSVデータを作成し、出力ファイルに書き込む
	for($i=0; $i<$nTargetFilesWComment; $i++)
	{
		if(defined($arrRefPathsWComment->[$i]->[2])){ $arrRefPathsWComment->[$i]->[2] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g; }	# 改行をエスケープ
		$csv->combine($arrRefPathsWComment->[$i]->[1], $arrRefPathsWComment->[$i]->[2]);
		print(FH_OUT $csv->string() . "\n") or die("ファイル $strTemp への書き込み異常");
	}
	### コメント無しのCSVデータを作成し、出力ファイルに書き込む
	for($i=0; $i<$nTargetFilesWoComment; $i++)
	{
		$csv->combine($arrRefPathsWoComment->[$i]->[1], "");
		print(FH_OUT $csv->string() . "\n") or die("ファイル $strTemp への書き込み異常");
	}
 
	close(FH_OUT) or die("ファイル $strTemp を close 出来ませんでした");
	print("ファイル $strTemp への書き込み正常終了\n");
 
};
if($@){
	# evalによるエラートラップ:エラー時の処理
	die("プログラム エラー : ".$@."¥n");
	exit();
}
 
print("スクリプト終了\n");
 
### スクリプト末端 EOF

コメント一覧をCSVファイルからインポート

#!/usr/bin/perl
 
# GNU GPL Free Software. (C) 2010 INOUE Hirokazu
# このファイルは UTF-8 で保存すること. save this file in UTF-8
 
use strict;
use warnings;
 
use DBI;
use Text::CSV_XS;
 
my $flagDebug = 1;	# 詳細表示する場合は 1 を代入する
 
my $database = './digikam4.db';		# digiKam のデータベースファイル
my $data_source = "dbi:SQLite:dbname=$database";
my $dbh = undef;
my $sth = undef;
my $strQuery = undef;
my $nSelectAlbum = 0;
my @arrPaths = undef;
my @arrFileAndComment = undef;		# CSVから読み込んだファイル名とコメントの配列
my $arrRefQueryResult = undef;		# SQLクエリ結果を一時的に保存
my $nTargetFiles = 0;		# CSV内のデータ行数
 
my $strTemp = undef;
my $i = 0;
 
print("digiKamの特定フォルダの画像に、CSVよりコメントをインポートします\n\n");
 
eval{
 
	### CSVファイルを読み込む
	print("インポートするデータが入ったCSVファイル名を入力 : ");
	$strTemp = <STDIN>;
	chomp($strTemp);
	if($strTemp eq '')
	{
		print("ファイル名が入力されなかったため、終了しました\n");
		exit();
	}
 
	my $csv = Text::CSV_XS->new({binary=>1});
 
	open(FH_IN, "<$strTemp") or die("ファイル $strTemp を読み込めません");
	$nTargetFiles = 0;
	while(<FH_IN>)
	{
		# CSV各行をパースして、ファイル名とコメントを配列$arrFileAndCommentに格納
		my $strLine = $_;
		if($strLine eq ''){ next; }
		$csv->parse($strLine) or next;
		my @arrFields = $csv->fields();
		$arrFileAndComment[$nTargetFiles][0] = $arrFields[0];
		$arrFields[1] =~ s/<br \/>/\x0A/g;	# 改行のエスケープ"<br />"を0x0aに戻す
		$arrFileAndComment[$nTargetFiles][1] = $arrFields[1];
		$nTargetFiles++;
	}
	close(FH_IN) or die("ファイル $strTemp を close 出来ませんでした");
 
	### CSVから読み込んだデータの画面表示
	printf("CSVから読み込み完了。データ行数 = %d\n", $nTargetFiles);
	if($flagDebug != 0)
	{
		print("CSV内のデータ一覧\nid   ファイル名           コメント\n----+--------------------+--------------------\n");
		for($i=0; $i<$nTargetFiles; $i++)
		{
			$strTemp = $arrFileAndComment[$i][1];
			$strTemp =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g;	# 画面表示での改行を抑止
			printf("%03d  %-20s %s\n", $i, $arrFileAndComment[$i][0], substr($strTemp,0,16));
		}
	}
 
	### 書き込み対象とするdigiKamディレクトリの選択
	print("データを書き込むdigiKamディレクトリを選択します\n");
	# 自動コミットOFFでデータベースを開く
	$dbh = DBI->connect($data_source, "", "", {PrintError => 1, AutoCommit => 0}) or die("DBI error : fail to open SQLite db (db file = $database)\n");
	$strQuery = "select id,relativePath from Albums;";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute() or die("$sth->errstr \n");
 
	print("ディレクトリ一覧\nno.  ディレクトリ名\n----+-----------------\n");
	while(my @arrPaths = $sth->fetchrow_array())
	{
		printf("%03d  %s\n", $arrPaths[0], $arrPaths[1]);
	}
 
	print("コメントを書き込むディレクトリの no. を指定してください : ");
 
	$strTemp = <STDIN>;
	chomp($strTemp);
	$nSelectAlbum = $strTemp;
	printf("select : %03d\n", $nSelectAlbum);
 
	### 選択されたdigiKamのディレクトリが正しいかチェックする
	$strQuery = "select id,relativePath from Albums where id=?;";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute($nSelectAlbum) or die("$sth->errstr \n");
 
	if(@arrPaths = $sth->fetchrow_array())
	{
		if($sth->rows() != 1)
		{
			$dbh->disconnect();
			die("ディレクトリの選択が間違っています\n");
		}
		printf("対象ディレクトリ : %s(id = %d)\n", $arrPaths[1], $nSelectAlbum);
	}
	else
	{
		$dbh->disconnect();
		die("ディレクトリの選択が間違っています\n");
	}
 
	### digiKamディレクトリ内のファイル数を画面表示する
	$strQuery = "select count(*) from Albums,Images on Albums.id=Images.album where Albums.id=?;";
	$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
	$sth->execute($nSelectAlbum) or die("$sth->errstr \n");
	$arrRefQueryResult = $sth->fetchall_arrayref();
 
	printf("CSVデータ行数 = %d\n", $nTargetFiles);
	printf("ディレクトリ内ファイル数=%d\n", $arrRefQueryResult->[0]->[0]);
 
 
	if($flagDebug != 0){ print("書き換え結果表示\n  id   ファイル名            旧コメント        新コメント\n-+----+---------------------+-----------------+----------------\n"); }
	else{ print("N:New, U:Update\n");}
	my $nWriitenLines = 0;
	for($i=0; $i<$nTargetFiles; $i++)
	{
		$strQuery = "select Images.id from Albums,Images on Albums.id=Images.album where Albums.id=? and Images.name=?;";
		$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
		$sth->execute($nSelectAlbum,$arrFileAndComment[$i][0]) or die("$sth->errstr \n");
		$arrRefQueryResult = $sth->fetchall_arrayref();
		if($sth->rows() != 1){ next; }
 
		my $nId = $arrRefQueryResult->[0]->[0];	# DB内のImages.id
 
		$strQuery = "select Images.id, ImageComments.comment from Albums,Images,ImageComments on Albums.id=Images.album and ImageComments.Imageid=Images.id where Albums.id=? and Images.name=?;";
		$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
		$sth->execute($nSelectAlbum,$arrFileAndComment[$i][0]) or die("$sth->errstr \n");
		my $arrRefComment = $sth->fetchall_arrayref();
		if($sth->rows() >= 1){
			# DB内にコメントが存在する
			if($arrRefComment->[0]->[1] ne $arrFileAndComment[$i][1])
			{
				$strQuery = "update ImageComments set comment=? where imageid=? and type=1;";
				$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
				$sth->execute($arrFileAndComment[$i][1],$nId) or die("$sth->errstr \n");
				$sth->finish() or die("データベース 書き込み時 finish 失敗");
				if($flagDebug != 0)
				{
					$arrFileAndComment[$i][1] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g;	# 画面表示での改行を抑止
					$arrRefComment->[0]->[1] =~  s/\x0D\x0A|\x0D|\x0A/<br \/>/g;
					printf("U %d  %-20s  %-16s  %-16s\n", $nId, $arrFileAndComment[$i][0], substr($arrRefComment->[0]->[1],0,16), substr($arrFileAndComment[$i][1],0,16));
				}
				else{ print("U"); }
				$nWriitenLines++;
			}
			elsif($flagDebug == 0){ print("."); }
		}
		else{
			# DB内にコメントが存在しない
			if($arrFileAndComment[$i][1] ne '')
			{
				$strQuery = "insert into ImageComments (imageid, type, language,comment) values (?,1,'x-default',?);";
				$sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n");
				$sth->execute($nId,$arrFileAndComment[$i][1]) or die("$sth->errstr \n");
				$sth->finish() or die("データベース 書き込み時 finish 失敗");
				if($flagDebug != 0)
				{
					$arrFileAndComment[$i][1] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g;	# 画面表示での改行を抑止
					printf("N %d  %-20s  %-16s  %-16s\n", $nId, $arrFileAndComment[$i][0],'', substr($arrFileAndComment[$i][1],0,16));
				}
				else{ print("N"); }
				$nWriitenLines++;
			}
			elsif($flagDebug == 0){ print("."); }
		}
 
	}
 
	if($flagDebug == 0){ print("\n"); }
 
	if($nWriitenLines <= 0)
	{
		print("コメントの書き換え対象がないため、終了しました\n");
		$dbh->rollback;
		$dbh->disconnect();
		exit();
	}
 
	### エクスポートするかどうか最終確認
	printf("以上の %d 個のコメントをdigikamデータベースに書き込みますか? (Y) : ", $nWriitenLines);
	$strTemp = <STDIN>;
	chomp($strTemp);
	if(uc($strTemp) ne 'Y')
	{
		print("\ndigiKamデータベースを更新せず、終了しました\n");
		$dbh->rollback;
		$dbh->disconnect();
		exit();
	}
 
	$dbh->commit() or die("データベース commit 失敗");
	printf("\n書き換えファイル数=%d\n", $nWriitenLines);
 
	$dbh->disconnect();
 
};
if($@){
	# evalによるエラートラップ:エラー時の処理
	if(defined($dbh))
	{
		$dbh->rollback;
		$dbh->disconnect();
	}
 
	die("プログラム エラー : ".$@."¥n");
}
 
print("スクリプト終了\n");
 
### スクリプト末端 EOF
戻る一つ前のメニューに戻る