Skip to content

Instantly share code, notes, and snippets.

@djanatyn
Last active October 17, 2023 14:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djanatyn/b3cab1d0dfb597fd1461566cd7f1db23 to your computer and use it in GitHub Desktop.
Save djanatyn/b3cab1d0dfb597fd1461566cd7f1db23 to your computer and use it in GitHub Desktop.
fixing scrobbles
//! https://github.com/Rockbox/rockbox/blob/3c89adbdbdd036baf313786b0694632c8e7e2bb3/apps/plugins/lastfm_scrobbler.c#L29
use chrono::{DateTime, TimeZone, Utc};
use nom::{
bytes::complete::take_until, character::complete::char, multi::count, sequence::terminated,
IResult,
};
fn main() -> std::io::Result<()> {
let log = std::fs::read_to_string("scrobbler.log")?;
let scrobbles: Vec<Scrobble> = log
.lines()
.skip(3)
.map(|i| {
let (_, scrobble) = parse_scrobble(dbg!(i)).unwrap();
scrobble
})
.collect();
Ok(println!("{scrobbles:#?}"))
}
#[derive(Debug)]
enum Rating {
Listened,
Skipped,
}
#[derive(Debug)]
struct Scrobble {
artist: String,
album: String,
track: String,
track_position: Option<u32>, // TODO: Option<u32>
song_duration: u32, // seconds
rating: Rating,
timestamp: DateTime<Utc>,
}
fn parse_scrobble_token(input: &str) -> IResult<&str, &str> {
terminated(take_until("\t"), char('\t'))(input)
}
fn parse_scrobble(input: &str) -> IResult<&str, Scrobble> {
let (rest, tokens) = count(parse_scrobble_token, 7)(input)?;
Ok((
rest,
Scrobble {
artist: tokens[0].to_string(),
album: tokens[1].to_string(),
track: tokens[2].to_string(),
track_position: match tokens[3] {
"" => None,
pos => Some(pos.parse::<u32>().expect("failed to parse track position")),
},
song_duration: tokens[4]
.parse::<u32>()
.expect("failed to parse song duration"),
rating: match tokens[5] {
"S" => Rating::Skipped,
"L" => Rating::Listened,
_ => panic!("failed to parse rating"),
},
timestamp: chrono::Utc.timestamp(
tokens[6].parse::<i64>().expect("failed to parse timestamp"),
0,
),
},
))
}
const HEADER: &str = r#"
#AUDIOSCROBBLER/1.1
#TZ/UNKNOWN
#CLIENT/Rockbox ipodvideo $Revision$
"#;
#[test]
fn parse_line() -> std::io::Result<()> {
let log = std::fs::read_to_string("scrobbler.log")?;
let scrobbles: Vec<Scrobble> = log
.lines()
.skip(3)
.map(|i| {
let (_, scrobble) = parse_scrobble(dbg!(i)).unwrap();
scrobble
})
.collect();
dbg!(scrobbles);
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment