Skip to content

Instantly share code, notes, and snippets.

@djanatyn
Created October 18, 2023 00:34
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/8c9aa04411bca67c9d48db837b3a20ba to your computer and use it in GitHub Desktop.
Save djanatyn/8c9aa04411bca67c9d48db837b3a20ba to your computer and use it in GitHub Desktop.
scrobble fix story

when i'm biking i listen to music on my ipod (hacked to run rockbox), i like using last.fm so i record a scrobble.log file

i was going to upload the scrobbles but i noticed some of the dates were from before 2001

$ cat /mnt/.scrobbler.log
...
JPEGMAFIA EP2!  FEED HER! 6 176 L 1616925238     
Yuzo Koshiro  Actraiser Opening   131 S 946994845
...
❯ date -d '@1616925238'
Sun Mar 28 05:53:58 AM EDT 2021
❯ date -d '@946994845'
Tue Jan  4 09:07:25 AM EST 2000

oh yeah oops, i guess my date got reset on the ipod. i checked out rockbox/apps/plugins/lastfm_scrobbler.c and saw that the AUDIOSCROBBLER/1.1 format they output is well-documented

i wanted to play with parser combinators so i wrote some rust code with nom to parse the timestamps

running 1 test
[src/main.rs:71] &scrobble = "JPEGMAFIA\tBlack Ben Carson\tDrake Era\t1\t237\tS\t1605758450\t"
[src/main.rs:73] &parsed = Scrobble {
    artist: "JPEGMAFIA",
    album: "Black Ben Carson",
    track: "Drake Era",
    track_position: 1,
    song_duration: 237,
    rating: Skipped,
    timestamp: 1605758450,
}
test parse_line ... ok

i picked an arbitrary date in the past and used it as a cutoff - if a scrobble was logged before 2005, it probably needs updated, since i got my ipod in 2020

/// Anything older than this needs an offset applied.
const SCROBBLE_CUTOFF: &str = "2005-01-01T00:00:00Z";

after writing some code to identify suspicious scrobbles, i played around a little bit until i found the right offset, a little over 22 years in the future

/// Number of days to add to the suspicious scrobbles.
const SCROBBLE_DAYS_OFFSET: u64 = (365 * 22) + 215;  

i use a bike computer and strava so i had the exact time of my bike ride available to check

i wrote a little Scrobble::fix function

impl Scrobble {
    /// Adjust the timestamps for suspicious scrobbles.
    fn fix(self, cutoff: DateTime<FixedOffset>) -> Result<Self, String> {
        if self.timestamp > cutoff {
            return Ok(self);
        }
        let updated_timestamp = self
            .timestamp
            .checked_add_days(Days::new(SCROBBLE_DAYS_OFFSET))
            .ok_or("failed to apply offset")?;
        Ok(Self {
            timestamp: updated_timestamp,
            ..self
        })
    }
}

and then i had my scrobbler.log file all fixed up with reasonable timestamps :)

/// Output scrobbler.log with fixed timestamps.
fn main() -> std::io::Result<()> {
    let cutoff =
        DateTime::parse_from_rfc3339(SCROBBLE_CUTOFF).expect("failed to parse cutoff date");
    let log = std::fs::read_to_string("scrobbler.log")?;
    let scrobbles: String = log
        .lines()
        .skip(3)
        .map(|input| {
            Scrobble::new(input)
                .and_then(|scrobble| scrobble.fix(cutoff).map(|fixed| fixed.to_string()))
        })
        .intersperse(Ok("\n".to_string()))
        .collect::<Result<String, _>>()
        .unwrap();
    Ok(println!("{HEADER}{scrobbles}"))
}

now i gotta write some code to upload this old scrobbler format to last.fm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment