Am Montag, den 08.03.2010, 09:28 +0000 schrieb Martyn Russell:
> On 07/03/10 09:51, Alexey Fisher wrote:
> > Am Freitag, den 05.03.2010, 11:50 +0000 schrieb Martyn Russell:
> >> Note that Tracker already sees mounted media and already indexes it
> >> (based on your config settings). So if you insert a memory card, those
> >> pictures are automatically being indexed for you (assuming
> >> tracker-miner-fs is running which it should be).
> >
> > Tracker need metadata to be useful. Most cameras do not provide any
> > metadata. In most cases we cant extract it, we need create it.
> >
> > Only way to get more info from photos/videos/audio is interpret file-
> > and foldernames, and getting filesystem creation date.
> > Do tracker will do this? or we need to create a separate tool.
> 
> No Tracker doesn't do anything like this right now.

I see.

>  Although, it might 
> make sense to add the necessary work there.

I would love to help.
I written some independent code for it, if you like i can send you.

I learning C by my self, and this project is my target to learn with. So
if you have time for mentorship, you are welcome.

To use attached programm, compile it:
gcc -ggdb -o walker whalker.c -I.

Usage:
./walker /media/path_to_storage_with_pics

Output looks like this:
buf: ../samples/canon/DCIM/105CANON/IMG_0540.JPG, 0, 540
buf: ../samples/canon/DCIM/105CANON/SND_0540.WAV, 0, 540
tadaaaaa this is audio comment
...
buf: ../samples/canon/DCIM/105CANON/IMG_0547.JPG, 0, 547
buf: ../samples/canon/DCIM/105CANON/STA_0548.JPG, 1, 548
buf: ../samples/canon/DCIM/105CANON/STB_0549.JPG, 1, 549

first part is path to the image. Second is tag number(0 - no tags, 1 -
panorama, 2 - interval image). Third is image number... i use it to find
audio comments.

regards,
Alexey
/*
 *  walker.c
 *
 *  This is parser for DCF name system found on all modern digital cameras,
 *  and provide interpretation, you can use to tag photos.
 *
 *  Author: Alexey Fisher <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; see the file COPYING. If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
/*
 * [wiki] "Design rule for Camera File system (DCF) is a JEITA specification 
 * (number CP-3461) which defines a file format and file system for digital
 * cameras, including the directory structure, file naming method, character
 * set, file format, and metadata format. It is currently the de facto
 * industry standard for digital still cameras. The file format of DCF is
 * based on the Exif 2.2 specification."
 *
 * Technical detiles:
 * It use static file name lenth, so we do not need to care about how to parse
 * dynamic names.
 *
 * independet of camera you use names have same lenth:
 * DCIM/100NIKON/DSCN1234.JPG
 * ^^^^ ^^^^^^^^ ^^^^^^^^^^^^
 *   4     8          12     -    chars
 */
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "walker.h"

/**
 * struct filebuffer - storage for filenames we can process. We put unsorted,
 * sort it, check for dependencies and push out. Normally in DCIM dir are 200
 * images, plus allowed 200 audio comments. So we hope it should be not
 * more than 400 files.
 * @file - path to the file (DCIM/101NIKON/DSCN0001.JPG = 26 chars + \0)
 * @number - image number (DSCN2561.JPG = 2561)
 * @tag - file tags, (panorama, interval photo, audio comment)
 * @file_type - type of produced file (jpg image, raw image, audio, video) 
 */
struct filebuffer {
	char file[27];
	int number;
	int tag;
	int print;
	int file_type;
} filebuffer[401];

/**
 * compare_int -  function needed for sorting filebuffer array.
 * @c1,c2 - pointers to filebuffer struct.
 */
