import codecs import csv import logging from datetime import datetime, timedelta from zoneinfo import ZoneInfo import requests from django.contrib.auth import get_user_model from music.models import Track from scrobbles.constants import AsTsvColumn from scrobbles.models import Scrobble logger = logging.getLogger(__name__) def import_audioscrobbler_tsv_file(file_path, user_id): """Takes a path to a file of TSV data and imports it as past scrobbles""" new_scrobbles = [] user = get_user_model().objects.get(id=user_id) is_os_file = "https://" not in file_path if not is_os_file: r = requests.get(file_path) tsv_data = codecs.iterdecode(r.iter_lines(), "utf-8") else: tsv_data = open(file_path) source = "Audioscrobbler File" rows = csv.reader(tsv_data, delimiter="\t") rockbox_info = "" for row_num, row in enumerate(rows): if row_num in [0, 1, 2]: if "Rockbox" in row[0]: source = "Rockbox" rockbox_info += row[0] + "\n" continue if len(row) > 8: logger.info( "[skip] too many columns in row", extra={"row": row}, ) continue album_name = row[AsTsvColumn["ALBUM_NAME"].value] track = Track.find_or_create( title=row[AsTsvColumn["TRACK_NAME"].value], artist_name=row[AsTsvColumn["ARTIST_NAME"].value], album_name=album_name, run_time_seconds=int(row[AsTsvColumn["RUN_TIME_SECONDS"].value]), enrich=True, ) # TODO Set all this up as constants if row[AsTsvColumn["COMPLETE"].value] == "S": logger.info( "[skip] track not finished", extra={ "album_name": row[AsTsvColumn["ALBUM_NAME"].value], "artist_name": row[AsTsvColumn["ARTIST_NAME"].value], }, ) continue timestamp = datetime.fromtimestamp( int(row[AsTsvColumn["TIMESTAMP"].value]) ).astimezone(ZoneInfo("UTC")) timestamp = user.profile.get_timestamp_with_tz(timestamp) stop_timestamp = timestamp + timedelta(seconds=track.run_time_seconds) new_scrobble = Scrobble( user=user, timestamp=timestamp, stop_timestamp=stop_timestamp, source=source, log={"rockbox_info": rockbox_info, "album_name": album_name}, playback_position_seconds=track.run_time_seconds, track=track, played_to_completion=True, in_progress=False, media_type=Scrobble.MediaType.TRACK, timezone=timestamp.tzinfo.name, visibility="private", ) existing = Scrobble.objects.filter( timestamp=timestamp, track=track, user=user ).first() if existing: logger.debug(f"Skipping existing scrobble {new_scrobble}") continue logger.debug(f"Queued scrobble {new_scrobble} for creation") new_scrobbles.append(new_scrobble) if is_os_file: tsv_data.close() created = Scrobble.objects.bulk_create(new_scrobbles) logger.info( f"Created {len(created)} scrobbles", extra={"created_scrobbles": created}, ) return created