tsv.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import codecs
  2. import csv
  3. import logging
  4. from datetime import datetime
  5. import pytz
  6. import requests
  7. from music.utils import (
  8. get_or_create_album,
  9. get_or_create_artist,
  10. get_or_create_track,
  11. )
  12. from scrobbles.constants import AsTsvColumn
  13. from scrobbles.models import Scrobble
  14. from music.constants import MOPIDY_POST_KEYS
  15. from scrobbles.utils import timestamp_user_tz_to_utc
  16. logger = logging.getLogger(__name__)
  17. def process_audioscrobbler_tsv_file(file_path, user_id, user_tz=None):
  18. """Takes a path to a file of TSV data and imports it as past scrobbles"""
  19. new_scrobbles = []
  20. if not user_tz:
  21. user_tz = pytz.utc
  22. is_os_file = "https://" not in file_path
  23. if not is_os_file:
  24. r = requests.get(file_path)
  25. tsv_data = codecs.iterdecode(r.iter_lines(), "utf-8")
  26. else:
  27. tsv_data = open(file_path)
  28. source = "Audioscrobbler File"
  29. rows = csv.reader(tsv_data, delimiter="\t")
  30. rockbox_info = ""
  31. for row_num, row in enumerate(rows):
  32. if row_num in [0, 1, 2]:
  33. if "Rockbox" in row[0]:
  34. source = "Rockbox"
  35. rockbox_info += row[0] + "\n"
  36. continue
  37. if len(row) > 8:
  38. logger.info(
  39. "[skip] too many columns in row",
  40. extra={"row": row},
  41. )
  42. continue
  43. track = get_or_create_track(
  44. {
  45. "title": row[AsTsvColumn["TRACK_NAME"].value],
  46. "mbid": row[AsTsvColumn["MB_ID"].value],
  47. "artist_name": row[AsTsvColumn["ARTIST_NAME"].value],
  48. "album_name": row[AsTsvColumn["ALBUM_NAME"].value],
  49. "run_time_seconds": int(
  50. row[AsTsvColumn["RUN_TIME_SECONDS"].value]
  51. ),
  52. },
  53. {
  54. "TRACK_MB_ID": "mbid",
  55. "TRACK_TITLE": "track_title",
  56. "ALBUM_NAME": "album_name",
  57. "ARTIST_NAME": "artist_name",
  58. "RUN_TIME": "run_time_seconds",
  59. },
  60. )
  61. if not track:
  62. logger.info(f"Skipping track {track} because not found")
  63. continue
  64. # TODO Set all this up as constants
  65. if row[AsTsvColumn["COMPLETE"].value] == "S":
  66. logger.info(
  67. "[skip] track not finished",
  68. extra={
  69. "album_name": row[AsTsvColumn["ALBUM_NAME"].value],
  70. "artist_name": row[AsTsvColumn["ARTIST_NAME"].value],
  71. }
  72. )
  73. continue
  74. timestamp = timestamp_user_tz_to_utc(
  75. int(row[AsTsvColumn["TIMESTAMP"].value]), user_tz
  76. )
  77. new_scrobble = Scrobble(
  78. user_id=user_id,
  79. timestamp=timestamp,
  80. source=source,
  81. log={"rockbox_info": rockbox_info},
  82. track=track,
  83. played_to_completion=True,
  84. in_progress=False,
  85. media_type=Scrobble.MediaType.TRACK,
  86. )
  87. existing = Scrobble.objects.filter(
  88. timestamp=timestamp, track=track
  89. ).first()
  90. if existing:
  91. logger.debug(f"Skipping existing scrobble {new_scrobble}")
  92. continue
  93. logger.debug(f"Queued scrobble {new_scrobble} for creation")
  94. new_scrobbles.append(new_scrobble)
  95. if is_os_file:
  96. tsv_data.close()
  97. created = Scrobble.objects.bulk_create(new_scrobbles)
  98. logger.info(
  99. f"Created {len(created)} scrobbles",
  100. extra={"created_scrobbles": created},
  101. )
  102. return created