mirror of
https://github.com/agersant/polaris
synced 2024-11-10 02:04:13 +00:00
Add few more fields to song information (#141)
* [meta] Add ignore paths to vscode settings * [feature] Add few more fields to song information Fields include lyricist, composer, genre, category and label.
This commit is contained in:
parent
4c25195deb
commit
f104355076
20 changed files with 512 additions and 470 deletions
10
.vscode/settings.json
vendored
Normal file
10
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"files.watcherExclude": {
|
||||||
|
"**/target/**": true,
|
||||||
|
"**/test-output/**": true
|
||||||
|
},
|
||||||
|
"files.exclude": {
|
||||||
|
"**/target": true,
|
||||||
|
"**/test-output": true
|
||||||
|
}
|
||||||
|
}
|
836
Cargo.lock
generated
836
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -30,7 +30,7 @@ lewton = "0.10.1"
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
metaflac = "0.2.3"
|
metaflac = "0.2.3"
|
||||||
mp3-duration = "0.1.9"
|
mp3-duration = "0.1.9"
|
||||||
mp4ameta = "0.7.1"
|
mp4ameta = "0.10.0"
|
||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
opus_headers = "0.1.2"
|
opus_headers = "0.1.2"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
|
@ -52,7 +52,7 @@ url = "2.1"
|
||||||
[dependencies.diesel]
|
[dependencies.diesel]
|
||||||
version = "1.4.5"
|
version = "1.4.5"
|
||||||
default_features = false
|
default_features = false
|
||||||
features = ["libsqlite3-sys", "r2d2", "sqlite"]
|
features = ["libsqlite3-sys", "r2d2", "sqlite", "64-column-tables"]
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
version = "0.23.12"
|
version = "0.23.12"
|
||||||
|
|
|
@ -1417,6 +1417,22 @@
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"example": 571
|
"example": 571
|
||||||
|
},
|
||||||
|
"lyricist": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Timo Tolkki"
|
||||||
|
},
|
||||||
|
"composer": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Timo Tolkki"
|
||||||
|
},
|
||||||
|
"genre": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Genre"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Noise Records"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1492,4 +1508,4 @@
|
||||||
"callbacks": {}
|
"callbacks": {}
|
||||||
},
|
},
|
||||||
"security": []
|
"security": []
|
||||||
}
|
}
|
||||||
|
|
20
migrations/2021-05-01-011426_add_lyricist/down.sql
Normal file
20
migrations/2021-05-01-011426_add_lyricist/down.sql
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
CREATE TEMPORARY TABLE songs_backup(id, path, parent, track_number, disc_number, title, artist, album_artist, year, album, artwork, duration);
|
||||||
|
INSERT INTO songs_backup SELECT id, path, parent, track_number, disc_number, title, artist, album_artist, year, album, artwork, duration FROM songs;
|
||||||
|
DROP TABLE songs;
|
||||||
|
CREATE TABLE songs (
|
||||||
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
|
path TEXT NOT NULL,
|
||||||
|
parent TEXT NOT NULL,
|
||||||
|
track_number INTEGER,
|
||||||
|
disc_number INTEGER,
|
||||||
|
title TEXT,
|
||||||
|
artist TEXT,
|
||||||
|
album_artist TEXT,
|
||||||
|
year INTEGER,
|
||||||
|
album TEXT,
|
||||||
|
artwork TEXT,
|
||||||
|
duration INTEGER,
|
||||||
|
UNIQUE(path) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
INSERT INTO songs SELECT * FROM songs_backup;
|
||||||
|
DROP TABLE songs_backup;
|
4
migrations/2021-05-01-011426_add_lyricist/up.sql
Normal file
4
migrations/2021-05-01-011426_add_lyricist/up.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE songs ADD COLUMN lyricist TEXT;
|
||||||
|
ALTER TABLE songs ADD COLUMN composer TEXT;
|
||||||
|
ALTER TABLE songs ADD COLUMN genre TEXT;
|
||||||
|
ALTER TABLE songs ADD COLUMN label TEXT;
|
|
@ -25,6 +25,10 @@ pub struct SongTags {
|
||||||
pub album: Option<String>,
|
pub album: Option<String>,
|
||||||
pub year: Option<i32>,
|
pub year: Option<i32>,
|
||||||
pub has_artwork: bool,
|
pub has_artwork: bool,
|
||||||
|
pub lyricist: Option<String>,
|
||||||
|
pub composer: Option<String>,
|
||||||
|
pub genre: Option<String>,
|
||||||
|
pub label: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<id3::Tag> for SongTags {
|
impl From<id3::Tag> for SongTags {
|
||||||
|
@ -42,6 +46,10 @@ impl From<id3::Tag> for SongTags {
|
||||||
.or_else(|| tag.date_released().map(|d| d.year))
|
.or_else(|| tag.date_released().map(|d| d.year))
|
||||||
.or_else(|| tag.date_recorded().map(|d| d.year));
|
.or_else(|| tag.date_recorded().map(|d| d.year));
|
||||||
let has_artwork = tag.pictures().count() > 0;
|
let has_artwork = tag.pictures().count() > 0;
|
||||||
|
let lyricist = tag.get_text("TEXT");
|
||||||
|
let composer = tag.get_text("TCOM");
|
||||||
|
let genre = tag.genre().map(|s| s.to_string());
|
||||||
|
let label = tag.get_text("TPUB");
|
||||||
|
|
||||||
SongTags {
|
SongTags {
|
||||||
artist,
|
artist,
|
||||||
|
@ -53,6 +61,10 @@ impl From<id3::Tag> for SongTags {
|
||||||
track_number,
|
track_number,
|
||||||
year,
|
year,
|
||||||
has_artwork,
|
has_artwork,
|
||||||
|
lyricist,
|
||||||
|
composer,
|
||||||
|
genre,
|
||||||
|
label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +92,22 @@ pub fn read(path: &Path) -> Option<SongTags> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait FrameContent {
|
||||||
|
/// Returns the value stored, if any, in the Frame.
|
||||||
|
/// Say "TCOM" returns composer field.
|
||||||
|
fn get_text(&self, key: &str) -> Option<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameContent for id3::Tag {
|
||||||
|
fn get_text(&self, key: &str) -> Option<String> {
|
||||||
|
let frame = self.get(key)?;
|
||||||
|
match frame.content() {
|
||||||
|
id3::Content::Text(value) => Some(value.to_string()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_mp3(path: &Path) -> Result<SongTags> {
|
fn read_mp3(path: &Path) -> Result<SongTags> {
|
||||||
let tag = id3::Tag::read_from_path(&path).or_else(|error| {
|
let tag = id3::Tag::read_from_path(&path).or_else(|error| {
|
||||||
if let Some(tag) = error.partial_tag {
|
if let Some(tag) = error.partial_tag {
|
||||||
|
@ -159,9 +187,14 @@ fn read_ape(path: &Path) -> Result<SongTags> {
|
||||||
let year = tag.item("Year").and_then(read_ape_i32);
|
let year = tag.item("Year").and_then(read_ape_i32);
|
||||||
let disc_number = tag.item("Disc").and_then(read_ape_x_of_y);
|
let disc_number = tag.item("Disc").and_then(read_ape_x_of_y);
|
||||||
let track_number = tag.item("Track").and_then(read_ape_x_of_y);
|
let track_number = tag.item("Track").and_then(read_ape_x_of_y);
|
||||||
|
let lyricist = tag.item("LYRICIST").and_then(read_ape_string);
|
||||||
|
let composer = tag.item("COMPOSER").and_then(read_ape_string);
|
||||||
|
let genre = tag.item("GENRE").and_then(read_ape_string);
|
||||||
|
let label = tag.item("PUBLISHER").and_then(read_ape_string);
|
||||||
Ok(SongTags {
|
Ok(SongTags {
|
||||||
artist,
|
//
|
||||||
album_artist,
|
artist, //
|
||||||
|
album_artist, //
|
||||||
album,
|
album,
|
||||||
title,
|
title,
|
||||||
duration: None,
|
duration: None,
|
||||||
|
@ -169,6 +202,10 @@ fn read_ape(path: &Path) -> Result<SongTags> {
|
||||||
track_number,
|
track_number,
|
||||||
year,
|
year,
|
||||||
has_artwork: false,
|
has_artwork: false,
|
||||||
|
lyricist,
|
||||||
|
composer,
|
||||||
|
genre,
|
||||||
|
label,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +223,10 @@ fn read_vorbis(path: &Path) -> Result<SongTags> {
|
||||||
track_number: None,
|
track_number: None,
|
||||||
year: None,
|
year: None,
|
||||||
has_artwork: false,
|
has_artwork: false,
|
||||||
|
lyricist: None,
|
||||||
|
composer: None,
|
||||||
|
genre: None,
|
||||||
|
label: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (key, value) in source.comment_hdr.comment_list {
|
for (key, value) in source.comment_hdr.comment_list {
|
||||||
|
@ -198,6 +239,10 @@ fn read_vorbis(path: &Path) -> Result<SongTags> {
|
||||||
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
|
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
|
||||||
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
|
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
|
||||||
"DATE" => tags.year = value.parse::<i32>().ok(),
|
"DATE" => tags.year = value.parse::<i32>().ok(),
|
||||||
|
"LYRICIST" => tags.lyricist = Some(value),
|
||||||
|
"COMPOSER" => tags.composer = Some(value),
|
||||||
|
"GENRE" => tags.genre = Some(value),
|
||||||
|
"PUBLISHER" => tags.label = Some(value),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,6 +264,10 @@ fn read_opus(path: &Path) -> Result<SongTags> {
|
||||||
track_number: None,
|
track_number: None,
|
||||||
year: None,
|
year: None,
|
||||||
has_artwork: false,
|
has_artwork: false,
|
||||||
|
lyricist: None,
|
||||||
|
composer: None,
|
||||||
|
genre: None,
|
||||||
|
label: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (key, value) in headers.comments.user_comments {
|
for (key, value) in headers.comments.user_comments {
|
||||||
|
@ -231,6 +280,10 @@ fn read_opus(path: &Path) -> Result<SongTags> {
|
||||||
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
|
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
|
||||||
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
|
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
|
||||||
"DATE" => tags.year = value.parse::<i32>().ok(),
|
"DATE" => tags.year = value.parse::<i32>().ok(),
|
||||||
|
"LYRICIST" => tags.lyricist = Some(value),
|
||||||
|
"COMPOSER" => tags.composer = Some(value),
|
||||||
|
"GENRE" => tags.genre = Some(value),
|
||||||
|
"PUBLISHER" => tags.label = Some(value),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,22 +320,31 @@ fn read_flac(path: &Path) -> Result<SongTags> {
|
||||||
track_number: vorbis.track(),
|
track_number: vorbis.track(),
|
||||||
year,
|
year,
|
||||||
has_artwork,
|
has_artwork,
|
||||||
|
lyricist: vorbis.get("LYRICIST").map(|v| v[0].clone()),
|
||||||
|
composer: vorbis.get("COMPOSER").map(|v| v[0].clone()),
|
||||||
|
genre: vorbis.get("GENRE").map(|v| v[0].clone()),
|
||||||
|
label: vorbis.get("PUBLISHER").map(|v| v[0].clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_mp4(path: &Path) -> Result<SongTags> {
|
fn read_mp4(path: &Path) -> Result<SongTags> {
|
||||||
let mut tag = mp4ameta::Tag::read_from_path(path)?;
|
let mut tag = mp4ameta::Tag::read_from_path(path)?;
|
||||||
|
let label_ident = mp4ameta::FreeformIdent::new("com.apple.iTunes", "Label");
|
||||||
|
|
||||||
Ok(SongTags {
|
Ok(SongTags {
|
||||||
artist: tag.take_artist(),
|
artist: tag.take_artist(),
|
||||||
album_artist: tag.take_album_artist(),
|
album_artist: tag.take_album_artist(),
|
||||||
album: tag.take_album(),
|
album: tag.take_album(),
|
||||||
title: tag.take_title(),
|
title: tag.take_title(),
|
||||||
duration: tag.duration().map(|v| v as u32),
|
duration: tag.duration().map(|v| v.as_secs() as u32),
|
||||||
disc_number: tag.disc_number().map(|d| d as u32),
|
disc_number: tag.disc_number().map(|d| d as u32),
|
||||||
track_number: tag.track_number().map(|d| d as u32),
|
track_number: tag.track_number().map(|d| d as u32),
|
||||||
year: tag.year().and_then(|v| v.parse::<i32>().ok()),
|
year: tag.year().and_then(|v| v.parse::<i32>().ok()),
|
||||||
has_artwork: tag.artwork().is_some(),
|
has_artwork: tag.artwork().is_some(),
|
||||||
|
lyricist: tag.take_lyricist(),
|
||||||
|
composer: tag.take_composer(),
|
||||||
|
genre: tag.take_genre(),
|
||||||
|
label: tag.take_string(&label_ident).next(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +360,10 @@ fn reads_file_metadata() {
|
||||||
duration: None,
|
duration: None,
|
||||||
year: Some(2016),
|
year: Some(2016),
|
||||||
has_artwork: false,
|
has_artwork: false,
|
||||||
|
lyricist: Some("TEST LYRICIST".into()),
|
||||||
|
composer: Some("TEST COMPOSER".into()),
|
||||||
|
genre: Some("TEST GENRE".into()),
|
||||||
|
label: Some("TEST LABEL".into()),
|
||||||
};
|
};
|
||||||
let flac_sample_tag = SongTags {
|
let flac_sample_tag = SongTags {
|
||||||
duration: Some(0),
|
duration: Some(0),
|
||||||
|
|
|
@ -27,6 +27,10 @@ pub struct Song {
|
||||||
pub album: Option<String>,
|
pub album: Option<String>,
|
||||||
pub artwork: Option<String>,
|
pub artwork: Option<String>,
|
||||||
pub duration: Option<i32>,
|
pub duration: Option<i32>,
|
||||||
|
pub lyricist: Option<String>,
|
||||||
|
pub composer: Option<String>,
|
||||||
|
pub genre: Option<String>,
|
||||||
|
pub label: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
|
|
|
@ -88,6 +88,10 @@ impl Collector {
|
||||||
album: tags.album,
|
album: tags.album,
|
||||||
year: tags.year,
|
year: tags.year,
|
||||||
artwork: artwork_path,
|
artwork: artwork_path,
|
||||||
|
lyricist: tags.lyricist,
|
||||||
|
composer: tags.composer,
|
||||||
|
genre: tags.genre,
|
||||||
|
label: tags.label,
|
||||||
})) {
|
})) {
|
||||||
error!("Error while sending song from collector: {}", e);
|
error!("Error while sending song from collector: {}", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ pub struct Song {
|
||||||
pub album: Option<String>,
|
pub album: Option<String>,
|
||||||
pub artwork: Option<String>,
|
pub artwork: Option<String>,
|
||||||
pub duration: Option<i32>,
|
pub duration: Option<i32>,
|
||||||
|
pub lyricist: Option<String>,
|
||||||
|
pub composer: Option<String>,
|
||||||
|
pub genre: Option<String>,
|
||||||
|
pub label: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Insertable)]
|
#[derive(Debug, Insertable)]
|
||||||
|
|
|
@ -164,7 +164,7 @@ impl Manager {
|
||||||
// Select songs. Not using Diesel because we need to LEFT JOIN using a custom column
|
// Select songs. Not using Diesel because we need to LEFT JOIN using a custom column
|
||||||
let query = diesel::sql_query(
|
let query = diesel::sql_query(
|
||||||
r#"
|
r#"
|
||||||
SELECT s.id, s.path, s.parent, s.track_number, s.disc_number, s.title, s.artist, s.album_artist, s.year, s.album, s.artwork, s.duration
|
SELECT s.id, s.path, s.parent, s.track_number, s.disc_number, s.title, s.artist, s.album_artist, s.year, s.album, s.artwork, s.duration, s.lyricist, s.composer, s.genre, s.label
|
||||||
FROM playlist_songs ps
|
FROM playlist_songs ps
|
||||||
LEFT JOIN songs s ON ps.path = s.path
|
LEFT JOIN songs s ON ps.path = s.path
|
||||||
WHERE ps.playlist = ?
|
WHERE ps.playlist = ?
|
||||||
|
|
|
@ -68,6 +68,10 @@ table! {
|
||||||
album -> Nullable<Text>,
|
album -> Nullable<Text>,
|
||||||
artwork -> Nullable<Text>,
|
artwork -> Nullable<Text>,
|
||||||
duration -> Nullable<Integer>,
|
duration -> Nullable<Integer>,
|
||||||
|
lyricist -> Nullable<Text>,
|
||||||
|
composer -> Nullable<Text>,
|
||||||
|
genre -> Nullable<Text>,
|
||||||
|
label -> Nullable<Text>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue