/* Cataract - Static web photo gallery generator * Copyright (C) 2009 Tomas Bzatek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * 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; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "setup.h" #include "items.h" #include "gallery-utils.h" #include "generators.h" #include "stats.h" #define DEFAULT_DATA_DIR_MODE S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH typedef struct { TGallerySetup *setup; TAlbum *items; const char *dst_dir; gboolean force_update; } TJob; static void mirror_files (TGallerySetup *setup, char **files, const char *src_tree, const char *dst_dir, const char *label) { char **extra; char *s1, *s2, *s3; int processed = 0; if (files && g_strv_length (files) > 0) { extra = files; while (*extra) { s1 = g_strstrip (*extra); if (strlen (s1) > 0) { /* First create target directory if it doesn't exist */ s2 = g_strconcat (dst_dir, "/", s1, NULL); s3 = g_path_get_dirname (s2); g_free (s2); if (g_mkdir_with_parents (s3, DEFAULT_DATA_DIR_MODE)) { log_error ("error making target extra directory '%s': %s\n", s3, strerror (errno)); g_free (s3); continue; } g_free (s3); /* Copy the file */ s2 = g_strconcat (src_tree, "/", s1, NULL); s3 = g_strconcat (dst_dir, "/", s1, NULL); if (! setup->update_mode || needs_update (s2, s3)) { if (setup->verbose) { if (processed == 0) printf ("%s", label); printf (" %s", s1); } copy_file (s2, s3); processed++; } g_free (s2); g_free (s3); } extra++; } if (setup->verbose && processed > 0) printf ("\n"); } } G_LOCK_DEFINE (items); G_LOCK_DEFINE (items_print); /* run in a cycle, returns when all completed */ static gpointer thread_func (gpointer data) { TIndexItem *item; char *imgname; int i; char *s1, *s2, *s3; int total, index, real_index; TJob *job = data; gboolean updated; do { item = NULL; total = 0; real_index = -1; index = -1; G_LOCK (items); if (job->items->items->len > 0) { for (i = 0; i < job->items->items->len; i++) { TIndexItem *item_tmp = g_ptr_array_index (job->items->items, i); if (item_tmp == NULL) { log_error ("run_job: error getting item %d\n", i); continue; } if (item_tmp->type == INDEX_ITEM_TYPE_PICTURE) { total++; if (item_tmp->gen_done == FALSE && item == NULL) { item_tmp->gen_done = TRUE; item = item_tmp; index = total; real_index = i; } } } } G_UNLOCK (items); /* actually do some work */ if (item != NULL) { imgname = g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path); updated = generate_image (job->setup, job->items, item, real_index, job->dst_dir, ! job->force_update); if (updated) { G_LOCK (items_print); if (job->setup->verbose) g_print (" [%d/%d] Processing item \"%s\"\n", index, total, imgname); G_UNLOCK (items_print); } if (updated && job->items->type == GALLERY_TYPE_ALBUM) { s1 = g_strconcat (job->setup->real_templates_dir, "/", job->setup->template_photo, NULL); s2 = g_strconcat (job->items->base_dir, "/", (item->path == NULL && item->preview) ? item->preview : item->path, NULL); s3 = g_strconcat (job->dst_dir, "/", imgname, GET_EXT (job->setup->index_file_name), NULL); /* We need to lock job->items as we're doing *lots* of accesses to it */ /* There were some replace_table failures due to that */ G_LOCK (items); write_html_image (job->setup, s1, s2, s3, item, job->items); G_UNLOCK (items); g_free (s1); g_free (s2); g_free (s3); } g_free (imgname); } } while (item != NULL); return NULL; } /* * build_tree: generate complete gallery tree based on source xml files * * src_tree = source directory of the items * dst_dir = destination of the generated items * parent_index = parent album to determine our descent in the tree * */ gboolean build_tree (TGallerySetup *setup, const char *src_tree, const char *dst_dir, TAlbum *parent_index, int parent_item_index, int jobs) { char *idx_file; TAlbum *items; TIndexItem *item; char *s1, *s2, *s3; char *thumb_dir; char *img_big_dir; char *img_orig_dir; char *template; char *dst_album_file; gboolean res; int i; TJob *job; GError *error; GThread *thread; GList *thread_list; gboolean force_update; printf ("Processing directory \"%s\"\n", src_tree); stats_dirs_inc (); #ifdef __DEBUG_ALL__ printf ("setup->real_templates_dir = %s\n", setup->real_templates_dir); #endif /* Check access permissions */ if (access (src_tree, R_OK)) { log_error ("error accessing source directory: %s\n", strerror (errno)); return FALSE; } if (access (dst_dir, R_OK | W_OK | X_OK)) { if (g_mkdir_with_parents (dst_dir, DEFAULT_DATA_DIR_MODE)) { log_error ("error creating destination directory: %s\n", strerror (errno)); return FALSE; } } /* Check the index file */ idx_file = g_strconcat (src_tree, "/index.xml", NULL); if (access (idx_file, R_OK)) { log_error ("error accessing index file '%s': %s\n", idx_file, strerror (errno)); g_free (idx_file); return FALSE; } /* Read the index file and fill items array */ items = malloc (sizeof (TAlbum)); memset (items, 0, sizeof (TAlbum)); if (! parse_album_xml (idx_file, items)) { log_error ("error reading index file '%s'\n", idx_file); g_free (idx_file); free_album_data (items); return FALSE; } items->parent_index = parent_index; items->parent_item_index = parent_item_index; /* Check if update is necessary */ dst_album_file = g_strconcat (dst_dir, "/", setup->index_file_name, NULL); force_update = (! setup->update_mode || needs_update (idx_file, dst_album_file)); g_free (idx_file); /* Copy support files */ if (! setup->support_files_use_common_root || parent_index == NULL) { /* copy only if we're in root level or old-style is active */ mirror_files (setup, setup->template_files, setup->real_templates_dir, dst_dir, " Copying template files: "); /* favicon */ if (setup->favicon_file && strlen (setup->favicon_file) > 0) { s3 = g_path_get_dirname (setup->setup_xml_path); s1 = g_strconcat (s3, "/", setup->favicon_file, NULL); s2 = g_strconcat (dst_dir, "/", setup->favicon_file, NULL); if (! setup->update_mode || needs_update (s1, s2)) { if (setup->verbose) printf (" Copying favicon: %s\n", setup->favicon_file); copy_file (s1, s2); } g_free (s1); g_free (s2); g_free (s3); } } /* Prepare target thumbnail directory */ thumb_dir = g_strconcat (dst_dir, "/", setup->thumbnail_dir, NULL); if (access (thumb_dir, R_OK | W_OK | X_OK)) if (g_mkdir_with_parents (thumb_dir, DEFAULT_DATA_DIR_MODE)) { log_error ("error making target thumbnail directory: %s\n", strerror (errno)); g_free (thumb_dir); g_free (dst_album_file); free_album_data (items); return FALSE; } g_free (thumb_dir); /* Prepare target preview and orig directories */ if (items->type == GALLERY_TYPE_ALBUM) { res = TRUE; img_big_dir = g_strconcat (dst_dir, "/", setup->img_big_dir, NULL); img_orig_dir = g_strconcat (dst_dir, "/", setup->img_orig_dir, NULL); if (access (img_big_dir, R_OK | W_OK | X_OK)) if (g_mkdir_with_parents (img_big_dir, DEFAULT_DATA_DIR_MODE)) { log_error ("error making target preview directory: %s\n", strerror (errno)); res = FALSE; } if (access (img_orig_dir, R_OK | W_OK | X_OK)) if (g_mkdir_with_parents (img_orig_dir, DEFAULT_DATA_DIR_MODE)) { log_error ("error making target full size directory: %s\n", strerror (errno)); res = FALSE; } g_free (img_big_dir); g_free (img_orig_dir); if (! res) { g_free (dst_album_file); free_album_data (items); return FALSE; } } /* Generate images + particular html pages */ job = g_new0 (TJob, 1); job->items = items; job->setup = setup; job->dst_dir = dst_dir; job->force_update = force_update; #ifdef G_THREADS_ENABLED thread_list = NULL; for (i = 0; i < jobs; i++) { error = NULL; thread = g_thread_create (thread_func, job, TRUE, &error); if (thread) thread_list = g_list_append (thread_list, thread); if (error) { log_error ("build_tree: error starting new thread: %s\n", error->message); g_clear_error (&error); } } /* wait for threads are finished */ GList *l; for (l = thread_list; l != NULL; l = l->next) { g_thread_join (l->data); } /* TODO: free threads? */ g_list_free (thread_list); #else /* threads are disabled */ thread_func (job); #endif g_free (job); /* Generate album page */ if (force_update) { if (items->type == GALLERY_TYPE_INDEX) template = g_strconcat ("/", setup->template_index, NULL); else if (items->type == GALLERY_TYPE_ALBUM) template = g_strconcat ("/", setup->template_album, NULL); else /* default to album */ template = g_strconcat ("/", setup->template_album, NULL); if (setup->verbose) printf (" Writing %s\n", setup->index_file_name); s1 = g_strconcat (setup->real_templates_dir, template, NULL); res = write_html_album (setup, s1, dst_album_file, items); g_free (s1); g_free (template); if (! res) { log_error ("error generating target index file\n"); free_album_data (items); return FALSE; } } g_free (dst_album_file); /* Recurse to sub-albums (in case of album index) */ if (items->type == GALLERY_TYPE_INDEX) { if (items->items->len > 0) { for (i = 0; i < items->items->len; i++) { item = g_ptr_array_index (items->items, i); if (item == NULL) { log_error ("build_tree: error getting item %d\n", i); continue; } if (item->type == INDEX_ITEM_TYPE_PICTURE) { s1 = g_strconcat (src_tree, "/", item->path, "/", NULL); s2 = g_strconcat (dst_dir, "/", item->path, "/", NULL); build_tree (setup, s1, s2, items, i, jobs); g_free (s1); g_free (s2); } } } } /* Copy extra files */ mirror_files (setup, items->extra_files, src_tree, dst_dir, " Copying extra files: "); free_album_data (items); return TRUE; }