···23232424## Features
25252626+### Duplicate Detection
2727+2828+Before a Play is scrobbled to a client MS checks existing scrobbles from the Client's API to see if the Play has already been scrobbled.
2929+3030+For each Play, MS fetches (cached) scrobbles from the Client in a time range inclusive of the Play's listening timestamp and then scores existing scrobbles against the Play based on:
3131+3232+* Similarity of Title, Artists, and Album
3333+* Temporal closeness of the Play's timestamp to the existing scrobble's timestamp
3434+* Whether MS detected the Play as a repeat (applicable to Sources that report realtime Player data)
3535+3636+Detailed scoring breakdowns against each existing scrobble are logged at the `TRACE` level.
3737+3838+<details>
3939+4040+<summary>Detailed Explanation</summary>
4141+4242+A **candidate** (to be scrobbled) Play is first transformed using the configured [`compare.candidate` Hook](/configuration/transforms/#hook), if any exists.
4343+4444+Next, MS checks (up to) the last 100 scrobbles *in-memory* scrobbles that it has made. These are *not* from the Client but the actual scrobbles MS made while it has been running. The data from these scrobbles is much richer than what is usually parsed from the Client which makes it easier to detect duplicates from.
4545+4646+If no in-memory scrobble matches then MS starts comparing the candidate against historical scrobbles fetched from an inclusive time range of the candidate's timestamp.
4747+4848+<h3>Matching Title/Artist/Album</h3>
4949+5050+For these string-based values MS uses an [token-order-invariant](https://github.com/FoxxMD/multi-scrobbler/blob/0dfa4c7aad6df98e13aee6d395827665c2414adb/src/backend/utils/StringUtils.ts#L299) method that scores string similarity based on a (token count) weighted average of two [similarity](https://foxxmd.github.io/string-sameness/#md:strategies) algorithms, [Levenshtien Distance](https://en.wikipedia.org/wiki/Levenshtein_distance) and [Dice's Coefficient](https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient). The weighted average ensures that very long strings (Track titles) are scored with a confidence proportional to their length.
5151+5252+<h3>Matching Timestamp</h3>
5353+5454+Timestamps are scored on how temporally close they are. There are four possible scores with decreasing value:
5555+5656+* **Exact** - Timestamps are within 1 second of each other
5757+* **Close** - Timestamps are within `threshold` seconds of each other
5858+ * This is determined by the smallest update interval of Source
5959+ * Some Sources (subsonic) only update every 60 seconds so this is the smallest "close" value possible. Most are 10 seconds. Signified by `(Needed <10s)` in the breakdown example below.
6060+* **Fuzzy** - One timestamp is within `threshold` seconds of the *end* of the other timestamp
6161+ * Sources can set the scrobble timestamp at different times. Some do it when the track is started listening to, some when it ends, some when the *player* stops.
6262+ * Where possible, MS knows and keeps track of when this timestamp *should* be, for each Source. If it's not possible then Fuzzy may be allowed.
6363+* **None** - There is no correlation between timestamps
6464+6565+<h3>Scoring and Breakdows</h3>
6666+6767+A candidate Play must score >= 1 to be detected as a duplicate of an existing scrobble.
6868+6969+Each score and a breakdown of the scores for its individual components can be see at the `TRACE` logging level or in the [debug data](/help#copy-play-debug-data) for a scrobble. An example:
7070+7171+```
7272+* Artist: 0.06 * 0.3 = 0.02
7373+* Title: 0.04 * 0.4 = 0.02
7474+* Time: (Exact) 1 * 0.5 = 0.50
7575+ * Existing: 19:13:33-04:00 - Candidate: 19:13:33-04:00
7676+ * Temporal Sameness: Exact
7777+ * Play Diff: 0s (Needed <10s)
7878+ * Range Comparison N/A
7979+Score 0.54 => No Match
8080+```
8181+8282+In each component equation, the first number is the similarity (or temporal closeness).
8383+8484+* 0 = no correlation
8585+* 1 = exactly the same
8686+8787+The second number is the *weight* of that component in the final score.
8888+8989+</details>
9090+2691### Dead Scrobbles
27922893If multi-scrobbler is unable to submit a scrobble to a Client then it places the scrobble into a queue which is retried every 5 minutes for a number of times before it gives up.