int compare_int(const void *c1, const void *c2)
{
	struct filebuffer *info1 = (struct filebuffer *) c1;
	struct filebuffer *info2 = (struct filebuffer *) c2;

	if (info1->number > info2->number)
		return 1;
	else if (info1->number < info2->number)
		return -1;
	else {
		/* file with same number is (mostly?) audio comment,
		 * just make sure audio file ordered after image file */
		if (((info2->file_type == JPG_IMG) ||
			    (info2->file_type == RAW_IMG)) &&
			   ((info1->file_type == AUDIO) ||
			    (info1->file_type == VIDEO)))
			return 1;
		else if (((info1->file_type == JPG_IMG) ||
			    (info1->file_type == RAW_IMG)) &&
			   ((info2->file_type == AUDIO) ||
			    (info2->file_type == VIDEO)))
			return -1;
		else
		return 0;
	}
}

/**
 * struct extinfo - structure for extension comparision. We convert here
 * extension string to file_type integer. Every thing out of this list will
 * not be processed!
 * @ext - file extension we can handle, just include all image extensions.
 * @file_type - we reduce different extensions to generic file type
 * for example image, audio, video.
 */
struct extinfo {
	const char *ext;
	const int file_type;
} extinfo[] = {
	{"JPG",		JPG_IMG},
	{"JPEG",	JPG_IMG}, /* do not belong to DCIM */

	{"NEF",		RAW_IMG}, /* nikon */
	{"CRW",		RAW_IMG}, /* canon */
	{"RAF",		RAW_IMG}, /* fuji */

	{"WAV",		AUDIO},

	{"AVI",		VIDEO},
	{"MOV",		VIDEO}, /* multi type - video, or audio comment */

	{"MRK",		PRINT_JOB}
};

#define sizeof_extinfo (sizeof(extinfo))/sizeof(struct extinfo)
/**
 * compare_ext - compare function needed to sort and search in extinfo array.
 */
int compare_ext(const void *c1, const void *c2)
{
	struct extinfo *info1 = (struct extinfo *) c1;
	struct extinfo *info2 = (struct extinfo *) c2;
	return strcasecmp(info1->ext, info2->ext);
}

/**
 * struct pattern_db - structure needed to match of file system,
 * all digits converted to xxx.
 * @pattern - pattern of DCIM fs.
 * @vendor - vendor name of DCIM fs.
 * @quirk - maske to read numerik part.
 * @tag - tag provided directory name.
 */
struct pattern_db {
	const char *pattern;
	const int vendor;
	const int quirk;
	const int tag;
} pattern_db[] = {
	/* nicon */
	{"xxxNIKON",	NICON,	Q_THIRST_3,	VEND_ID},
	{"xxxP_xxx",	NICON,	Q_THIRST_3,	GR_PANR},
	{"xxxINTVL",	NICON,	Q_THIRST_3,	GR_INTV},

	/*  WAV in sound folder - voice record */
	{"xxxSOUND",	NICON,	Q_THIRST_3,	GR_NONE},

	/*  JPG, AVI, WAV(comment)to in base */
	{"DSCNxxxx",	NICON,	Q_LAST_4,	GR_NONE},

	/* same exif time to base. */
	{"SSCNxxxx",	NICON,	Q_LAST_4,	REL_MOD},
	{"FSCNxxxx",	NICON,	Q_LAST_4,	REL_MOD},

	/* canon */
	{"xxxCANON",	CANON,	Q_THIRST_3,	VEND_ID},
	{"CANONMSC",	CANON,	0,		GR_NONE},

	{"IMG_xxxx",	CANON,	Q_LAST_4,	GR_NONE}, /* JPG */
	{"CRW_xxxx",	CANON,	Q_LAST_4,	GR_NONE}, /* CRW */

	/* JPG, b = A  picture in panoram group, then B, C */
	{"STx_xxxx",	CANON,	Q_LAST_4,	GR_PANR},

	/* AVI or THM */
	{"MVI_xxxx",	CANON,	Q_LAST_4,	GR_NONE},

	/* WAV , xxxx same as jpg to com */
	{"SND_xxxx",	CANON,	Q_LAST_4,	GR_NONE},

	{"xxx_FUJI",	FUIJI,	Q_THIRST_3,	VEND_ID},

	/*  JPG, RAF (raw), AVI, WAV (WAV with same xxxx as JPG is comment) */
	{"DSCFxxxx",	FUIJI,	Q_LAST_4,	GR_NONE},

	{"xxx_PANA",	PANAS,	Q_THIRST_3,	VEND_ID},

	/* JPG, MOV (vid), MOV aud comment xxx same for JPG */
	{"Pxxxxxxx",	PANAS,	Q_LAST_4,	GR_NONE}
};

