Module serato_data.history
Parser for Serato history and session files.
Classes
class HistoryFile (filename: str)-
Expand source code
class HistoryFile(Parser): """\ Represents the overall Serato history. Entries in this file correspond to individual sessions. """ DATACLASS = Session FIELDS = { b'\x00\x00\x00\x01': ('session_id', Parser.parse_field_int, None), b'\x00\x00\x00)': ('date', Parser.parse_field_str, None), b'\x00\x00\x009': ('software', Parser.parse_field_str, lambda s: f'Serato {s}' if s else None), b'\x00\x00\x00:': ('software_build', Parser.parse_field_int, None), b'\x00\x00\x00?': ('hardware', Parser.parse_field_str, None), b'\x00\x00\x00*': ('collapsed', Parser.parse_field_bool, None), b'\x00\x00\x00+': ('start', Parser.parse_field_arrow, None), b'\x00\x00\x00,': ('end', Parser.parse_field_arrow, None), b'\x00\x00\x00-': False, b'\x00\x00\x00.': False, b'\x00\x00\x00/': False, b'\x00\x00\x000': False, b'\x00\x00\x001': False, b'\x00\x00\x002': False, b'\x00\x00\x003': False, b'\x00\x00\x004': False, b'\x00\x00\x005': False, b'\x00\x00\x006': False, b'\x00\x00\x007': False, b'\x00\x00\x008': False, b'\x00\x00\x00;': False, b'\x00\x00\x00<': False, b'\x00\x00\x00=': False, b'\x00\x00\x00>': False, b'\x00\x00\x00@': False, b'\x00\x00\x00A': False, b'\x00\x00\x00B': False, b'\x00\x00\x00C': False, b'\x00\x00\x00D': False, b'\x00\x00\x00E': False, b'\x00\x00\x00F': False, b'\x00\x00\x00G': False, b'\x00\x00\x00H': False, } SKIP_CTYPES = [b'ocol'] MAIN_CTYPE = b'oses' def make_dataclass_args(self, *args, **kwargs): sess_filename = os.path.join(os.path.dirname(self.filename), 'Sessions', f"{kwargs['session_id']}.session") return ([sess_filename] + list(args)), kwargs def loaded(self): self.data = list(sorted(self.data, key=lambda s: s.start))Represents the overall Serato history. Entries in this file correspond to individual sessions.
Create a new parser for the given filename
Ancestors
Inherited members
class Session (filename: dataclasses.InitVar[str],
session_id: int,
date: str,
software: str | None = None,
software_build: int | None = None,
hardware: str | None = None,
collapsed: bool = True,
start: arrow.arrow.Arrow | None = None,
end: arrow.arrow.Arrow | None = None)-
Expand source code
@dataclass class Session: """\ Represents a single Serato session (period during which Serato was open) and provides access to the songs played within that session. Session entries (songs) are the data parsed out of the session file, iterate through the instance to retrieve them. """ filename: InitVar[str] session_id: int """ID of this session""" date: str """Date on which this session was started""" software: t.Optional[str] = None """Software used for this session""" software_build: t.Optional[int] = None """Build number of software used for this session""" hardware: t.Optional[str] = None """Primary hardware used for this session""" collapsed: bool = True """\ Use case is not clear; typically the most recent session will be False and all other sessions are True """ start: t.Optional[arrow.arrow.Arrow] = None """Time at which this session started""" end: t.Optional[arrow.arrow.Arrow] = None """Time at which this session ended""" def __post_init__(self, filename): self._session_filename = filename @property def _session_file(self): if not hasattr(self, '_session_file_obj'): self._session_file_obj = SessionFile(self._session_filename) return self._session_file_obj def __iter__(self): return iter(self._session_file)Represents a single Serato session (period during which Serato was open) and provides access to the songs played within that session.
Session entries (songs) are the data parsed out of the session file, iterate through the instance to retrieve them.
Instance variables
var collapsed : bool-
Use case is not clear; typically the most recent session will be False and all other sessions are True
var date : str-
Date on which this session was started
var end : arrow.arrow.Arrow | None-
Time at which this session ended
var filename : dataclasses.InitVar[str]-
The type of the None singleton.
var hardware : str | None-
Primary hardware used for this session
var session_id : int-
ID of this session
var software : str | None-
Software used for this session
var software_build : int | None-
Build number of software used for this session
var start : arrow.arrow.Arrow | None-
Time at which this session started
class SessionEntry (entry_id: int,
full_path: str | None = None,
location: str | None = None,
filename: str | None = None,
title: str | None = None,
artist: str | None = None,
album: str | None = None,
genre: str | None = None,
length: int | None = None,
size: int | None = None,
bitrate: int | None = None,
frequency: int | None = None,
bpm: int | None = None,
comments: str | None = None,
lang: str | None = None,
grouping: str | None = None,
remixer: str | None = None,
label: str | None = None,
composer: str | None = None,
year: str | None = None,
start: arrow.arrow.Arrow | None = None,
end: arrow.arrow.Arrow | None = None,
deck: int | None = None,
preview: bool = False,
playtime: int | None = None,
session_id: int | None = None,
played: bool = False,
raw_key: str | None = None,
key: camelot_key.Key | None = None,
added: arrow.arrow.Arrow | None = None,
updated_at: arrow.arrow.Arrow | None = None,
hardware: str | None = None,
comment_name: str | None = None,
rejected: bool = False,
beatport_id: int | None = None,
beatport_url: str | None = None)-
Expand source code
@dataclass class SessionEntry: """Represents an entry within a Serato session""" entry_id: int """ID of this entry""" full_path: t.Optional[str] = None """Path to the filename that was loaded. May be in other formats if the file was loaded from somewhere other than disk (like a streaming service)""" location: t.Optional[str] = None filename: t.Optional[str] = None title: t.Optional[str] = None """Song title""" artist: t.Optional[str] = None """Song artist""" album: t.Optional[str] = None """Song album""" genre: t.Optional[str] = None """Song genre""" length: t.Optional[int] = None """\ Length of the song, in seconds .. warning:: Unsure on units """ size: t.Optional[int] = None """Size of the loaded file""" bitrate: t.Optional[int] = None """Audio file bitrate""" frequency: t.Optional[int] = None """Audio file sample frequency""" bpm: t.Optional[int] = None """Song BPM""" comments: t.Optional[str] = None lang: t.Optional[str] = None grouping: t.Optional[str] = None remixer: t.Optional[str] = None """Song remixer""" label: t.Optional[str] = None """Song label""" composer: t.Optional[str] = None """Song composer""" year: t.Optional[str] = None """Release year of the song""" start: t.Optional[arrow.arrow.Arrow] = None """Time at which the song was loaded into the deck""" end: t.Optional[arrow.arrow.Arrow] = None """Time at which the song was unloaded from the deck""" deck: t.Optional[int] = None """Deck into which the song was loaded""" preview: bool = False """\ True if the song was loaded into the deck and previewed via cueing, but not actually played audibly .. warning:: Unsure on this """ playtime: t.Optional[int] = None """\ Duration of the time period the song was played, in seconds .. warning:: Unsure on units .. warning:: May be entire duration song was loaded (end - start) """ session_id: t.Optional[int] = None """ID of the session to which this entry belongs""" played: bool = False """\ Whether the song was played audibly or not .. warning:: Could use verification """ raw_key: t.Optional[str] = None """Key of the song, as present in the data file""" key: t.Optional[Key] = None """Parsed key of the song, if one was present and can be parsed""" added: t.Optional[arrow.arrow.Arrow] = None updated_at: t.Optional[arrow.arrow.Arrow] = None hardware: t.Optional[str] = None """Hardware on which the deck was loaded""" comment_name: t.Optional[str] = None rejected: bool = False """\ True if the song was loaded but not played audibly .. warning:: Unsure on this, may be set to True if the song was played even if it was not audible """ # Generated later; included here for defaults beatport_id: t.Optional[int] = None """ID of the track on Beatport, if loaded via streaming""" beatport_url: t.Optional[str] = None """URL to the track on Beatport, if loaded via streaming""" def __post_init__(self, *args, **kwargs): match = re.match(r'^_(\d+)\.beatport', self.full_path or '') if match: self.beatport_id = int(match.group(1)) self.beatport_url = f'https://www.beatport.com/track/unknown/{self.beatport_id}' if self.raw_key: try: self.key = parse(self.raw_key) except: # TODO: maybe log key parsing failure pass @property def url(self) -> t.Optional[str]: """\ URL to the song if loaded from a streaming service. """ return self.beatport_url or NoneRepresents an entry within a Serato session
Instance variables
var added : arrow.arrow.Arrow | None-
The type of the None singleton.
var album : str | None-
Song album
var artist : str | None-
Song artist
var beatport_id : int | None-
ID of the track on Beatport, if loaded via streaming
var beatport_url : str | None-
URL to the track on Beatport, if loaded via streaming
var bitrate : int | None-
Audio file bitrate
var bpm : int | None-
Song BPM
var comment_name : str | None-
The type of the None singleton.
var comments : str | None-
The type of the None singleton.
var composer : str | None-
Song composer
var deck : int | None-
Deck into which the song was loaded
var end : arrow.arrow.Arrow | None-
Time at which the song was unloaded from the deck
var entry_id : int-
ID of this entry
var filename : str | None-
The type of the None singleton.
var frequency : int | None-
Audio file sample frequency
var full_path : str | None-
Path to the filename that was loaded. May be in other formats if the file was loaded from somewhere other than disk (like a streaming service)
var genre : str | None-
Song genre
var grouping : str | None-
The type of the None singleton.
var hardware : str | None-
Hardware on which the deck was loaded
var key : camelot_key.Key | None-
Parsed key of the song, if one was present and can be parsed
var label : str | None-
Song label
var lang : str | None-
The type of the None singleton.
var length : int | None-
Length of the song, in seconds
Warning: Unsure on units
var location : str | None-
The type of the None singleton.
var played : bool-
Whether the song was played audibly or not
Warning: Could use verification
var playtime : int | None-
Duration of the time period the song was played, in seconds
Warning: Unsure on units
Warning: May be entire duration song was loaded (end - start)
var preview : bool-
True if the song was loaded into the deck and previewed via cueing, but not actually played audibly
Warning: Unsure on this
var raw_key : str | None-
Key of the song, as present in the data file
var rejected : bool-
True if the song was loaded but not played audibly
Warning: Unsure on this, may be set to True if the song was played even if it was not audible
var remixer : str | None-
Song remixer
var session_id : int | None-
ID of the session to which this entry belongs
var size : int | None-
Size of the loaded file
var start : arrow.arrow.Arrow | None-
Time at which the song was loaded into the deck
var title : str | None-
Song title
var updated_at : arrow.arrow.Arrow | None-
The type of the None singleton.
prop url : str | None-
Expand source code
@property def url(self) -> t.Optional[str]: """\ URL to the song if loaded from a streaming service. """ return self.beatport_url or NoneURL to the song if loaded from a streaming service.
var year : str | None-
Release year of the song
class SessionFile (filename: str)-
Expand source code
class SessionFile(Parser): """\ Parses session entries out of a session data file. """ DATACLASS = SessionEntry FIELDS = { b'\x00\x00\x00\x01': ('entry_id', Parser.parse_field_int, None), b'\x00\x00\x00\x02': ('full_path', Parser.parse_field_str, None), b'\x00\x00\x00\x03': ('location', Parser.parse_field_str, None), b'\x00\x00\x00\x04': ('filename', Parser.parse_field_str, None), b'\x00\x00\x00\x06': ('title', Parser.parse_field_str, None), b'\x00\x00\x00\x07': ('artist', Parser.parse_field_str, None), b'\x00\x00\x00\x08': ('album', Parser.parse_field_str, None), b'\x00\x00\x00\x09': ('genre', Parser.parse_field_str, None), b'\x00\x00\x00\x0a': ('length', Parser.parse_field_str, None), b'\x00\x00\x00\x0b': ('size', Parser.parse_field_str, None), b'\x00\x00\x00\x0d': ('bitrate', Parser.parse_field_str, None), b'\x00\x00\x00\x0e': ('frequency', Parser.parse_field_str, None), b'\x00\x00\x00\x0f': ('bpm', Parser.parse_field_int, None), b'\x00\x00\x00\x11': ('comments', Parser.parse_field_str, None), b'\x00\x00\x00\x12': ('lang', Parser.parse_field_str, None), b'\x00\x00\x00\x13': ('grouping', Parser.parse_field_str, None), b'\x00\x00\x00\x14': ('remixer', Parser.parse_field_str, None), b'\x00\x00\x00\x15': ('label', Parser.parse_field_str, None), b'\x00\x00\x00\x16': ('composer', Parser.parse_field_str, None), b'\x00\x00\x00\x17': ('year', Parser.parse_field_str, None), b'\x00\x00\x00\x1c': ('start', Parser.parse_field_arrow, None), b'\x00\x00\x00\x1d': ('end', Parser.parse_field_arrow, None), b'\x00\x00\x00\x1f': ('deck', Parser.parse_field_int, None), b'\x00\x00\x00\x27': ('preview', Parser.parse_field_bool, None), b'\x00\x00\x00\x2d': ('playtime', Parser.parse_field_int, None), b'\x00\x00\x00\x2f': ('session_id', Parser.parse_field_int, None), b'\x00\x00\x00\x32': ('played', Parser.parse_field_bool, None), b'\x00\x00\x00\x33': ('key', Parser.parse_field_str, None), b'\x00\x00\x00\x34': ('added', Parser.parse_field_bool, None), b'\x00\x00\x00\x35': ('updated_at', Parser.parse_field_arrow, None), b'\x00\x00\x00\x3f': ('hardware', Parser.parse_field_str, None), b'\x00\x00\x00\x40': ('comment_name', Parser.parse_field_str, None), b'\x00\x00\x00\x46': ('rejected', Parser.parse_field_bool, None), b'\x00\x00\x000': False, b'\x00\x00\x00D': False, b'\x00\x00\x00E': False, b'\x00\x00\x00H': False, b'\x00\x00\x00N': False, } MAIN_CTYPE = b'oent' def make_dataclass_args(self, *args, **kwargs): # Convert key to raw_key; then key is parsed back out if possible kwargs['raw_key'] = kwargs.pop('key', None) return args, kwargs def loaded(self): self.data = list(sorted({e.entry_id: e for e in self.data}.values(), key=lambda e: e.start))Parses session entries out of a session data file.
Create a new parser for the given filename
Ancestors
Inherited members