Skip to content
This repository has been archived by the owner on Aug 1, 2022. It is now read-only.

Commit

Permalink
Establish fork (#6)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
martpie committed May 25, 2022
1 parent 04a5f71 commit 1276b3a
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 238 deletions.
64 changes: 0 additions & 64 deletions .github/workflow/main.yml

This file was deleted.

65 changes: 56 additions & 9 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,64 @@
name: Rust

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-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
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## [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 +37,4 @@

## [0.2.0] 2020-10-26

- conversion between tag types (naive and unstable implementation)
- conversion between tag types (naive and unstable implementation)
19 changes: 11 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
[package]
name = "audiotags"
version = "0.2.7182"
authors = ["Tianyi <ShiTianyi2001@outlook.com>"]
edition = "2018"
description = "Unified IO for different types of audio metadata"
name = "audiotags2"
version = "0.3.0"
authors = ["Pierre de la Martinière <pierre.de.la.martiniere@gmail.com>"]
edition = "2021"
description = "Unified IO for different types of audio metadata (maintained fork)"
license = "MIT"
repository = "https://github.com/TianyiShi2001/audiotags"
repository = "https://github.com/martpie/audiotags"

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

[dependencies]
id3 = "1.0.3"
mp4ameta = "0.11.0"
metaflac = "0.2"
thiserror = "1.0.21"
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]
default = ['from']
from = []
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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
111 changes: 50 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,68 @@
# audiotags
# audiotags2

[![Crate](https://img.shields.io/crates/v/audiotags.svg)](https://crates.io/crates/audiotags)
[![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)
[![Crate](https://img.shields.io/crates/v/audiotags2.svg)](https://crates.io/crates/audiotags2)
[![Crate](https://img.shields.io/crates/d/audiotags2.svg)](https://crates.io/crates/audiotags2)
[![Crate](https://img.shields.io/crates/l/audiotags2.svg)](https://crates.io/crates/audiotags2)
[![Documentation](https://docs.rs/audiotags2/badge.svg)](https://docs.rs/audiotags2/)

**audiotags** makes it easier to **parse, convert and write metadata** (a.k.a tag) in audio files of different file types.
`audiotags2` is a fork of [`audiotags`](https://crates.io/crates/audiotags2) which is currently unmaintained.

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 makes it easier to parse, convert and write metadata (a.k.a tag) in audio files of different file types.

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!
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.

## Examples
### Performance

Examples can be found in the [manual](https://tianyishi2001.github.io/audiotags).
Using **audiotags2** 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.

## Performance
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 **audiotags2** or use getters and setters provided
by specialized libraries. **audiotags2** is not making additional unnecessary copies.

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
13 changes: 5 additions & 8 deletions src/anytag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ pub struct AnyTag<'a> {
pub total_tracks: Option<u16>,
pub disc_number: Option<u16>,
pub total_discs: Option<u16>,
pub genre:Option<&'a str>,
pub genre: Option<&'a str>,
}

impl AudioTagConfig for AnyTag<'_> {
fn config(&self) -> &Config {
&self.config
}
fn set_config(&mut self, config: Config) {
self.config = config.clone();
self.config = config;
}
}

impl<'a> AnyTag<'a> {
pub fn title(&self) -> Option<&str> {
self.title.as_deref()
self.title
}
pub fn set_title(&mut self, title: &'a str) {
self.title = Some(title);
Expand All @@ -46,11 +46,8 @@ impl<'a> AnyTag<'a> {
pub fn duration(&self) -> Option<f64> {
self.duration
}
pub fn set_duration(&mut self, duration: f64) {
self.duration = Some(duration);
}
pub fn album_title(&self) -> Option<&str> {
self.album_title.as_deref()
self.album_title
}
pub fn album_artists(&self) -> Option<&[&str]> {
self.album_artists.as_deref()
Expand All @@ -68,7 +65,7 @@ impl<'a> AnyTag<'a> {
self.total_tracks
}
pub fn genre(&self) -> Option<&str> {
self.genre.as_deref()
self.genre
}
}

Expand Down
Loading

0 comments on commit 1276b3a

Please sign in to comment.