#define sizeof_pattern_db (sizeof(pattern_db))/sizeof(struct pattern_db)

/**
 * compare_pattern - compare function needed to sort and search in pattern_db
 */
int compare_pattern (const void *c1, const void *c2)
{
	struct pattern_db *info1 = (struct pattern_db *) c1;
	struct pattern_db *info2 = (struct pattern_db *) c2;
	return strcasecmp(info1->pattern, info2->pattern);
}

/**
 * match_pattern - function to match pattern of DCIM fs.
 */
static int match_pattern(const char *name, int tflag, struct FTW *ftwbuf)
{
	char patt_tmp[8];
	int i = 0;
	char val[5];
	struct pattern_db key, *res;

	/* convert all digits to x, so we can match this pattern */
	while (i < 9) {
		if ((name[i] >= '0') && (name[i] <= '9')) {
			patt_tmp[i] = 'x';
		} else
			patt_tmp[i] = name[i];
		/* exception for canon, it use letter for panorama group */
		/* TODO: handle better relations, B -> A, C -> B, */
		if ((i == 2) && ((ftwbuf->level - current.dcim_level) == 2) &&
				(current.vendor == CANON) &&
				(name[0] == 'S' && name[1] == 'T'))
			patt_tmp[i] = 'x';
		/* if we parse filenames we have patt_tmp[8] == '.',
		 * so let us make sure it is '\0' */
		if (i == 8)
			patt_tmp[i] = '\0';
		i += 1;
	}

	/* search for pattern */
	key.pattern = patt_tmp;
	res = bsearch(&key, pattern_db, sizeof_pattern_db,
			sizeof(struct pattern_db), compare_pattern);
	/* stop here if didn't found */
	if (res == NULL)
		return NOT_MATCH;

	/* after we know what we have, we can use quirk for numerik part */
	if (res->quirk == Q_THIRST_3)
		sscanf(name, "%3s", val);
	else if (res->quirk == Q_LAST_4)
		sscanf(name, "%*4c%4s", val);

	current.number = atoi(val);

	/* not all pattern provide us vendor id, just test to be sure */
	if (res->tag == VEND_ID)
		current.vendor = res->vendor;

	/* we can set l2_tag now, we get here only on level 2
	 * and we will use this info on level 3 too */
	if ((ftwbuf->level - current.dcim_level) == 1)
		current.l2_tag = res->tag;
	else if ((ftwbuf->level - current.dcim_level) == 2) {
		current.l3_tag = res->tag;
		/* if panorama or interval photo,
		 * apply level 2 tag to level 3 */
		if (((current.l2_tag == GR_PANR) ||
				(current.l2_tag == GR_INTV)) &&
				(current.l3_tag == GR_NONE))
			current.l3_tag = current.l2_tag;
	}

	return MATCH;
}

/**
 * check_file_ext - function to check file extention, and file type.
 */
static int check_file_ext(const char *file_name)
{
	char *val;
	struct extinfo key, *res;

	/* first of all find point in file name */
	val = strrchr(file_name, '.');

	/* if there is no point, drop it */
	if (val == NULL)
		return NOT_MATCH;

	/* map pinter to the next char after point */
	++val;
	key.ext = val;
	/* search for  file extension in extinfo database */
	res = bsearch(&key, extinfo, sizeof_extinfo,
			sizeof(struct extinfo), compare_ext);

	if (res == NULL)
		return NOT_MATCH;
	/* we may need file type info later  */
	current.file_type = res->file_type;

	return MATCH;
}

/**
 * direct_out - push out directly files we do not care of but are images.
 */
static int direct_out(const char *fpath)
{
	printf("direct: %s\n", fpath);
	return 0;
}

/**
 * buff_trigger - sort, parse and push out content of file buffer.
 * in file buffer are only files we can match.
 */
