Parse id3 tags from mpeg files
This commit is contained in:
parent
2cc7bf2bca
commit
4606a117f6
|
@ -124,10 +124,14 @@ export class TrackCard extends HTMLElement {
|
||||||
trackName.name = this["name"];
|
trackName.name = this["name"];
|
||||||
container.appendChild(trackName);
|
container.appendChild(trackName);
|
||||||
}
|
}
|
||||||
this["length"] && container.appendChild(document.createTextNode("1:23"));
|
if (this["length"]) {
|
||||||
this["album"] &&
|
container.appendChild(document.createTextNode(this["length"]));
|
||||||
container.appendChild(document.createTextNode("Shatter Me"));
|
}
|
||||||
this["artist"] &&
|
if (this["album"]) {
|
||||||
container.appendChild(document.createTextNode("Lindsey Stirling"));
|
container.appendChild(document.createTextNode(this["album"]));
|
||||||
|
}
|
||||||
|
if (this["artist"]) {
|
||||||
|
container.appendChild(document.createTextNode(this["artist"]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ const updateTrackList = (tracks: TrackInfo[]) => {
|
||||||
let track_formats = _.map(tracks, (info) => {
|
let track_formats = _.map(tracks, (info) => {
|
||||||
let card: TrackCard = document.createElement("track-card");
|
let card: TrackCard = document.createElement("track-card");
|
||||||
card.name = info.name || null;
|
card.name = info.name || null;
|
||||||
|
card.album = info.album || null;
|
||||||
|
card.artist = info.artist || null;
|
||||||
return card;
|
return card;
|
||||||
});
|
});
|
||||||
_.map(track_formats, (trackCard) => {
|
_.map(track_formats, (trackCard) => {
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
|
@ -77,6 +83,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -118,6 +133,16 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flow"
|
name = "flow"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -329,6 +354,17 @@ dependencies = [
|
||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "id3"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19d7a833474b30425eb64132d1f9b727b4e39537418bcc3288497c8d2f5c8948"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"byteorder",
|
||||||
|
"flate2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -421,6 +457,15 @@ dependencies = [
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -456,6 +501,7 @@ name = "music-player"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flow",
|
"flow",
|
||||||
|
"id3",
|
||||||
"mime",
|
"mime",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
|
|
@ -7,8 +7,9 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
flow = { path = "../../flow" }
|
flow = { path = "../../flow" }
|
||||||
mime = { version = "0.3" }
|
id3 = { version = "1.6" }
|
||||||
mime_guess = { version = "2.0" }
|
mime_guess = { version = "2.0" }
|
||||||
|
mime = { version = "0.3" }
|
||||||
rusqlite = { version = "0.28" }
|
rusqlite = { version = "0.28" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
thiserror = { version = "1.0" }
|
thiserror = { version = "1.0" }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::audio::{TrackId, TrackInfo};
|
use crate::audio::{TrackId, TrackInfo};
|
||||||
|
use id3::{Tag, TagLike};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{DirEntry, ReadDir},
|
fs::{DirEntry, ReadDir},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -7,10 +8,12 @@ use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ScannerError {
|
pub enum ScannerError {
|
||||||
#[error("Cannot scan {0}")]
|
#[error("Cannot scan file")]
|
||||||
CannotScan(PathBuf),
|
CannotScan,
|
||||||
#[error("Not found {0}")]
|
#[error("File not found")]
|
||||||
NotFound(PathBuf),
|
FileNotFound,
|
||||||
|
#[error("Tag not found")]
|
||||||
|
TagNotFound,
|
||||||
#[error("IO error {0}")]
|
#[error("IO error {0}")]
|
||||||
IO(std::io::Error),
|
IO(std::io::Error),
|
||||||
}
|
}
|
||||||
|
@ -21,6 +24,19 @@ impl From<std::io::Error> for ScannerError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<id3::Error> for ScannerError {
|
||||||
|
fn from(err: id3::Error) -> ScannerError {
|
||||||
|
match err.kind {
|
||||||
|
id3::ErrorKind::Io(err) => ScannerError::IO(err),
|
||||||
|
id3::ErrorKind::StringDecoding(_) => ScannerError::CannotScan,
|
||||||
|
id3::ErrorKind::NoTag => ScannerError::TagNotFound,
|
||||||
|
id3::ErrorKind::Parsing => ScannerError::CannotScan,
|
||||||
|
id3::ErrorKind::InvalidInput => ScannerError::CannotScan,
|
||||||
|
id3::ErrorKind::UnsupportedFeature => ScannerError::CannotScan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait MusicScanner: Send {
|
pub trait MusicScanner: Send {
|
||||||
fn scan<'a>(&'a self) -> Box<dyn Iterator<Item = Result<TrackInfo, ScannerError>> + 'a>;
|
fn scan<'a>(&'a self) -> Box<dyn Iterator<Item = Result<TrackInfo, ScannerError>> + 'a>;
|
||||||
}
|
}
|
||||||
|
@ -49,19 +65,10 @@ impl FileIterator {
|
||||||
);
|
);
|
||||||
let mimetype = mime_guess::from_path(path.clone())
|
let mimetype = mime_guess::from_path(path.clone())
|
||||||
.first()
|
.first()
|
||||||
.ok_or(ScannerError::CannotScan(path.clone()))?;
|
.ok_or(ScannerError::CannotScan)?;
|
||||||
match (mimetype.type_(), mimetype.subtype().as_str()) {
|
match (mimetype.type_(), mimetype.subtype().as_str()) {
|
||||||
(mime::AUDIO, "mpeg") => Ok(TrackInfo {
|
(mime::AUDIO, "mpeg") => TrackInfo::scan_id3(path, mimetype),
|
||||||
id: TrackId::from(path.to_str().unwrap().to_owned()),
|
|
||||||
album: None,
|
|
||||||
artist: None,
|
|
||||||
name: path
|
|
||||||
.file_stem()
|
|
||||||
.and_then(|s| s.to_str())
|
|
||||||
.map(|s| s.to_owned()),
|
|
||||||
track_number: None,
|
|
||||||
filetype: mimetype,
|
|
||||||
}),
|
|
||||||
/*
|
/*
|
||||||
(mime::AUDIO, "ogg") => Ok(TrackInfo {
|
(mime::AUDIO, "ogg") => Ok(TrackInfo {
|
||||||
id: TrackId::from(path.to_str().unwrap().to_owned()),
|
id: TrackId::from(path.to_str().unwrap().to_owned()),
|
||||||
|
@ -86,11 +93,30 @@ impl FileIterator {
|
||||||
filetype: mimetype,
|
filetype: mimetype,
|
||||||
}),
|
}),
|
||||||
*/
|
*/
|
||||||
_ => Err(ScannerError::CannotScan(path)),
|
_ => Err(ScannerError::CannotScan),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TrackInfo {
|
||||||
|
fn scan_id3(path: PathBuf, mimetype: mime::Mime) -> Result<TrackInfo, ScannerError> {
|
||||||
|
let tags = Tag::read_from_path(path.clone()).map_err(ScannerError::from)?;
|
||||||
|
|
||||||
|
Ok(TrackInfo {
|
||||||
|
id: TrackId::from(path.to_str().unwrap().to_owned()),
|
||||||
|
album: tags.album().map(|s| s.to_owned()),
|
||||||
|
artist: tags.artist().map(|s| s.to_owned()),
|
||||||
|
name: tags.title().map(|s| s.to_owned()).or_else(|| {
|
||||||
|
path.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.map(|s| s.to_owned())
|
||||||
|
}),
|
||||||
|
track_number: None,
|
||||||
|
filetype: mimetype,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum EntryInfo {
|
enum EntryInfo {
|
||||||
Dir(PathBuf),
|
Dir(PathBuf),
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
|
@ -136,7 +162,7 @@ impl Iterator for FileIterator {
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if err.kind() == std::io::ErrorKind::NotFound {
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||||||
Some(Err(ScannerError::NotFound(dir)))
|
Some(Err(ScannerError::FileNotFound))
|
||||||
} else {
|
} else {
|
||||||
Some(Err(ScannerError::from(err)))
|
Some(Err(ScannerError::from(err)))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue