Can read image from memory
This commit is contained in:
		
							
								
								
									
										36
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/lib.rs
									
									
									
									
									
								
							@@ -6,6 +6,7 @@
 | 
				
			|||||||
use std::path::Path;
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::anyhow;
 | 
					use anyhow::anyhow;
 | 
				
			||||||
 | 
					use image::DynamicImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Embed the image from `image_filename` into `music_filename`, in-place. Any errors reading ID3
 | 
					/// Embed the image from `image_filename` into `music_filename`, in-place. Any errors reading ID3
 | 
				
			||||||
/// tags from the music file or parsing the image get propagated upwards.
 | 
					/// tags from the music file or parsing the image get propagated upwards.
 | 
				
			||||||
@@ -14,10 +15,21 @@ use anyhow::anyhow;
 | 
				
			|||||||
/// Tags get written as ID3v2.3.
 | 
					/// Tags get written as ID3v2.3.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
pub fn embed_image(music_filename: &Path, image_filename: &Path) -> anyhow::Result<()> {
 | 
					pub fn embed_image(music_filename: &Path, image_filename: &Path) -> anyhow::Result<()> {
 | 
				
			||||||
    let mut tag = read_tag(music_filename)?;
 | 
					 | 
				
			||||||
    let image = image::open(&image_filename).
 | 
					    let image = image::open(&image_filename).
 | 
				
			||||||
        map_err(|e| anyhow!("Error reading image {:?}: {}", image_filename, e))?;
 | 
					        map_err(|e| anyhow!("Error reading image {:?}: {}", image_filename, e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    embed_image_from_memory(music_filename, &image)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Embed the image `image` into `music_filename`, in-place. Any errors reading ID3
 | 
				
			||||||
 | 
					/// tags from the music file get propagated upwards.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The image is encoded as a JPEG with a 90% quality setting, and embedded as a "Front cover".
 | 
				
			||||||
 | 
					/// Tags get written as ID3v2.3.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					pub fn embed_image_from_memory(music_filename: &Path, image: &image::DynamicImage) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					    let mut tag = read_tag(music_filename)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut encoded_image_bytes = Vec::new();
 | 
					    let mut encoded_image_bytes = Vec::new();
 | 
				
			||||||
    // Unwrap: Writing to a Vec should always succeed;
 | 
					    // Unwrap: Writing to a Vec should always succeed;
 | 
				
			||||||
    image.write_to(&mut encoded_image_bytes, image::ImageOutputFormat::Jpeg(90)).unwrap();
 | 
					    image.write_to(&mut encoded_image_bytes, image::ImageOutputFormat::Jpeg(90)).unwrap();
 | 
				
			||||||
@@ -42,19 +54,23 @@ pub fn embed_image(music_filename: &Path, image_filename: &Path) -> anyhow::Resu
 | 
				
			|||||||
/// there's no embedded images in the mp3 file.
 | 
					/// there's no embedded images in the mp3 file.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
pub fn extract_first_image(music_filename: &Path, image_filename: &Path) -> anyhow::Result<()> {
 | 
					pub fn extract_first_image(music_filename: &Path, image_filename: &Path) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					    extract_first_image_as_img(music_filename)?
 | 
				
			||||||
 | 
					        .save(&image_filename).
 | 
				
			||||||
 | 
					        map_err(|e| anyhow!("Couldn't write image file {:?}: {}", image_filename, e))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Extract the first found embedded image from `music_filename` and return it as image object
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Any errors from parsing id3 tags will be propagated. The function will also return an error if
 | 
				
			||||||
 | 
					/// there's no embedded images in the mp3 file.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					pub fn extract_first_image_as_img(music_filename: &Path) -> anyhow::Result<DynamicImage> {
 | 
				
			||||||
    let tag = read_tag(music_filename)?;
 | 
					    let tag = read_tag(music_filename)?;
 | 
				
			||||||
    let first_picture = tag.pictures().next();
 | 
					    let first_picture = tag.pictures().next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Some(p) = first_picture {
 | 
					    if let Some(p) = first_picture {
 | 
				
			||||||
        match image::load_from_memory(&p.data) {
 | 
					        return image::load_from_memory(&p.data)
 | 
				
			||||||
            Ok(image) => {
 | 
					            .map_err(|e| anyhow!("Couldn't load image: {}", e));
 | 
				
			||||||
                image.save(&image_filename).
 | 
					 | 
				
			||||||
                    map_err(|e| anyhow!("Couldn't write image file {:?}: {}", image_filename, e))?;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            Err(e) => return Err(anyhow!("Couldn't load image: {}", e)),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        Err(anyhow!("No image found in music file"))
 | 
					        Err(anyhow!("No image found in music file"))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
use std::env;
 | 
					use std::env;
 | 
				
			||||||
use std::fs;
 | 
					use std::fs;
 | 
				
			||||||
use std::path::{PathBuf, Path};
 | 
					 | 
				
			||||||
use std::ops::Deref;
 | 
					use std::ops::Deref;
 | 
				
			||||||
 | 
					use std::path::{Path, PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use tempfile::TempDir;
 | 
					use tempfile::TempDir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use id3_image::*;
 | 
					use id3_image::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Fixture {
 | 
					struct Fixture {
 | 
				
			||||||
@@ -45,9 +46,10 @@ fn read_tag(path: &Path) -> id3::Tag {
 | 
				
			|||||||
    id3::Tag::read_from_path(path).unwrap()
 | 
					    id3::Tag::read_from_path(path).unwrap()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_unsuccessful_image_embedding() {
 | 
					fn test_unsuccessful_image_embedding() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_no_image.mp3");
 | 
					    let song = Fixture::copy("attempt_1_no_image.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Nonexistent files
 | 
					    // Nonexistent files
 | 
				
			||||||
@@ -63,11 +65,11 @@ fn test_unsuccessful_image_embedding() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_successful_jpeg_image_embedding() {
 | 
					fn test_successful_jpeg_image_embedding() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_no_image.mp3");
 | 
					    let song = Fixture::copy("attempt_1_no_image.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
    assert!(tag.pictures().count() == 0);
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    embed_image(&song, &image).unwrap();
 | 
					    embed_image(&song, &image).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -77,7 +79,7 @@ fn test_successful_jpeg_image_embedding() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_successful_jpeg_image_embedding_with_a_broken_file() {
 | 
					fn test_successful_jpeg_image_embedding_with_a_broken_file() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_broken.mp3");
 | 
					    let song = Fixture::copy("attempt_1_broken.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    embed_image(&song, &image).unwrap();
 | 
					    embed_image(&song, &image).unwrap();
 | 
				
			||||||
@@ -88,11 +90,11 @@ fn test_successful_jpeg_image_embedding_with_a_broken_file() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_successful_png_image_embedding() {
 | 
					fn test_successful_png_image_embedding() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_no_image.mp3");
 | 
					    let song = Fixture::copy("attempt_1_no_image.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.png");
 | 
					    let image = Fixture::copy("attempt_1.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
    assert!(tag.pictures().count() == 0);
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    embed_image(&song, &image).unwrap();
 | 
					    embed_image(&song, &image).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,9 +102,41 @@ fn test_successful_png_image_embedding() {
 | 
				
			|||||||
    assert!(tag.pictures().count() > 0);
 | 
					    assert!(tag.pictures().count() > 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn test_successful_png_image_embedding_from_memory() {
 | 
				
			||||||
 | 
					    let song = Fixture::copy("attempt_1_no_image.mp3");
 | 
				
			||||||
 | 
					    let image = Fixture::copy("attempt_1.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    embed_image_from_memory(&song, &image::open(&*image).unwrap()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
 | 
					    assert!(tag.pictures().count() > 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn test_successful_png_image_embedding_and_extracting() {
 | 
				
			||||||
 | 
					    let song = Fixture::copy("attempt_1_no_image.mp3");
 | 
				
			||||||
 | 
					    let image = Fixture::copy("attempt_1.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extract_first_image_as_img(&song).unwrap_err();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    embed_image(&song, &image).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
 | 
					    assert!(tag.pictures().count() > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extract_first_image_as_img(&song).unwrap();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_successful_image_embedding_in_a_file_that_already_has_an_image() {
 | 
					fn test_successful_image_embedding_in_a_file_that_already_has_an_image() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1.mp3");
 | 
					    let song = Fixture::copy("attempt_1.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
@@ -116,7 +150,7 @@ fn test_successful_image_embedding_in_a_file_that_already_has_an_image() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_removing_and_adding_an_image() {
 | 
					fn test_removing_and_adding_an_image() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1.mp3");
 | 
					    let song = Fixture::copy("attempt_1.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
@@ -125,7 +159,7 @@ fn test_removing_and_adding_an_image() {
 | 
				
			|||||||
    remove_images(&song).unwrap();
 | 
					    remove_images(&song).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
    assert!(tag.pictures().count() == 0);
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    embed_image(&song, &image).unwrap();
 | 
					    embed_image(&song, &image).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -135,13 +169,13 @@ fn test_removing_and_adding_an_image() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_removing_and_adding_an_image_to_a_broken_file() {
 | 
					fn test_removing_and_adding_an_image_to_a_broken_file() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_broken.mp3");
 | 
					    let song = Fixture::copy("attempt_1_broken.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    remove_images(&song).unwrap();
 | 
					    remove_images(&song).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
    assert!(tag.pictures().count() == 0);
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    embed_image(&song, &image).unwrap();
 | 
					    embed_image(&song, &image).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -151,7 +185,7 @@ fn test_removing_and_adding_an_image_to_a_broken_file() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_extracting_a_jpg_image() {
 | 
					fn test_extracting_a_jpg_image() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1.mp3");
 | 
					    let song = Fixture::copy("attempt_1.mp3");
 | 
				
			||||||
    let image = Fixture::blank("attempt_1.jpg");
 | 
					    let image = Fixture::blank("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
@@ -165,7 +199,7 @@ fn test_extracting_a_jpg_image() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_extracting_a_jpg_image_from_a_broken_file() {
 | 
					fn test_extracting_a_jpg_image_from_a_broken_file() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_broken.mp3");
 | 
					    let song = Fixture::copy("attempt_1_broken.mp3");
 | 
				
			||||||
    let image = Fixture::blank("attempt_1.jpg");
 | 
					    let image = Fixture::blank("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    extract_first_image(&song, &image).unwrap();
 | 
					    extract_first_image(&song, &image).unwrap();
 | 
				
			||||||
@@ -175,7 +209,7 @@ fn test_extracting_a_jpg_image_from_a_broken_file() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_extracting_a_png_image() {
 | 
					fn test_extracting_a_png_image() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1.mp3");
 | 
					    let song = Fixture::copy("attempt_1.mp3");
 | 
				
			||||||
    let image = Fixture::blank("attempt_1.png");
 | 
					    let image = Fixture::blank("attempt_1.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
@@ -189,7 +223,7 @@ fn test_extracting_a_png_image() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_overwriting_an_existing_image() {
 | 
					fn test_overwriting_an_existing_image() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1.mp3");
 | 
					    let song = Fixture::copy("attempt_1.mp3");
 | 
				
			||||||
    let image = Fixture::copy("attempt_1.jpg");
 | 
					    let image = Fixture::copy("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
@@ -203,11 +237,11 @@ fn test_overwriting_an_existing_image() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_extracting_an_image_with_no_pictures() {
 | 
					fn test_extracting_an_image_with_no_pictures() {
 | 
				
			||||||
    let song  = Fixture::copy("attempt_1_no_image.mp3");
 | 
					    let song = Fixture::copy("attempt_1_no_image.mp3");
 | 
				
			||||||
    let image = Fixture::blank("attempt_1.jpg");
 | 
					    let image = Fixture::blank("attempt_1.jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tag = read_tag(&song);
 | 
					    let tag = read_tag(&song);
 | 
				
			||||||
    assert!(tag.pictures().count() == 0);
 | 
					    assert_eq!(tag.pictures().count(), 0);
 | 
				
			||||||
    assert!(!image.exists());
 | 
					    assert!(!image.exists());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert!(extract_first_image(&song, &image).is_err());
 | 
					    assert!(extract_first_image(&song, &image).is_err());
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user