static int buff_trigger(void)
{
	if (current.bufnum != 0) {
		int i = 0;

		printf("drop old and start new\n");

		/* sort filebuffer by file numbers */
		qsort(filebuffer, current.bufnum,
				sizeof(struct filebuffer), compare_int);
		while (i < current.bufnum) {
			/* push out sortet file */
			printf("buf: %s%s, %i, %i\n", current.pre_path,
				filebuffer[i].file, filebuffer[i].tag,
					filebuffer[i].number);

			/* check this file and file before have same name.
			 * Only audio comments have same file names */
			if ((filebuffer[i].number == filebuffer[i-1].number) &&
			   ((filebuffer[i-1].file_type == JPG_IMG) ||
			    (filebuffer[i-1].file_type == RAW_IMG)) &&
			   ((filebuffer[i].file_type == AUDIO) ||
			    (filebuffer[i].file_type == VIDEO))) {
				printf("tadaaaaa thi is audio comment\n");
			}
			i += 1;
		}
		/* reset buffer counter */
		current.bufnum = 0;
		current.pre_path[0] = '\0';
	}
	return 0;
}

/**
 * add_to_buf - add matched files to buffer.
 */
static int add_to_buf(const char *fpath)
{
	const char *ptr;

	/* DCIM structure is static
	 * DCIM/101CANON/STA_1256.JPG = 26 char.
	 * we remove here prefix and leave only interesting part */
	int pre_path_size = strlen(fpath) - 26;
	ptr = fpath + pre_path_size;

	strncpy(current.pre_path, fpath, pre_path_size);
	current.pre_path[pre_path_size] = '\0';

	/* copy content to the buffer */
	strcpy(filebuffer[current.bufnum].file, ptr);
	filebuffer[current.bufnum].number = current.number;
	filebuffer[current.bufnum].tag = current.l3_tag;
	filebuffer[current.bufnum].file_type = current.file_type;

	/* update buffer counter */
	current.bufnum += 1;

	return 0;
}

/**
 * belong_to_dcim - function to check if processed names belong to
 * DCIM name space.
 */
static int belong_to_dcim(const char *fpath, int tflag, struct FTW *ftwbuf)
{
	if (current.dcim_level == 0) {
		if ((tflag == FTW_D) && 
			(!strcmp(fpath + ftwbuf->base,  "DCIM"))) {
				current.dcim_level = ftwbuf->level;
		}
	} else if ((ftwbuf->level - current.dcim_level) < 0) {
		current.dcim_level = 0;
		return NOT_MATCH;
	}

	/* check if there is a struction we know about */
	if ((ftwbuf->level - current.dcim_level) == 0) {
		/* we know about DCIM and MISC, 
		 * if there are images outside of this, 
		 * we still can copy them, but we do not need to 
		 * tag it.
		 */
		if ((tflag == FTW_D) && 
			    (!strcmp(fpath + ftwbuf->base,  "DCIM"))) {
			current.dcim_1 = D_DCIM;
			/* reset l2_tag we update dcim_1 */
			current.l2_tag = D_UNCN;
			return MATCH;
		} else if ((tflag == FTW_D) &&
			    (!strcmp(fpath + ftwbuf->base,  "MISC"))) {

			current.dcim_1 = D_MISC;
			current.l2_tag = D_UNCN;
			return MATCH;
		} else {
			current.dcim_1 = D_UNCN;
			current.l2_tag = D_UNCN;
			return NOT_MATCH;
		}
	} else if ((ftwbuf->level - current.dcim_level) == 1) {
		/* on this level should be only dirs,
		 * do not bother to match it */
		if ((tflag == FTW_D) &&
				(current.dcim_1 == D_DCIM) &&
				(strlen(fpath + ftwbuf->base) == 8)) {

			if (match_pattern(fpath + ftwbuf->base,
						tflag, ftwbuf) == MATCH)
				return MATCH;
			else
				current.l2_tag = D_UNCN;
				return NOT_MATCH;
		} else {
			current.l2_tag = D_UNCN;
			return NOT_MATCH;
		}
	} else if ((ftwbuf->level - current.dcim_level) == 2) {
		if ((tflag == FTW_F) &&
				/* extra check dir we currently in */
				(current.l2_tag != D_UNCN) &&
				(strlen(fpath + ftwbuf->base) == 12)) {

			if (match_pattern(fpath + ftwbuf->base,
						tflag, ftwbuf) == MATCH)
				return MATCH;
		}
	}
	return NOT_MATCH;
}

