int64_t GetImageMetadata(const char *path, char *name) { ExifData *ed; ExifEntry *e = NULL; ExifLoader *l; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE *infile; int width=0, height=0, thumb=0; char make[32], model[64] = {'\0'}; char b[1024]; struct stat file; int64_t ret; image_s *imsrc; metadata_t m; uint32_t free_flags = 0xFFFFFFFF; memset(&m, '\0', sizeof(metadata_t)); //DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing %s...\n", path); if ( stat(path, &file) != 0 ) return 0; strip_ext(name); //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * size: %jd\n", file.st_size); /* MIME hard-coded to JPEG for now, until we add PNG support */ m.mime = strdup("image/jpeg"); l = exif_loader_new(); exif_loader_write_file(l, path); ed = exif_loader_get_data(l); exif_loader_unref(l); if( !ed ) goto no_exifdata; e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL); if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_DIGITIZED)) ) { m.date = strdup(exif_entry_get_value(e, b, sizeof(b))); if( strlen(m.date) > 10 ) { m.date[4] = '-'; m.date[7] = '-'; m.date[10] = 'T'; } else { free(m.date); m.date = NULL; } } else { /* One last effort to get the date from XMP */ image_get_jpeg_date_xmp(path, &m.date); } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * date: %s\n", m.date); e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_MAKE); if( e ) { strncpyt(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make)); e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_MODEL); if( e ) { strncpyt(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model)); if( !strcasestr(model, make) ) snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b))); m.creator = escape_tag(trim(model), 1); } } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * model: %s\n", model); e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION); if( e ) { int rotate; switch( exif_get_short(e->data, exif_data_get_byte_order(ed)) ) { case 3: rotate = 180; break; case 6: rotate = 90; break; case 8: rotate = 270; break; default: rotate = 0; break; } if( rotate ) xasprintf(&m.rotation, "%d", rotate); } if( ed->size ) { /* We might need to verify that the thumbnail is 160x160 or smaller */ if( ed->size > 12000 ) { imsrc = image_new_from_jpeg(NULL, 0, (char *)ed->data, ed->size, 1, ROTATE_NONE); if( imsrc ) { if( (imsrc->width <= 160) && (imsrc->height <= 160) ) thumb = 1; image_free(imsrc); } } else thumb = 1; } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * thumbnail: %d\n", thumb); exif_data_unref(ed); no_exifdata: /* If SOF parsing fails, then fall through to reading the JPEG data with libjpeg to get the resolution */ if( image_get_jpeg_resolution(path, &width, &height) != 0 || !width || !height ) { infile = fopen(path, "r"); if( infile ) { cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = libjpeg_error_handler; jpeg_create_decompress(&cinfo); if( setjmp(setjmp_buffer) ) goto error; jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); width = cinfo.output_width; height = cinfo.output_height; error: jpeg_destroy_decompress(&cinfo); fclose(infile); } } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height); if( !width || !height ) { free_metadata(&m, free_flags); return 0; } if( width <= 640 && height <= 480 ) m.dlna_pn = strdup("JPEG_SM"); else if( width <= 1024 && height <= 768 ) m.dlna_pn = strdup("JPEG_MED"); else if( (width <= 4096 && height <= 4096) || !GETFLAG(DLNA_STRICT_MASK) ) m.dlna_pn = strdup("JPEG_LRG"); xasprintf(&m.resolution, "%dx%d", width, height); ret = sql_exec(db, "INSERT into DETAILS" " (PATH, TITLE, SIZE, TIMESTAMP, DATE, RESOLUTION," " ROTATION, THUMBNAIL, CREATOR, DLNA_PN, MIME) " "VALUES" " (%Q, '%q', %lld, %ld, %Q, %Q, %Q, %d, %Q, %Q, %Q);", path, name, (long long)file.st_size, file.st_mtime, m.date, m.resolution, m.rotation, thumb, m.creator, m.dlna_pn, m.mime); if( ret != SQLITE_OK ) { fprintf(stderr, "Error inserting details for '%s'!\n", path); ret = 0; } else { ret = sqlite3_last_insert_rowid(db); } free_metadata(&m, free_flags); return ret; }
char * check_for_album_file(const char * dir, const char * path) { char file[MAXPATHLEN]; struct album_art_name_s * album_art_name; image_s * imsrc = NULL; int width=0, height=0; char * art_file; /* First look for file-specific cover art */ snprintf(file, sizeof(file), "%s.cover.jpg", path); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) goto existing_file; free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1); if( imsrc ) goto found_file; } snprintf(file, sizeof(file), "%s", path); art_file = strrchr(file, '.'); if( art_file ) strcpy(art_file, ".jpg"); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) goto existing_file; free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1); if( imsrc ) goto found_file; } /* Then fall back to possible generic cover art file names */ for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) { snprintf(file, sizeof(file), "%s/%s", dir, album_art_name->name); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) { existing_file: return art_file; } free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1); if( !imsrc ) continue; found_file: width = imsrc->width; height = imsrc->height; if( width > 160 || height > 160 ) art_file = save_resized_album_art(imsrc, file); else art_file = strdup(file); image_free(imsrc); return(art_file); } } return NULL; }
char * check_for_album_file(char * dir, const char * path) { char * file = malloc(PATH_MAX); struct album_art_name_s * album_art_name; image * imsrc = NULL; int width=0, height=0; char * art_file; /* First look for file-specific cover art */ sprintf(file, "%s.cover.jpg", path); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) goto existing_file; free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1); if( imsrc ) goto found_file; } sprintf(file, "%s", path); strip_ext(file); strcat(file, ".jpg"); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) goto existing_file; free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1); if( imsrc ) goto found_file; } /* Then fall back to possible generic cover art file names */ for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) { sprintf(file, "%s/%s", dir, album_art_name->name); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) { existing_file: free(file); return art_file; } free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1); if( !imsrc ) continue; found_file: width = imsrc->width; height = imsrc->height; if( width > 160 || height > 160 ) { art_file = file; file = save_resized_album_art(imsrc, art_file); free(art_file); } image_free(imsrc); return(file); } } free(file); return NULL; }
char * check_embedded_art(const char * path, const char * image_data, int image_size) { int width = 0, height = 0; char * art_path = NULL; char * cache_dir; FILE * dstfile; image_s * imsrc; static char last_path[PATH_MAX]; static unsigned int last_hash = 0; static int last_success = 0; unsigned int hash; if( !image_data || !image_size || !path ) { return NULL; } /* If the embedded image matches the embedded image from the last file we * checked, just make a hard link. Better than storing it on the disk twice. */ hash = DJBHash(image_data, image_size); if( hash == last_hash ) { if( !last_success ) return NULL; art_cache_exists(path, &art_path); if( link(last_path, art_path) == 0 ) { return(art_path); } else { if( errno == ENOENT ) { cache_dir = strdup(art_path); make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); free(cache_dir); if( link(last_path, art_path) == 0 ) return(art_path); } DPRINTF(E_WARN, L_METADATA, "Linking %s to %s failed [%s]\n", art_path, last_path, strerror(errno)); free(art_path); art_path = NULL; } } last_hash = hash; imsrc = image_new_from_jpeg(NULL, 0, image_data, image_size, 1); if( !imsrc ) { last_success = 0; return NULL; } width = imsrc->width; height = imsrc->height; if( width > 160 || height > 160 ) { art_path = save_resized_album_art(imsrc, path); } else if( width > 0 && height > 0 ) { size_t nwritten; if( art_cache_exists(path, &art_path) ) goto end_art; cache_dir = strdup(art_path); make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); free(cache_dir); dstfile = fopen(art_path, "w"); if( !dstfile ) { free(art_path); art_path = NULL; goto end_art; } nwritten = fwrite((void *)image_data, 1, image_size, dstfile); fclose(dstfile); if( nwritten != image_size ) { DPRINTF(E_WARN, L_METADATA, "Embedded art error: wrote %d/%d bytes\n", nwritten, image_size); remove(art_path); free(art_path); art_path = NULL; goto end_art; } } end_art: image_free(imsrc); if( !art_path ) { DPRINTF(E_WARN, L_METADATA, "Invalid embedded album art in %s\n", basename((char *)path)); last_success = 0; return NULL; } DPRINTF(E_DEBUG, L_METADATA, "Found new embedded album art in %s\n", basename((char *)path)); last_success = 1; strcpy(last_path, art_path); return(art_path); }
static char * check_for_album_file(const char *path) { char file[MAXPATHLEN]; char mypath[MAXPATHLEN]; struct album_art_name_s *album_art_name; image_s *imsrc = NULL; int width=0, height=0; char *art_file; const char *dir; struct stat st; int ret; if( stat(path, &st) != 0 ) return NULL; if( S_ISDIR(st.st_mode) ) { dir = path; goto check_dir; } strncpyt(mypath, path, sizeof(mypath)); dir = dirname(mypath); /* First look for file-specific cover art */ snprintf(file, sizeof(file), "%s.cover.jpg", path); ret = access(file, R_OK); if( ret != 0 ) { strncpyt(file, path, sizeof(file)); art_file = strrchr(file, '.'); if( art_file ) { strcpy(art_file, ".jpg"); ret = access(file, R_OK); } if( ret != 0 ) { art_file = strrchr(file, '/'); if( art_file ) { memmove(art_file+2, art_file+1, file+MAXPATHLEN-art_file-2); art_file[1] = '.'; ret = access(file, R_OK); } } } if( ret == 0 ) { if( art_cache_exists(file, &art_file) ) goto existing_file; free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1, ROTATE_NONE); if( imsrc ) goto found_file; } check_dir: /* Then fall back to possible generic cover art file names */ for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) { snprintf(file, sizeof(file), "%s/%s", dir, album_art_name->name); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) { existing_file: return art_file; } free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1, ROTATE_NONE); if( !imsrc ) continue; found_file: width = imsrc->width; height = imsrc->height; if( width > 160 || height > 160 ) art_file = save_resized_album_art(imsrc, file); else art_file = strdup(file); image_free(imsrc); return(art_file); } } //- 20130708 Sungmin add int i; struct dirent **namelist; int n = scandir(dir, &namelist, filter_image_files, alphasort); if(n>0) { snprintf(file, sizeof(file), "%s/%s", dir, namelist[0]->d_name); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) { return art_file; } free(art_file); imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1, ROTATE_NONE); if( !imsrc ) return NULL; width = imsrc->width; height = imsrc->height; //DPRINTF(E_WARN, L_METADATA, "imgsrc->size: %s, %d X %d\n", file, width, height); if( width > 160 || height > 160 ) art_file = save_resized_album_art(imsrc, file); else art_file = strdup(file); image_free(imsrc); for(i=0; i<n; i++) { free(namelist[i]); } } } return NULL; }