Skip to content

Commit

Permalink
Audiotags2 -> Audiotags (#18)
Browse files Browse the repository at this point in the history
* Upgrade id3 from 0.5.1 to 1.0.3

* Upgrade mp4ameta from 0.6 to 0.11

* Copy test files to tmp directory before running tests

* Run GitHub actions on all PR

* Import @RodolpheThienard's duration (#4)

* give back the duration for mp4 and mp3

* calcul duration for flac

* adding set_duration

* fix duration for all extension

* Remove set_duration + reset test data

Co-authored-by: RodolpheThienard <[email protected]>

* Import @Dalvany's genre (#5)

* Add genre

Add genre tag

* Add some tests

Co-authored-by: Dalvany <[email protected]>

* Establish fork (#6)

* Adapt Cargo + License + Changelog

* Update GitHub actions to use actions-rs

* Fix formatting

* Fix clippy

* Re-fix formatting

* Adapt README.md

* Rename crate, update README.md

* Fix cargo test

* Fix clippy (again)

* Docs improvements

* Remove deprecated function

* Remove old documentation

* 0.3.1

* Re-merged Cargo.toml for 0.4.0

* Re-merged License

* Re-merge README.md

* Re-merge lib.rs tests

* Happy clippy is the best clippy

Co-authored-by: Pierre de la Martinière <[email protected]>
Co-authored-by: RodolpheThienard <[email protected]>
Co-authored-by: Dalvany <[email protected]>
  • Loading branch information
4 people authored Jul 31, 2022
1 parent 0ac19f0 commit 039f2f4
Show file tree
Hide file tree
Showing 63 changed files with 416 additions and 4,845 deletions.
64 changes: 0 additions & 64 deletions .github/workflow/main.yml

This file was deleted.

72 changes: 57 additions & 15 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
name: Rust

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
on: [push, pull_request]

env:
CARGO_TERM_COLOR: always
name: Rust

jobs:
build:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: check

test:
name: Test Suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test

fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## [0.3.1] 2022-05-25

- Upgraded `id3` from 1.0.3 to 1.1.0

## [0.3.0] 2022-05-25

- Added support for `duration`
- Added support for `genre`
- Upgraded `id3` from 0.5.1 to 1.0.3
- Upgrade `mp4ameta` from 0.6 to 0.11
- Execute tests from tmp directory to avoid repo corruption

## [0.2.7182] 2020-10-29

- Improve docs
Expand Down Expand Up @@ -29,4 +41,4 @@

## [0.2.0] 2020-10-26

- conversion between tag types (naive and unstable implementation)
- conversion between tag types (naive and unstable implementation)
27 changes: 17 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
[package]
name = "audiotags"
version = "0.2.7182"
authors = ["Tianyi <[email protected]>"]
edition = "2018"
version = "0.4.0"
authors = ["Tianyi <[email protected]>", "Pierre de la Martinière <[email protected]>"]
edition = "2021"
description = "Unified IO for different types of audio metadata"
license = "MIT"
repository = "https://github.com/TianyiShi2001/audiotags"
repository = "https://github.com/martpie/audiotags"
keywords = ["id3", "tag", "tags", "audio", "audiotags"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
id3 = "0.5.1"
mp4ameta = "0.6"
metaflac = "0.2"
thiserror = "1.0.21"
id3 = "1.1.0"
mp4ameta = "0.11.0"
metaflac = "0.2.5"
thiserror = "1.0.31"
audiotags-dev-macro = {path = "./audiotags-dev-macro", version = "0.1.4"}

[dev-dependencies]
tempfile = "3.3.0"

[build-dependencies]
readme-rustdocifier = "0.1.0"

[features]
defualt = ['from']
from = []
default = ['from']
from = []
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2020 Tianyi Shi
Copyright (c) 2022 Pierre de la Martinière

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
101 changes: 44 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,63 @@
[![Crate](https://img.shields.io/crates/d/audiotags.svg)](https://crates.io/crates/audiotags)
[![Crate](https://img.shields.io/crates/l/audiotags.svg)](https://crates.io/crates/audiotags)
[![Documentation](https://docs.rs/audiotags/badge.svg)](https://docs.rs/audiotags/)
[![Manual](https://img.shields.io/badge/RTFM-Manual-blue)](https://tianyishi2001.github.io/audiotags)

**audiotags** makes it easier to **parse, convert and write metadata** (a.k.a tag) in audio files of different file types.
This crate makes it easier to parse, convert and write metadata (a.k.a tag) in audio files of different file types.

This crate aims to provide a unified trait for parsers and writers of different audio file formats. This means that you can parse tags in mp3, flac, and m4a files with a single function: `Tag::default().read_from_path()` and get fields by directly calling `.album()`, `.artist()` on its result. Without this crate, you would otherwise need to learn different APIs in **id3**, **mp4ameta** etc. in order to parse metadata in different file formats.
This crate aims to provide a unified trait for parsers and writers of different audio file formats.
This means that you can parse tags in mp3, flac, and m4a files with a single function: `Tag::default().
read_from_path()` and get fields by directly calling `.album()`, `.artist()` on its result. Without this
crate, you would otherwise need to learn different APIs in **id3**, **mp4ameta** etc. in order to parse
metadata in different file formats.

I'm relatively new to Rust and programming in general, and this is my first attempt to make a non-trivial crate. If you see anything that you think isn't right, just say it!
### Performance

## Examples
Using **audiotags** incurs a little overhead due to vtables if you want to guess the metadata format
(from file extension). Apart from this the performance is almost the same as directly calling function
provided by those 'specialized' crates.

Examples can be found in the [manual](https://tianyishi2001.github.io/audiotags).
No copies will be made if you only need to read and write metadata of one format. If you want to convert
between tags, copying is unavoidable no matter if you use **audiotags** or use getters and setters provided
by specialized libraries. **audiotags** is not making additional unnecessary copies.

## Performance

Using **audiotags** incurs a little overhead due to vtables if you want to guess the metadata format (from file extension). Apart from this the performance is almost the same as directly calling function provided by those 'specialized' crates. (It is possible to use **audiotags** *without* dynamic dispatch, in which case you need to specify the tag type but benefit from speed improvement).

No copies will be made if you only need to read and write metadata of one format. If you want to convert between tags, copying is unavoidable no matter if you use **audiotags** or use getters and setters provided by specialized libraries. **audiotags** is not making additional unnecessary copies.

Theoretically it is possible to achieve zero-copy conversions if all parsers can parse into a unified struct. However, this is going to be a lot of work. I might be able to implement them, but it will be no sooner than the Christmas vacation.

## Supported Formats
### Supported Formats

| File Fomat | Metadata Format | backend |
| ------------- | --------------------- | ----------------------------------------------------------- |
| `mp3` | id3v2.4 | [**id3**](https://github.com/polyfloyd/rust-id3) |
| `m4a/mp4/...` | MPEG-4 audio metadata | [**mp4ameta**](https://github.com/Saecki/rust-mp4ameta) |
| `flac` | Vorbis comment | [**metaflac**](https://github.com/jameshurst/rust-metaflac) |

## Getters and Setters
### Examples

Read the [manual](https://docs.rs/audiotags) for some examples, but here's a quick-one:

```rust
pub trait AudioTagEdit{
fn title(&self) -> Option<&str>;
fn set_title(&mut self, title: &str);
fn remove_title(&mut self);
fn artist(&self) -> Option<&str>;
fn remove_artist(&mut self);
fn set_artist(&mut self, artist: &str);
fn year(&self) -> Option<i32>;
fn set_year(&mut self, year: i32);
fn remove_year(&mut self);
fn album(&self) -> Option<Album>;
fn remove_album(&mut self);
fn album_title(&self) -> Option<&str>;
fn remove_album_title(&mut self);
fn album_artist(&self) -> Option<&str>;
fn remove_album_artist(&mut self);
fn album_cover(&self) -> Option<Picture>;
fn remove_album_cover(&mut self);
fn set_album(&mut self, album: Album);
fn set_album_title(&mut self, v: &str);
fn set_album_artist(&mut self, v: &str);
fn set_album_cover(&mut self, cover: Picture);
fn track(&self) -> (Option<u16>, Option<u16>);
fn set_track(&mut self, track: (u16, u16));
fn remove_track(&mut self);
fn track_number(&self) -> Option<u16>;
fn set_track_number(&mut self, track_number: u16);
fn remove_track_number(&mut self);
fn total_tracks(&self) -> Option<u16>;
fn set_total_tracks(&mut self, total_track: u16);
fn remove_total_tracks(&mut self);
fn disc(&self) -> (Option<u16>, Option<u16>);
fn set_disc(&mut self, disc: (u16, u16));
fn remove_disc(&mut self);
fn disc_number(&self) -> Option<u16>;
fn set_disc_number(&mut self, disc_number: u16);
fn remove_disc_number(&mut self);
fn total_discs(&self) -> Option<u16>;
fn set_total_discs(&mut self, total_discs: u16);
fn remove_total_discs(&mut self);
fn main() {
// using `default()` or `new()` alone so that the metadata format is
// guessed (from the file extension) (in this case, Id3v2 tag is read)
let mut tag = Tag::new().read_from_path(MP3_FILE).unwrap();

tag.set_title("foo title");
assert_eq!(tag.title(), Some("foo title"));
tag.remove_title();
assert!(tag.title().is_none());
tag.remove_title();
// trying to remove a field that's already empty won't hurt

let cover = Picture {
mime_type: MimeType::Jpeg,
data: &vec![0u8; 10],
};

tag.set_album_cover(cover.clone());
assert_eq!(tag.album_cover(), Some(cover));
tag.remove_album_cover();
assert!(tag.album_cover().is_none());
tag.remove_album_cover();

tag.write_to_path(MP3_FILE).expect("Fail to save");
}
```
```

License: MIT
Loading

0 comments on commit 039f2f4

Please sign in to comment.