static int file_info(const char *fpath)
{
	struct stat stat_buff;

	stat(fpath, &stat_buff);
	current.file_size = stat_buff.st_size;
	current.all_size += stat_buff.st_size;

}

static int prezess_file(const char *fpath, const struct stat *sb,
				int tflag, struct FTW *ftwbuf)
{
	int err;

	/* drop files which extensions we do not know */
	if ((tflag == FTW_F) && 
			(check_file_ext(fpath + ftwbuf->base) == NOT_MATCH))
		return 0;

	/* check if we are in DCIM structure */
	err = belong_to_dcim(fpath, tflag, ftwbuf);

	/* trigger buffer if we in a new directory */
	if (current.level != ftwbuf->level)
		buff_trigger();

	current.level = ftwbuf->level;

	/* now we need only files */
	if (tflag != FTW_F)
		return 0;

	file_info(fpath);
	/* patterns we can match got to the buffer, other image files
	 * go direct out. We need buffer here to sort things before we
	 * can put it together */
	if (err == MATCH)
		add_to_buf(fpath);
	else
		direct_out(fpath);

	return 0;
	
}

int main(int argc, char *argv[])
{
	current.bufnum = 0;
	current.dcim_level = 0;

	/* create our tmp storage */
	struct current *curr_pnt;
	curr_pnt = &current;

	/* sort databases */
	qsort(extinfo, sizeof_extinfo,
			sizeof(struct extinfo), compare_ext);
	qsort(pattern_db, sizeof_pattern_db,
			sizeof(struct pattern_db), compare_pattern);

	/* read directory tree */
	if (nftw((argc < 2) ? "." : argv[1], prezess_file, 20,
					FTW_PHYS | FTW_MOUNT) == -1) {
		perror("nftw");
		exit(EXIT_FAILURE);
	}
	/* trigger the buffer if we forgot some thing in there */
	if (current.bufnum != 0)
		buff_trigger();

	printf("%lld\n", current.all_size);
	exit(EXIT_SUCCESS);
}
#define D_UNCN 0x0f
#define D_DCIM 0x01
#define D_MISC 0x02
#define Q_LAST_4 0x4
#define Q_THIRST_3 0x3

#define JPG_IMG 0x01
#define RAW_IMG 0x02
#define AUDIO   0x03
#define VIDEO   0x04
#define PRINT_JOB 0x10

#define NICON 0x01
#define CANON 0x02
#define FUIJI 0x03
#define PANAS 0x04 /* panasonic */

#define GR_NONE 0x00 /* do not set any group */
#define GR_PANR 0x01 /* panorama group */
#define GR_INTV 0x02 /* interval group */
#define VEND_ID 0x03 /* we can match vendor here */
#define REL_MOD 0x04 /* related to some photo */
#define REL_COM 0x05

#define MATCH 1
#define NOT_MATCH 0

/**
 * struct current - structure for temporary storage.
 * @dcim_1 - flag we set if we found DCIM folder.
 * @l2_tag - information provided on second level of DCIM system.
 * @l3_tag - information provided on third level of DCIM system.
 * @number - number we get from file name.
 * @vendor - vendor name of fs.
 * @level - current directory level we in.
 * @bufnum - number of buffer array.
 * @file_type - file type we currently process.
 * @pre_patch - path before DCIM, we use it to safe space in the buffer.
 */
struct current {
	int dcim_1;
	int l2_tag;
	int l3_tag;
	int number;
	int vendor;
	int level;
	int dcim_level;
	int bufnum;
	int file_type;
	char pre_path[1024];
	long long file_size;
	long long all_size;
} current;
_______________________________________________
tracker-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/tracker-list

Reply via email to