[scrobbles] Blacken files and add local dev static
All checks were successful
build & deploy / test (push) Successful in 1m47s
build & deploy / deploy (push) Has been skipped

This commit is contained in:
2026-03-08 16:50:43 -04:00
parent 8309a4e3c3
commit 8357ce8901
6 changed files with 182 additions and 289 deletions

View File

@ -139,7 +139,6 @@ def build_book_map(rows) -> dict:
book_id_map = {} book_id_map = {}
for book_row in rows: for book_row in rows:
if book_row[KoReaderBookColumn.TITLE.value] in BOOKS_TITLES_TO_IGNORE: if book_row[KoReaderBookColumn.TITLE.value] in BOOKS_TITLES_TO_IGNORE:
logger.info( logger.info(
"[build_book_map] Ignoring book title that is likely garbage", "[build_book_map] Ignoring book title that is likely garbage",
@ -147,9 +146,7 @@ def build_book_map(rows) -> dict:
) )
continue continue
book = Book.objects.filter( book = Book.objects.filter(
koreader_data_by_hash__icontains=book_row[ koreader_data_by_hash__icontains=book_row[KoReaderBookColumn.MD5.value]
KoReaderBookColumn.MD5.value
]
).first() ).first()
if not book: if not book:
@ -203,15 +200,11 @@ def build_page_data(page_rows: list, book_map: dict, user_tz=None) -> dict:
"end_ts": start_ts + duration, "end_ts": start_ts + duration,
} }
if book_ids_not_found: if book_ids_not_found:
logger.info( logger.info(f"Found pages for books not in file: {set(book_ids_not_found)}")
f"Found pages for books not in file: {set(book_ids_not_found)}"
)
return book_map return book_map
def build_scrobbles_from_book_map( def build_scrobbles_from_book_map(book_map: dict, user: "User") -> list["Scrobble"]:
book_map: dict, user: "User"
) -> list["Scrobble"]:
Scrobble = apps.get_model("scrobbles", "Scrobble") Scrobble = apps.get_model("scrobbles", "Scrobble")
scrobbles_to_create = [] scrobbles_to_create = []
@ -241,9 +234,9 @@ def build_scrobbles_from_book_map(
seconds_from_last_page = 0 seconds_from_last_page = 0
if prev_page_stats: if prev_page_stats:
seconds_from_last_page = stats.get( seconds_from_last_page = stats.get("end_ts") - prev_page_stats.get(
"end_ts" "start_ts"
) - prev_page_stats.get("start_ts") )
playback_position_seconds = playback_position_seconds + stats.get( playback_position_seconds = playback_position_seconds + stats.get(
"duration" "duration"
@ -252,9 +245,7 @@ def build_scrobbles_from_book_map(
end_of_reading = pages_processed == total_pages_read end_of_reading = pages_processed == total_pages_read
big_jump_to_this_page = (cur_page_number - last_page_number) > 10 big_jump_to_this_page = (cur_page_number - last_page_number) > 10
is_session_gap = seconds_from_last_page > SESSION_GAP_SECONDS is_session_gap = seconds_from_last_page > SESSION_GAP_SECONDS
if ( if (is_session_gap and not big_jump_to_this_page) or end_of_reading:
is_session_gap and not big_jump_to_this_page
) or end_of_reading:
should_create_scrobble = True should_create_scrobble = True
if should_create_scrobble: if should_create_scrobble:
@ -378,9 +369,7 @@ def process_koreader_sqlite_file(file_path, user_id) -> list:
return new_scrobbles return new_scrobbles
book_map = build_page_data( book_map = build_page_data(
cur.execute( cur.execute("SELECT * from page_stat_data ORDER BY id_book, start_time"),
"SELECT * from page_stat_data ORDER BY id_book, start_time"
),
book_map, book_map,
tz, tz,
) )

View File

@ -58,9 +58,7 @@ logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
BNULL = {"blank": True, "null": True} BNULL = {"blank": True, "null": True}
POINTS_FOR_MOVEMENT_HISTORY = int( POINTS_FOR_MOVEMENT_HISTORY = int(getattr(settings, "POINTS_FOR_MOVEMENT_HISTORY", 3))
getattr(settings, "POINTS_FOR_MOVEMENT_HISTORY", 3)
)
class BaseFileImportMixin(TimeStampedModel): class BaseFileImportMixin(TimeStampedModel):
@ -96,7 +94,6 @@ class BaseFileImportMixin(TimeStampedModel):
from scrobbles.models import Scrobble from scrobbles.models import Scrobble
if not self.process_log: if not self.process_log:
logger.warning("No lines in process log found to undo") logger.warning("No lines in process log found to undo")
return return
@ -104,9 +101,7 @@ class BaseFileImportMixin(TimeStampedModel):
scrobble_id = line.split("\t")[0] scrobble_id = line.split("\t")[0]
scrobble = Scrobble.objects.filter(id=scrobble_id).first() scrobble = Scrobble.objects.filter(id=scrobble_id).first()
if not scrobble: if not scrobble:
logger.warning( logger.warning(f"Could not find scrobble {scrobble_id} to undo")
f"Could not find scrobble {scrobble_id} to undo"
)
continue continue
logger.info(f"Removing scrobble {scrobble_id}") logger.info(f"Removing scrobble {scrobble_id}")
if not dryrun: if not dryrun:
@ -149,9 +144,7 @@ class BaseFileImportMixin(TimeStampedModel):
return return
for count, scrobble in enumerate(scrobbles): for count, scrobble in enumerate(scrobbles):
scrobble_str = ( scrobble_str = f"{scrobble.id}\t{scrobble.timestamp}\t{scrobble.media_obj}"
f"{scrobble.id}\t{scrobble.timestamp}\t{scrobble.media_obj}"
)
log_line = f"{scrobble_str}" log_line = f"{scrobble_str}"
if count > 0: if count > 0:
log_line = "\n" + log_line log_line = "\n" + log_line
@ -173,9 +166,7 @@ class KoReaderImport(BaseFileImportMixin):
return "KOReader" return "KOReader"
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return reverse("scrobbles:koreader-import-detail", kwargs={"slug": self.uuid})
"scrobbles:koreader-import-detail", kwargs={"slug": self.uuid}
)
def get_path(instance, filename): def get_path(instance, filename):
extension = filename.split(".")[-1] extension = filename.split(".")[-1]
@ -211,15 +202,11 @@ class KoReaderImport(BaseFileImportMixin):
fix_profile_historic_timezones(self.user.profile) fix_profile_historic_timezones(self.user.profile)
if self.processed_finished and not force: if self.processed_finished and not force:
logger.info( logger.info(f"{self} already processed on {self.processed_finished}")
f"{self} already processed on {self.processed_finished}"
)
return return
self.mark_started() self.mark_started()
scrobbles = process_koreader_sqlite_file( scrobbles = process_koreader_sqlite_file(self.upload_file_path, self.user.id)
self.upload_file_path, self.user.id
)
self.record_log(scrobbles) self.record_log(scrobbles)
self.mark_finished() self.mark_finished()
@ -233,9 +220,7 @@ class AudioScrobblerTSVImport(BaseFileImportMixin):
return "AudiosScrobbler" return "AudiosScrobbler"
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return reverse("scrobbles:tsv-import-detail", kwargs={"slug": self.uuid})
"scrobbles:tsv-import-detail", kwargs={"slug": self.uuid}
)
def get_path(instance, filename): def get_path(instance, filename):
extension = filename.split(".")[-1] extension = filename.split(".")[-1]
@ -259,16 +244,12 @@ class AudioScrobblerTSVImport(BaseFileImportMixin):
fix_profile_historic_timezones(self.user.profile) fix_profile_historic_timezones(self.user.profile)
if self.processed_finished and not force: if self.processed_finished and not force:
logger.info( logger.info(f"{self} already processed on {self.processed_finished}")
f"{self} already processed on {self.processed_finished}"
)
return return
self.mark_started() self.mark_started()
scrobbles = import_audioscrobbler_tsv_file( scrobbles = import_audioscrobbler_tsv_file(self.upload_file_path, self.user.id)
self.upload_file_path, self.user.id
)
self.record_log(scrobbles) self.record_log(scrobbles)
self.mark_finished() self.mark_finished()
@ -282,9 +263,7 @@ class LastFmImport(BaseFileImportMixin):
return "LastFM" return "LastFM"
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return reverse("scrobbles:lastfm-import-detail", kwargs={"slug": self.uuid})
"scrobbles:lastfm-import-detail", kwargs={"slug": self.uuid}
)
def process(self, import_all=False): def process(self, import_all=False):
"""Import scrobbles found on LastFM""" """Import scrobbles found on LastFM"""
@ -293,9 +272,7 @@ class LastFmImport(BaseFileImportMixin):
fix_profile_historic_timezones(self.user.profile) fix_profile_historic_timezones(self.user.profile)
if self.processed_finished: if self.processed_finished:
logger.info( logger.info(f"{self} already processed on {self.processed_finished}")
f"{self} already processed on {self.processed_finished}"
)
return return
last_import = None last_import = None
@ -333,9 +310,7 @@ class RetroarchImport(BaseFileImportMixin):
return "Retroarch" return "Retroarch"
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return reverse("scrobbles:retroarch-import-detail", kwargs={"slug": self.uuid})
"scrobbles:retroarch-import-detail", kwargs={"slug": self.uuid}
)
def process(self, import_all=False, force=False): def process(self, import_all=False, force=False):
"""Import scrobbles found on Retroarch""" """Import scrobbles found on Retroarch"""
@ -343,9 +318,7 @@ class RetroarchImport(BaseFileImportMixin):
fix_profile_historic_timezones(self.user.profile) fix_profile_historic_timezones(self.user.profile)
if self.processed_finished and not force: if self.processed_finished and not force:
logger.info( logger.info(f"{self} already processed on {self.processed_finished}")
f"{self} already processed on {self.processed_finished}"
)
return return
if force: if force:
@ -536,39 +509,25 @@ class Scrobble(TimeStampedModel):
podcast_episode = models.ForeignKey( podcast_episode = models.ForeignKey(
PodcastEpisode, on_delete=models.DO_NOTHING, **BNULL PodcastEpisode, on_delete=models.DO_NOTHING, **BNULL
) )
sport_event = models.ForeignKey( sport_event = models.ForeignKey(SportEvent, on_delete=models.DO_NOTHING, **BNULL)
SportEvent, on_delete=models.DO_NOTHING, **BNULL
)
book = models.ForeignKey(Book, on_delete=models.DO_NOTHING, **BNULL) book = models.ForeignKey(Book, on_delete=models.DO_NOTHING, **BNULL)
paper = models.ForeignKey(Paper, on_delete=models.DO_NOTHING, **BNULL) paper = models.ForeignKey(Paper, on_delete=models.DO_NOTHING, **BNULL)
video_game = models.ForeignKey( video_game = models.ForeignKey(VideoGame, on_delete=models.DO_NOTHING, **BNULL)
VideoGame, on_delete=models.DO_NOTHING, **BNULL board_game = models.ForeignKey(BoardGame, on_delete=models.DO_NOTHING, **BNULL)
) geo_location = models.ForeignKey(GeoLocation, on_delete=models.DO_NOTHING, **BNULL)
board_game = models.ForeignKey(
BoardGame, on_delete=models.DO_NOTHING, **BNULL
)
geo_location = models.ForeignKey(
GeoLocation, on_delete=models.DO_NOTHING, **BNULL
)
beer = models.ForeignKey(Beer, on_delete=models.DO_NOTHING, **BNULL) beer = models.ForeignKey(Beer, on_delete=models.DO_NOTHING, **BNULL)
puzzle = models.ForeignKey(Puzzle, on_delete=models.DO_NOTHING, **BNULL) puzzle = models.ForeignKey(Puzzle, on_delete=models.DO_NOTHING, **BNULL)
food = models.ForeignKey(Food, on_delete=models.DO_NOTHING, **BNULL) food = models.ForeignKey(Food, on_delete=models.DO_NOTHING, **BNULL)
trail = models.ForeignKey(Trail, on_delete=models.DO_NOTHING, **BNULL) trail = models.ForeignKey(Trail, on_delete=models.DO_NOTHING, **BNULL)
task = models.ForeignKey(Task, on_delete=models.DO_NOTHING, **BNULL) task = models.ForeignKey(Task, on_delete=models.DO_NOTHING, **BNULL)
web_page = models.ForeignKey(WebPage, on_delete=models.DO_NOTHING, **BNULL) web_page = models.ForeignKey(WebPage, on_delete=models.DO_NOTHING, **BNULL)
life_event = models.ForeignKey( life_event = models.ForeignKey(LifeEvent, on_delete=models.DO_NOTHING, **BNULL)
LifeEvent, on_delete=models.DO_NOTHING, **BNULL
)
mood = models.ForeignKey(Mood, on_delete=models.DO_NOTHING, **BNULL) mood = models.ForeignKey(Mood, on_delete=models.DO_NOTHING, **BNULL)
brick_set = models.ForeignKey( brick_set = models.ForeignKey(BrickSet, on_delete=models.DO_NOTHING, **BNULL)
BrickSet, on_delete=models.DO_NOTHING, **BNULL
)
media_type = models.CharField( media_type = models.CharField(
max_length=14, choices=MediaType.choices, default=MediaType.VIDEO max_length=14, choices=MediaType.choices, default=MediaType.VIDEO
) )
user = models.ForeignKey( user = models.ForeignKey(User, blank=True, null=True, on_delete=models.DO_NOTHING)
User, blank=True, null=True, on_delete=models.DO_NOTHING
)
# Time keeping # Time keeping
timestamp = models.DateTimeField(**BNULL) timestamp = models.DateTimeField(**BNULL)
@ -596,9 +555,7 @@ class Scrobble(TimeStampedModel):
upload_to="scrobbles/videogame_save_data/", **BNULL upload_to="scrobbles/videogame_save_data/", **BNULL
) )
gpx_file = models.FileField(upload_to="scrobbles/gpx_file/", **BNULL) gpx_file = models.FileField(upload_to="scrobbles/gpx_file/", **BNULL)
screenshot = models.ImageField( screenshot = models.ImageField(upload_to="scrobbles/videogame_screenshot/", **BNULL)
upload_to="scrobbles/videogame_screenshot/", **BNULL
)
screenshot_small = ImageSpecField( screenshot_small = ImageSpecField(
source="screenshot", source="screenshot",
processors=[ResizeToFit(100, 100)], processors=[ResizeToFit(100, 100)],
@ -614,6 +571,16 @@ class Scrobble(TimeStampedModel):
long_play_seconds = models.BigIntegerField(**BNULL) long_play_seconds = models.BigIntegerField(**BNULL)
long_play_complete = models.BooleanField(**BNULL) long_play_complete = models.BooleanField(**BNULL)
class Meta:
indexes = [
models.Index(fields=["timestamp"]),
models.Index(fields=["media_type"]),
models.Index(fields=["source_id"]),
models.Index(
fields=["user", "in_progress", "played_to_completion", "is_paused"]
),
]
@classmethod @classmethod
def for_year(cls, user, year): def for_year(cls, user, year):
return cls.objects.filter(timestamp__year=year, user=user).order_by( return cls.objects.filter(timestamp__year=year, user=user).order_by(
@ -679,9 +646,7 @@ class Scrobble(TimeStampedModel):
from scrobbles.models import Scrobble from scrobbles.models import Scrobble
if self.logdata and self.logdata.serial_scrobble_id: if self.logdata and self.logdata.serial_scrobble_id:
return Scrobble.objects.filter( return Scrobble.objects.filter(id=self.logdata.serial_scrobble_id).first()
id=self.logdata.serial_scrobble_id
).first()
@property @property
def finish_url(self) -> str: def finish_url(self) -> str:
@ -703,8 +668,12 @@ class Scrobble(TimeStampedModel):
if self.media_obj: if self.media_obj:
self.media_type = self.MediaType(self.media_obj.__class__.__name__) self.media_type = self.MediaType(self.media_obj.__class__.__name__)
if (self.timestamp and self.stop_timestamp) and (not self.playback_position_seconds or self.playback_position_seconds <= 0): if (self.timestamp and self.stop_timestamp) and (
self.playback_position_seconds = (self.stop_timestamp - self.timestamp).seconds not self.playback_position_seconds or self.playback_position_seconds <= 0
):
self.playback_position_seconds = (
self.stop_timestamp - self.timestamp
).seconds
return super(Scrobble, self).save(*args, **kwargs) return super(Scrobble, self).save(*args, **kwargs)
@ -715,9 +684,9 @@ class Scrobble(TimeStampedModel):
return reverse("scrobbles:detail", kwargs={"uuid": self.uuid}) return reverse("scrobbles:detail", kwargs={"uuid": self.uuid})
def push_to_archivebox(self): def push_to_archivebox(self):
pushable_media = hasattr( pushable_media = hasattr(self.media_obj, "push_to_archivebox") and callable(
self.media_obj, "push_to_archivebox" self.media_obj.push_to_archivebox
) and callable(self.media_obj.push_to_archivebox) )
if pushable_media and self.user.profile.archivebox_url: if pushable_media and self.user.profile.archivebox_url:
try: try:
@ -781,10 +750,7 @@ class Scrobble(TimeStampedModel):
logger.info(f"Redirecting to {self.media_obj.url}") logger.info(f"Redirecting to {self.media_obj.url}")
redirect_url = self.media_obj.url redirect_url = self.media_obj.url
if ( if self.media_type == self.MediaType.VIDEO and self.media_obj.youtube_id:
self.media_type == self.MediaType.VIDEO
and self.media_obj.youtube_id
):
redirect_url = self.media_obj.youtube_link redirect_url = self.media_obj.youtube_link
return redirect_url return redirect_url
@ -800,9 +766,7 @@ class Scrobble(TimeStampedModel):
@property @property
def local_stop_timestamp(self): def local_stop_timestamp(self):
if self.stop_timestamp: if self.stop_timestamp:
return timezone.localtime( return timezone.localtime(self.stop_timestamp, timezone=self.tzinfo)
self.stop_timestamp, timezone=self.tzinfo
)
@property @property
def scrobble_media_key(self) -> str: def scrobble_media_key(self) -> str:
@ -930,9 +894,7 @@ class Scrobble(TimeStampedModel):
long_play_secs = 0 long_play_secs = 0
if self.previous and not self.previous.long_play_complete: if self.previous and not self.previous.long_play_complete:
long_play_secs = self.previous.long_play_seconds or 0 long_play_secs = self.previous.long_play_seconds or 0
percent = int( percent = int(((playback_seconds + long_play_secs) / run_time_secs) * 100)
((playback_seconds + long_play_secs) / run_time_secs) * 100
)
return percent return percent
@ -977,9 +939,7 @@ class Scrobble(TimeStampedModel):
expected_end = self.timestamp + datetime.timedelta( expected_end = self.timestamp + datetime.timedelta(
seconds=self.media_obj.run_time_seconds seconds=self.media_obj.run_time_seconds
) )
expected_end_padded = expected_end + datetime.timedelta( expected_end_padded = expected_end + datetime.timedelta(seconds=padding_seconds)
seconds=padding_seconds
)
# Take our start time, add our media length and an extra 30 min (1800s) is it still in the future? keep going # Take our start time, add our media length and an extra 30 min (1800s) is it still in the future? keep going
is_in_progress = expected_end_padded > pendulum.now() is_in_progress = expected_end_padded > pendulum.now()
logger.info( logger.info(
@ -995,7 +955,10 @@ class Scrobble(TimeStampedModel):
@property @property
def can_be_updated(self) -> bool: def can_be_updated(self) -> bool:
if self.media_obj.__class__.__name__ in LONG_PLAY_MEDIA.values() and self.source != "readcomicsonline.ru": if (
self.media_obj.__class__.__name__ in LONG_PLAY_MEDIA.values()
and self.source != "readcomicsonline.ru"
):
logger.info( logger.info(
"[scrobbling] cannot be updated, long play media", "[scrobbling] cannot be updated, long play media",
extra={ extra={
@ -1039,11 +1002,9 @@ class Scrobble(TimeStampedModel):
@classmethod @classmethod
def by_date(cls, media_type: str = "Track"): def by_date(cls, media_type: str = "Track"):
cls.objects.filter(media_type=media_type).values( cls.objects.filter(media_type=media_type).values("timestamp__date").annotate(
"timestamp__date" count=models.Count("id")
).annotate(count=models.Count("id")).values( ).values("timestamp__date", "count").order_by(
"timestamp__date", "count"
).order_by(
"-count", "-count",
) )
@ -1150,7 +1111,10 @@ class Scrobble(TimeStampedModel):
if existing_by_source_id: if existing_by_source_id:
logger.info( logger.info(
"[create_or_update] found existing scrobble by source_id, updating", "[create_or_update] found existing scrobble by source_id, updating",
extra={"scrobble_id": existing_by_source_id.id, "source_id": source_id}, extra={
"scrobble_id": existing_by_source_id.id,
"source_id": source_id,
},
) )
scrobble_data["playback_status"] = scrobble_data.pop("status", None) scrobble_data["playback_status"] = scrobble_data.pop("status", None)
return existing_by_source_id.update(scrobble_data) return existing_by_source_id.update(scrobble_data)
@ -1172,9 +1136,7 @@ class Scrobble(TimeStampedModel):
logger.warning( logger.warning(
f"[create_or_update] geoloc requires create_or_update_location" f"[create_or_update] geoloc requires create_or_update_location"
) )
scrobble = cls.create_or_update_location( scrobble = cls.create_or_update_location(media, scrobble_data, user_id)
media, scrobble_data, user_id
)
return scrobble return scrobble
if not skip_in_progress_check or read_log_page: if not skip_in_progress_check or read_log_page:
@ -1187,9 +1149,7 @@ class Scrobble(TimeStampedModel):
"scrobble_data": scrobble_data, "scrobble_data": scrobble_data,
}, },
) )
scrobble_data["playback_status"] = scrobble_data.pop( scrobble_data["playback_status"] = scrobble_data.pop("status", None)
"status", None
)
# If it's marked as stopped, send it through our update mechanism, which will complete it # If it's marked as stopped, send it through our update mechanism, which will complete it
if scrobble and ( if scrobble and (
scrobble.can_be_updated scrobble.can_be_updated
@ -1207,7 +1167,7 @@ class Scrobble(TimeStampedModel):
page_list.append( page_list.append(
BookPageLogData( BookPageLogData(
page_number=read_log_page, page_number=read_log_page,
start_ts=int(timezone.now().timestamp()) start_ts=int(timezone.now().timestamp()),
) )
) )
scrobble.log["page_data"] = page_list scrobble.log["page_data"] = page_list
@ -1220,7 +1180,14 @@ class Scrobble(TimeStampedModel):
scrobble_data.pop("playback_status") scrobble_data.pop("playback_status")
if read_log_page: if read_log_page:
scrobble_data["log"] = BookLogData(page_data=[BookPageLogData(page_number=read_log_page, start_ts=int(timezone.now().timestamp()))]) scrobble_data["log"] = BookLogData(
page_data=[
BookPageLogData(
page_number=read_log_page,
start_ts=int(timezone.now().timestamp()),
)
]
)
logger.info( logger.info(
f"[scrobbling] creating new scrobble", f"[scrobbling] creating new scrobble",
@ -1231,7 +1198,9 @@ class Scrobble(TimeStampedModel):
"source": source, "source": source,
}, },
) )
if mtype == cls.MediaType.FOOD and not scrobble_data.get("log", {}).get("calories", None): if mtype == cls.MediaType.FOOD and not scrobble_data.get("log", {}).get(
"calories", None
):
if media.calories: if media.calories:
scrobble_data["log"] = FoodLogData(calories=media.calories) scrobble_data["log"] = FoodLogData(calories=media.calories)
@ -1318,9 +1287,7 @@ class Scrobble(TimeStampedModel):
if existing_locations := location.in_proximity(named=True): if existing_locations := location.in_proximity(named=True):
existing_location = existing_locations.first() existing_location = existing_locations.first()
ts = int(pendulum.now().timestamp()) ts = int(pendulum.now().timestamp())
scrobble.log[ scrobble.log[ts] = f"Location {location.id} too close to this scrobble"
ts
] = f"Location {location.id} too close to this scrobble"
scrobble.save(update_fields=["log"]) scrobble.save(update_fields=["log"])
logger.info( logger.info(
f"[scrobbling] finished - found existing named location", f"[scrobbling] finished - found existing named location",
@ -1499,9 +1466,7 @@ class Scrobble(TimeStampedModel):
"""Returns true if our media is beyond our completion percent, unless """Returns true if our media is beyond our completion percent, unless
our type is geolocation in which case we always return false our type is geolocation in which case we always return false
""" """
beyond_completion = ( beyond_completion = self.percent_played >= self.media_obj.COMPLETION_PERCENT
self.percent_played >= self.media_obj.COMPLETION_PERCENT
)
if self.media_type == "GeoLocation": if self.media_type == "GeoLocation":
logger.info( logger.info(

View File

@ -87,6 +87,7 @@ def mopidy_scrobble_media(post_data: dict, user_id: int) -> Scrobble:
log = { log = {
"mopidy_source": post_data.get("mopidy_uri", "").split(":")[0], "mopidy_source": post_data.get("mopidy_uri", "").split(":")[0],
"raw_data": post_data, "raw_data": post_data,
"album": post_data.get("album", ""),
} }
except IndexError: except IndexError:
pass pass
@ -95,17 +96,14 @@ def mopidy_scrobble_media(post_data: dict, user_id: int) -> Scrobble:
user_id, user_id,
source="Mopidy", source="Mopidy",
playback_position_seconds=int( playback_position_seconds=int(
post_data.get(MOPIDY_POST_KEYS.get("PLAYBACK_POSITION_TICKS"), 1) post_data.get(MOPIDY_POST_KEYS.get("PLAYBACK_POSITION_TICKS"), 1) / 1000
/ 1000
), ),
status=post_data.get(MOPIDY_POST_KEYS.get("STATUS"), ""), status=post_data.get(MOPIDY_POST_KEYS.get("STATUS"), ""),
log=log, log=log,
) )
def jellyfin_scrobble_media( def jellyfin_scrobble_media(post_data: dict, user_id: int) -> Optional[Scrobble]:
post_data: dict, user_id: int
) -> Optional[Scrobble]:
media_type = Scrobble.MediaType.VIDEO media_type = Scrobble.MediaType.VIDEO
if post_data.pop("ItemType", "") in JELLYFIN_AUDIO_ITEM_TYPES: if post_data.pop("ItemType", "") in JELLYFIN_AUDIO_ITEM_TYPES:
media_type = Scrobble.MediaType.TRACK media_type = Scrobble.MediaType.TRACK
@ -123,13 +121,12 @@ def jellyfin_scrobble_media(
) )
return return
timestamp = parse( timestamp = parse(post_data.get(JELLYFIN_POST_KEYS.get("TIMESTAMP"), "")).replace(
post_data.get(JELLYFIN_POST_KEYS.get("TIMESTAMP"), "") tzinfo=pytz.utc
).replace(tzinfo=pytz.utc) )
playback_position_seconds = int( playback_position_seconds = int(
post_data.get(JELLYFIN_POST_KEYS.get("PLAYBACK_POSITION_TICKS"), 1) post_data.get(JELLYFIN_POST_KEYS.get("PLAYBACK_POSITION_TICKS"), 1) / 10000000
/ 10000000
) )
if media_type == Scrobble.MediaType.VIDEO: if media_type == Scrobble.MediaType.VIDEO:
imdb_id = post_data.get("Provider_imdb", "") imdb_id = post_data.get("Provider_imdb", "")
@ -139,9 +136,7 @@ def jellyfin_scrobble_media(
title=post_data.get("Name", ""), title=post_data.get("Name", ""),
artist_name=post_data.get("Artist", ""), artist_name=post_data.get("Artist", ""),
album_name=post_data.get("Album", ""), album_name=post_data.get("Album", ""),
run_time_seconds=convert_to_seconds( run_time_seconds=convert_to_seconds(post_data.get("RunTime", 900000)),
post_data.get("RunTime", 900000)
),
) )
# A hack because we don't worry about updating music ... we either finish it or we don't # A hack because we don't worry about updating music ... we either finish it or we don't
playback_position_seconds = 0 playback_position_seconds = 0
@ -165,6 +160,7 @@ def jellyfin_scrobble_media(
source_id=post_data.get(JELLYFIN_POST_KEYS.get("MEDIA_SOURCE_ID")), source_id=post_data.get(JELLYFIN_POST_KEYS.get("MEDIA_SOURCE_ID")),
playback_position_seconds=playback_position_seconds, playback_position_seconds=playback_position_seconds,
status=playback_status, status=playback_status,
log={"album": post_data.get("Album", "")},
) )
@ -301,9 +297,7 @@ def manual_scrobble_book(
if not page: if not page:
page = 1 page = 1
logger.info( logger.info("[scrobblers] Book page included in scrobble, should update!")
"[scrobblers] Book page included in scrobble, should update!"
)
source = READCOMICSONLINE_URL.replace("https://", "") source = READCOMICSONLINE_URL.replace("https://", "")
@ -420,9 +414,7 @@ def email_scrobble_board_game(
if not location.name: if not location.name:
location.name = location_dict.get("name") location.name = location_dict.get("name")
update_fields.append("name") update_fields.append("name")
geoloc = GeoLocation.objects.filter( geoloc = GeoLocation.objects.filter(title__icontains=location.name).first()
title__icontains=location.name
).first()
if geoloc: if geoloc:
location.geo_location = geoloc location.geo_location = geoloc
update_fields.append("geo_location") update_fields.append("geo_location")
@ -474,9 +466,7 @@ def email_scrobble_board_game(
log_data.pop("expansion_ids") log_data.pop("expansion_ids")
if play_dict.get("locationRefId", False): if play_dict.get("locationRefId", False):
log_data["location_id"] = locations[ log_data["location_id"] = locations[play_dict.get("locationRefId")].id
play_dict.get("locationRefId")
].id
if play_dict.get("rounds", False): if play_dict.get("rounds", False):
log_data["rounds"] = play_dict.get("rounds") log_data["rounds"] = play_dict.get("rounds")
if play_dict.get("board", False): if play_dict.get("board", False):
@ -499,9 +489,7 @@ def email_scrobble_board_game(
timestamp = parse(play_dict.get("playDate")) timestamp = parse(play_dict.get("playDate"))
if hour and minute: if hour and minute:
logger.info(f"Scrobble playDate has manual start time {timestamp}") logger.info(f"Scrobble playDate has manual start time {timestamp}")
timestamp = timestamp.replace( timestamp = timestamp.replace(hour=hour, minute=minute, second=second or 0)
hour=hour, minute=minute, second=second or 0
)
logger.info(f"Update to {timestamp}") logger.info(f"Update to {timestamp}")
profile = UserProfile.objects.filter(user_id=user_id).first() profile = UserProfile.objects.filter(user_id=user_id).first()
@ -596,9 +584,7 @@ def manual_scrobble_from_url(
scrobbler. Otherwise, return nothing.""" scrobbler. Otherwise, return nothing."""
if RecipeScraperService.is_recipe_url(url): if RecipeScraperService.is_recipe_url(url):
return scrobble_from_recipe_website( return scrobble_from_recipe_website(url, user_id, source=source, action=action)
url, user_id, source=source, action=action
)
content_key = "" content_key = ""
domain = extract_domain(url) domain = extract_domain(url)
@ -801,9 +787,7 @@ def emacs_scrobble_update_task(
if not scrobble.log.get('notes"'): if not scrobble.log.get('notes"'):
scrobble.log["notes"] = [] scrobble.log["notes"] = []
if note.get("timestamp") not in existing_note_ts: if note.get("timestamp") not in existing_note_ts:
scrobble.log["notes"].append( scrobble.log["notes"].append({note.get("timestamp"): note.get("content")})
{note.get("timestamp"): note.get("content")}
)
notes_updated = True notes_updated = True
if notes_updated: if notes_updated:
@ -829,9 +813,7 @@ def emacs_scrobble_task(
user_context_list: list[str] = [], user_context_list: list[str] = [],
) -> Scrobble | None: ) -> Scrobble | None:
orgmode_id = task_data.get("source_id") orgmode_id = task_data.get("source_id")
title = get_title_from_labels( title = get_title_from_labels(task_data.get("labels", []), user_context_list)
task_data.get("labels", []), user_context_list
)
task = Task.find_or_create(title) task = Task.find_or_create(title)
@ -954,9 +936,7 @@ def manual_scrobble_webpage(
): ):
if RecipeScraperService.is_recipe_url(url): if RecipeScraperService.is_recipe_url(url):
return scrobble_from_recipe_website( return scrobble_from_recipe_website(url, user_id, source=source, action=action)
url, user_id, source=source, action=action
)
webpage = WebPage.find_or_create({"url": url}) webpage = WebPage.find_or_create({"url": url})

View File

@ -155,25 +155,17 @@ class RecentScrobbleList(ListView):
next_date = date + timedelta(weeks=+2) next_date = date + timedelta(weeks=+2)
prev_date = date + timedelta(weeks=-1) prev_date = date + timedelta(weeks=-1)
if date.isocalendar()[1] < today.isocalendar()[1]: if date.isocalendar()[1] < today.isocalendar()[1]:
data[ data["next_link"] = f"?date={next_date.strftime('%Y-W%W')}"
"next_link"
] = f"?date={next_date.strftime('%Y-W%W')}"
data["prev_link"] = f"?date={prev_date.strftime('%Y-W%W')}" data["prev_link"] = f"?date={prev_date.strftime('%Y-W%W')}"
data[ data["title"] = f"Week {date.strftime('%-W')} of {date.year}"
"title"
] = f"Week {date.strftime('%-W')} of {date.year}"
data = data | Scrobble.as_dict_by_type( data = data | Scrobble.as_dict_by_type(
Scrobble.for_week( Scrobble.for_week(user_id, date.year, date.isocalendar()[1])
user_id, date.year, date.isocalendar()[1]
)
) )
elif date_str == "this_month" or date_str.count("-") == 1: elif date_str == "this_month" or date_str.count("-") == 1:
next_date = date + relativedelta(months=1) next_date = date + relativedelta(months=1)
prev_date = date + relativedelta(months=-1) prev_date = date + relativedelta(months=-1)
if date.month < today.month: if date.month < today.month:
data[ data["next_link"] = f"?date={next_date.strftime('%Y-%m')}"
"next_link"
] = f"?date={next_date.strftime('%Y-%m')}"
data["title"] = f"{date.strftime('%B %Y')}" data["title"] = f"{date.strftime('%B %Y')}"
data["prev_link"] = f"?date={prev_date.strftime('%Y-%m')}" data["prev_link"] = f"?date={prev_date.strftime('%Y-%m')}"
data = data | Scrobble.as_dict_by_type( data = data | Scrobble.as_dict_by_type(
@ -182,24 +174,16 @@ class RecentScrobbleList(ListView):
elif date_str == "today" or date_str.count("-") == 2: elif date_str == "today" or date_str.count("-") == 2:
next_date = date + timedelta(days=1) next_date = date + timedelta(days=1)
prev_date = date - timedelta(days=1) prev_date = date - timedelta(days=1)
data[ data["prev_link"] = f"?date={prev_date.strftime('%Y-%m-%d')}"
"prev_link"
] = f"?date={prev_date.strftime('%Y-%m-%d')}"
if date < today: if date < today:
data[ data["next_link"] = f"?date={next_date.strftime('%Y-%m-%d')}"
"next_link"
] = f"?date={next_date.strftime('%Y-%m-%d')}"
if date == today: if date == today:
data["title"] = "Today" data["title"] = "Today"
else: else:
data["title"] = f"{date.strftime('%Y-%m-%d')}" data["title"] = f"{date.strftime('%Y-%m-%d')}"
data[ data["today_link"] = f"?date={today.strftime('%Y-%m-%d')}"
"today_link"
] = f"?date={today.strftime('%Y-%m-%d')}"
data = data | Scrobble.as_dict_by_type( data = data | Scrobble.as_dict_by_type(
Scrobble.for_day( Scrobble.for_day(user_id, date.year, date.month, date.day)
user_id, date.year, date.month, date.day
)
) )
elif date_str == "this_year" or date_str.count("-") == 0: elif date_str == "this_year" or date_str.count("-") == 0:
next_date = date + relativedelta(years=+1) next_date = date + relativedelta(years=+1)
@ -222,9 +206,7 @@ class RecentScrobbleList(ListView):
data = data | Scrobble.as_dict_by_type( data = data | Scrobble.as_dict_by_type(
Scrobble.for_day(user_id, date.year, date.month, date.day) Scrobble.for_day(user_id, date.year, date.month, date.day)
) )
data[ data["today_link"] = "" # f"?date={today.strftime('%Y-%m-%d')}"
"today_link"
] = "" # f"?date={today.strftime('%Y-%m-%d')}"
data["active_imports"] = AudioScrobblerTSVImport.objects.filter( data["active_imports"] = AudioScrobblerTSVImport.objects.filter(
processing_started__isnull=False, processing_started__isnull=False,
@ -232,7 +214,9 @@ class RecentScrobbleList(ListView):
user=self.request.user, user=self.request.user,
) )
data["counts"] = [] # scrobble_counts(user) data["counts"] = [] # scrobble_counts(user)
data["daily_calories"] = get_daily_calories_for_user_by_day(self.request.user.id, date) data["daily_calories"] = get_daily_calories_for_user_by_day(
self.request.user.id, date
)
else: else:
data["weekly_data"] = week_of_scrobbles() data["weekly_data"] = week_of_scrobbles()
data["counts"] = scrobble_counts() data["counts"] = scrobble_counts()
@ -251,9 +235,7 @@ class ScrobbleLongPlaysView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs) context_data = super().get_context_data(**kwargs)
context_data["view"] = self.request.GET.get("view", "grid") context_data["view"] = self.request.GET.get("view", "grid")
context_data["in_progress"] = get_long_plays_in_progress( context_data["in_progress"] = get_long_plays_in_progress(self.request.user)
self.request.user
)
context_data["completed"] = get_long_plays_completed(self.request.user) context_data["completed"] = get_long_plays_completed(self.request.user)
return context_data return context_data
@ -330,9 +312,7 @@ class ManualScrobbleView(FormView):
scrobble_fn = MANUAL_SCROBBLE_FNS[key] scrobble_fn = MANUAL_SCROBBLE_FNS[key]
scrobble = eval(scrobble_fn)(item_id, self.request.user.id) scrobble = eval(scrobble_fn)(item_id, self.request.user.id)
return HttpResponseRedirect( return HttpResponseRedirect(scrobble.redirect_url(self.request.user.id))
scrobble.redirect_url(self.request.user.id)
)
class JsonableResponseMixin: class JsonableResponseMixin:
@ -378,9 +358,7 @@ class AudioScrobblerImportCreateView(
return HttpResponseRedirect(self.request.META.get("HTTP_REFERER")) return HttpResponseRedirect(self.request.META.get("HTTP_REFERER"))
class KoReaderImportCreateView( class KoReaderImportCreateView(LoginRequiredMixin, JsonableResponseMixin, CreateView):
LoginRequiredMixin, JsonableResponseMixin, CreateView
):
model = KoReaderImport model = KoReaderImport
fields = ["sqlite_file"] fields = ["sqlite_file"]
template_name = "scrobbles/upload_form.html" template_name = "scrobbles/upload_form.html"
@ -439,9 +417,7 @@ def web_scrobbler_webhook(request):
playing_state = "started" playing_state = "started"
if request.POST.get("isPlaying") == "false": if request.POST.get("isPlaying") == "false":
playing_state = "stopped" playing_state = "stopped"
scrobble = web_scrobbler_scrobble_media( scrobble = web_scrobbler_scrobble_media(youtube_id, user_id, status=playing_state)
youtube_id, user_id, status=playing_state
)
if not scrobble: if not scrobble:
logger.warning( logger.warning(
"[web_scrobbler_webhook] failed", "[web_scrobbler_webhook] failed",
@ -566,18 +542,12 @@ def import_audioscrobbler_file(request):
scrobbles_created = [] scrobbles_created = []
# tsv_file = request.FILES[0] # tsv_file = request.FILES[0]
file_serializer = serializers.AudioScrobblerTSVImportSerializer( file_serializer = serializers.AudioScrobblerTSVImportSerializer(data=request.data)
data=request.data
)
if file_serializer.is_valid(): if file_serializer.is_valid():
import_file = file_serializer.save() import_file = file_serializer.save()
return Response( return Response({"scrobble_ids": scrobbles_created}, status=status.HTTP_200_OK)
{"scrobble_ids": scrobbles_created}, status=status.HTTP_200_OK
)
else: else:
return Response( return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
file_serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
@ -623,9 +593,9 @@ def scrobble_start(request, uuid):
request, messages.ERROR, f"Media with uuid {uuid} not found." request, messages.ERROR, f"Media with uuid {uuid} not found."
) )
if ( if user.profile.redirect_to_webpage and (
user.profile.redirect_to_webpage media_obj.__class__.__name__ == Scrobble.MediaType.WEBPAGE
and (media_obj.__class__.__name__ == Scrobble.MediaType.WEBPAGE or media_obj.__class__.__name__ == Scrobble.MediaType.BOOK) or media_obj.__class__.__name__ == Scrobble.MediaType.BOOK
): ):
logger.info(f"Redirecting to {media_obj} detail page") logger.info(f"Redirecting to {media_obj} detail page")
return HttpResponseRedirect(media_obj.url) return HttpResponseRedirect(media_obj.url)
@ -754,9 +724,7 @@ class ChartRecordView(TemplateView):
).order_by("rank") ).order_by("rank")
if charts.count() == 0: if charts.count() == 0:
ChartRecord.build( ChartRecord.build(user=self.request.user, model_str=media_type, **kwargs)
user=self.request.user, model_str=media_type, **kwargs
)
charts = ChartRecord.objects.filter( charts = ChartRecord.objects.filter(
media_filter, user=self.request.user, **kwargs media_filter, user=self.request.user, **kwargs
).order_by("rank") ).order_by("rank")
@ -788,9 +756,7 @@ class ChartRecordView(TemplateView):
media_type = self.request.GET.get("media", "Track") media_type = self.request.GET.get("media", "Track")
user = self.request.user user = self.request.user
params = {} params = {}
context_data["chart_type"] = self.request.GET.get( context_data["chart_type"] = self.request.GET.get("chart_type", "maloja")
"chart_type", "maloja"
)
context_data["artist_charts"] = {} context_data["artist_charts"] = {}
if not date: if not date:
@ -855,9 +821,7 @@ class ChartRecordView(TemplateView):
params["day"] = day params["day"] = day
month_str = calendar.month_name[month] month_str = calendar.month_name[month]
name = f"Chart for {month_str} {day}, {year}" name = f"Chart for {month_str} {day}, {year}"
in_progress = ( in_progress = now.month == month and now.year == year and now.day == day
now.month == month and now.year == year and now.day == day
)
media_filter = self.get_media_filter("Track") media_filter = self.get_media_filter("Track")
track_charts = ChartRecord.objects.filter( track_charts = ChartRecord.objects.filter(
@ -869,17 +833,13 @@ class ChartRecordView(TemplateView):
).order_by("rank") ).order_by("rank")
if track_charts.count() == 0 and not in_progress: if track_charts.count() == 0 and not in_progress:
ChartRecord.build( ChartRecord.build(user=self.request.user, model_str="Track", **params)
user=self.request.user, model_str="Track", **params
)
media_filter = self.get_media_filter("Track") media_filter = self.get_media_filter("Track")
track_charts = ChartRecord.objects.filter( track_charts = ChartRecord.objects.filter(
media_filter, user=self.request.user, **params media_filter, user=self.request.user, **params
).order_by("rank") ).order_by("rank")
if artist_charts.count() == 0 and not in_progress: if artist_charts.count() == 0 and not in_progress:
ChartRecord.build( ChartRecord.build(user=self.request.user, model_str="Artist", **params)
user=self.request.user, model_str="Artist", **params
)
media_filter = self.get_media_filter("Artist") media_filter = self.get_media_filter("Artist")
artist_charts = ChartRecord.objects.filter( artist_charts = ChartRecord.objects.filter(
media_filter, user=self.request.user, **params media_filter, user=self.request.user, **params
@ -900,38 +860,24 @@ class ScrobbleStatusView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs) data = super().get_context_data(**kwargs)
user_scrobble_qs = Scrobble.objects.filter().order_by("-timestamp") user_scrobble_qs = Scrobble.objects.filter().order_by("-timestamp")
progress_plays = user_scrobble_qs.filter( progress_plays = user_scrobble_qs.filter(in_progress=True, is_paused=False)
in_progress=True, is_paused=False
)
data["listening"] = progress_plays.filter( data["listening"] = progress_plays.filter(
Q(track__isnull=False) | Q(podcast_episode__isnull=False) Q(track__isnull=False) | Q(podcast_episode__isnull=False)
).first() ).first()
data["watching"] = progress_plays.filter(video__isnull=False).first() data["watching"] = progress_plays.filter(video__isnull=False).first()
data["going"] = progress_plays.filter( data["going"] = progress_plays.filter(geo_location__isnull=False).first()
geo_location__isnull=False data["playing"] = progress_plays.filter(board_game__isnull=False).first()
).first() data["sporting"] = progress_plays.filter(sport_event__isnull=False).first()
data["playing"] = progress_plays.filter( data["browsing"] = progress_plays.filter(web_page__isnull=False).first()
board_game__isnull=False data["participating"] = progress_plays.filter(life_event__isnull=False).first()
).first()
data["sporting"] = progress_plays.filter(
sport_event__isnull=False
).first()
data["browsing"] = progress_plays.filter(
web_page__isnull=False
).first()
data["participating"] = progress_plays.filter(
life_event__isnull=False
).first()
data["working"] = progress_plays.filter(task__isnull=False).first() data["working"] = progress_plays.filter(task__isnull=False).first()
long_plays = user_scrobble_qs.filter( long_plays = user_scrobble_qs.filter(
long_play_complete=False, played_to_completion=True long_play_complete=False, played_to_completion=True
) )
data["reading"] = long_plays.filter(book__isnull=False).first() data["reading"] = long_plays.filter(book__isnull=False).first()
data["sessioning"] = long_plays.filter( data["sessioning"] = long_plays.filter(video_game__isnull=False).first()
video_game__isnull=False
).first()
return data return data
@ -949,14 +895,16 @@ class ScrobbleDetailView(DetailView):
log = self.object.log or {} log = self.object.log or {}
initial_notes = log.get("notes", []) initial_notes = log.get("notes", [])
if isinstance(initial_notes, list) and len(initial_notes) > 0 and isinstance(initial_notes[0], dict): if (
isinstance(initial_notes, list)
and len(initial_notes) > 0
and isinstance(initial_notes[0], dict)
):
notes_str = note_list_to_str(notes) notes_str = note_list_to_str(notes)
else: else:
notes_str = "\n".join(initial_notes) if initial_notes else "" notes_str = "\n".join(initial_notes) if initial_notes else ""
notes_str_fixed = notes_str.encode("utf-8").decode( notes_str_fixed = notes_str.encode("utf-8").decode("unicode_escape")
"unicode_escape"
)
log["notes"] = notes_str_fixed log["notes"] = notes_str_fixed
return FormClass(initial=log) return FormClass(initial=log)
@ -975,9 +923,7 @@ class ScrobbleDetailView(DetailView):
data[field_name] = original_value data[field_name] = original_value
if data.get("with_people_ids", False): if data.get("with_people_ids", False):
data["with_people_ids"] = [ data["with_people_ids"] = [p.id for p in data["with_people_ids"]]
p.id for p in data["with_people_ids"]
]
if data.get("platform_id", False): if data.get("platform_id", False):
data["platform_id"] = data["platform_id"].id data["platform_id"] = data["platform_id"].id

View File

@ -37,9 +37,7 @@ TESTING = len(sys.argv) > 1 and sys.argv[1] == "test"
TAGGIT_CASE_INSENSITIVE = True TAGGIT_CASE_INSENSITIVE = True
KEEP_DETAILED_SCROBBLE_LOGS = os.getenv( KEEP_DETAILED_SCROBBLE_LOGS = os.getenv("VROBBLER_KEEP_DETAILED_SCROBBLE_LOGS", False)
"VROBBLER_KEEP_DETAILED_SCROBBLE_LOGS", False
)
# Key must be 16, 24 or 32 bytes long and will be converted to a byte stream # Key must be 16, 24 or 32 bytes long and will be converted to a byte stream
ENCRYPTED_FIELD_KEY = os.getenv( ENCRYPTED_FIELD_KEY = os.getenv(
@ -54,9 +52,7 @@ DELETE_STALE_SCROBBLES = (
) )
# Used to dump data coming from srobbling sources, helpful for building new inputs # Used to dump data coming from srobbling sources, helpful for building new inputs
DUMP_REQUEST_DATA = ( DUMP_REQUEST_DATA = os.getenv("VROBBLER_DUMP_REQUEST_DATA", "false").lower() in TRUTHY
os.getenv("VROBBLER_DUMP_REQUEST_DATA", "false").lower() in TRUTHY
)
USDA_API_KEY = os.getenv("VROBBLER_USDA_API_KEY") USDA_API_KEY = os.getenv("VROBBLER_USDA_API_KEY")
THESPORTSDB_API_KEY = os.getenv("VROBBLER_THESPORTSDB_API_KEY", "2") THESPORTSDB_API_KEY = os.getenv("VROBBLER_THESPORTSDB_API_KEY", "2")
@ -72,9 +68,7 @@ COMICVINE_API_KEY = os.getenv("VROBBLER_COMICVINE_API_KEY")
BGG_ACCESS_TOKEN = os.getenv("VROBBLER_BGG_ACCESS_TOKEN", "") BGG_ACCESS_TOKEN = os.getenv("VROBBLER_BGG_ACCESS_TOKEN", "")
GEOLOC_ACCURACY = os.getenv("VROBBLER_GEOLOC_ACCURACY", 3) GEOLOC_ACCURACY = os.getenv("VROBBLER_GEOLOC_ACCURACY", 3)
GEOLOC_PROXIMITY = os.getenv("VROBBLER_GEOLOC_PROXIMITY", "0.0001") GEOLOC_PROXIMITY = os.getenv("VROBBLER_GEOLOC_PROXIMITY", "0.0001")
POINTS_FOR_MOVEMENT_HISTORY = os.getenv( POINTS_FOR_MOVEMENT_HISTORY = os.getenv("VROBBLER_POINTS_FOR_MOVEMENT_HISTORY", 3)
"VROBBLER_POINTS_FOR_MOVEMENT_HISTORY", 3
)
TODOIST_CLIENT_ID = os.getenv("VROBBLER_TODOIST_CLIENT_ID", "") TODOIST_CLIENT_ID = os.getenv("VROBBLER_TODOIST_CLIENT_ID", "")
TODOIST_CLIENT_SECRET = os.getenv("VROBBLER_TODOIST_CLIENT_SECRET", "") TODOIST_CLIENT_SECRET = os.getenv("VROBBLER_TODOIST_CLIENT_SECRET", "")
@ -97,9 +91,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
TIME_ZONE = os.getenv("VROBBLER_TIME_ZONE", "America/New_York") TIME_ZONE = os.getenv("VROBBLER_TIME_ZONE", "America/New_York")
ALLOWED_HOSTS = ["*"] ALLOWED_HOSTS = ["*"]
CSRF_TRUSTED_ORIGINS = [ CSRF_TRUSTED_ORIGINS = [os.getenv("VROBBLER_TRUSTED_ORIGINS", "http://localhost:8000")]
os.getenv("VROBBLER_TRUSTED_ORIGINS", "http://localhost:8000")
]
X_FRAME_OPTIONS = "SAMEORIGIN" X_FRAME_OPTIONS = "SAMEORIGIN"
REDIS_URL = os.getenv("VROBBLER_REDIS_URL", None) REDIS_URL = os.getenv("VROBBLER_REDIS_URL", None)
@ -108,9 +100,7 @@ if REDIS_URL:
else: else:
print("Eagerly running all tasks") print("Eagerly running all tasks")
CELERY_TASK_ALWAYS_EAGER = ( CELERY_TASK_ALWAYS_EAGER = os.getenv("VROBBLER_SKIP_CELERY", "false").lower() in TRUTHY
os.getenv("VROBBLER_SKIP_CELERY", "false").lower() in TRUTHY
)
CELERY_BROKER_URL = REDIS_URL if REDIS_URL else "memory://localhost/" CELERY_BROKER_URL = REDIS_URL if REDIS_URL else "memory://localhost/"
CELERY_RESULT_BACKEND = "django-db" CELERY_RESULT_BACKEND = "django-db"
CELERY_TIMEZONE = os.getenv("VROBBLER_TIME_ZONE", "America/New_York") CELERY_TIMEZONE = os.getenv("VROBBLER_TIME_ZONE", "America/New_York")
@ -210,9 +200,7 @@ DATABASES = {
} }
if TESTING: if TESTING:
DATABASES = { DATABASES = {"default": dj_database_url.config(default="sqlite:///testdb.sqlite3")}
"default": dj_database_url.config(default="sqlite:///testdb.sqlite3")
}
db_str = "" db_str = ""
if "sqlite" in DATABASES["default"]["ENGINE"]: if "sqlite" in DATABASES["default"]["ENGINE"]:
@ -310,9 +298,7 @@ else:
STATIC_ROOT = os.getenv( STATIC_ROOT = os.getenv(
"VROBBLER_STATIC_ROOT", os.path.join(PROJECT_ROOT, "static") "VROBBLER_STATIC_ROOT", os.path.join(PROJECT_ROOT, "static")
) )
MEDIA_ROOT = os.getenv( MEDIA_ROOT = os.getenv("VROBBLER_MEDIA_ROOT", os.path.join(PROJECT_ROOT, "media"))
"VROBBLER_MEDIA_ROOT", os.path.join(PROJECT_ROOT, "media")
)
STATIC_URL = os.getenv("VROBBLER_STATIC_URL", "/static/") STATIC_URL = os.getenv("VROBBLER_STATIC_URL", "/static/")
MEDIA_URL = os.getenv("VROBBLER_MEDIA_URL", "/media/") MEDIA_URL = os.getenv("VROBBLER_MEDIA_URL", "/media/")
@ -396,9 +382,7 @@ LOGGING = {
}, },
} }
LOG_TO_CONSOLE = ( LOG_TO_CONSOLE = os.getenv("VROBBLER_LOG_TO_CONSOLE", "false").lower() in TRUTHY
os.getenv("VROBBLER_LOG_TO_CONSOLE", "false").lower() in TRUTHY
)
if LOG_TO_CONSOLE: if LOG_TO_CONSOLE:
LOGGING["loggers"]["django"]["handlers"] = ["console"] LOGGING["loggers"]["django"]["handlers"] = ["console"]
LOGGING["loggers"]["vrobbler"]["handlers"] = ["console"] LOGGING["loggers"]["vrobbler"]["handlers"] = ["console"]

View File

@ -1,3 +1,5 @@
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from oauth2_provider import urls as oauth2_urls from oauth2_provider import urls as oauth2_urls
@ -6,7 +8,12 @@ from rest_framework import routers
import vrobbler.apps.scrobbles.views as scrobbles_views import vrobbler.apps.scrobbles.views as scrobbles_views
from vrobbler.apps.boardgames import urls as boardgame_urls from vrobbler.apps.boardgames import urls as boardgame_urls
from vrobbler.apps.boardgames.api.views import BoardGameViewSet, BoardGameDesignerViewSet, BoardGamePublisherViewSet, BoardGameLocationViewSet from vrobbler.apps.boardgames.api.views import (
BoardGameViewSet,
BoardGameDesignerViewSet,
BoardGamePublisherViewSet,
BoardGameLocationViewSet,
)
from vrobbler.apps.books import urls as book_urls from vrobbler.apps.books import urls as book_urls
from vrobbler.apps.books.api.views import AuthorViewSet, BookViewSet from vrobbler.apps.books.api.views import AuthorViewSet, BookViewSet
@ -31,7 +38,11 @@ from vrobbler.apps.music.api.views import (
) )
from vrobbler.apps.podcasts import urls as podcast_urls from vrobbler.apps.podcasts import urls as podcast_urls
from vrobbler.apps.podcasts.api.views import ProducerViewSet, PodcastViewSet, PodcastEpisodeViewSet from vrobbler.apps.podcasts.api.views import (
ProducerViewSet,
PodcastViewSet,
PodcastEpisodeViewSet,
)
from vrobbler.apps.scrobbles import urls as scrobble_urls from vrobbler.apps.scrobbles import urls as scrobble_urls
from vrobbler.apps.scrobbles.api.views import ( from vrobbler.apps.scrobbles.api.views import (
@ -60,14 +71,25 @@ from vrobbler.apps.trails import urls as trails_urls
from vrobbler.apps.trails.api.views import TrailViewSet from vrobbler.apps.trails.api.views import TrailViewSet
from vrobbler.apps.beers import urls as beers_urls from vrobbler.apps.beers import urls as beers_urls
from vrobbler.apps.beers.api.views import BeerViewSet, BeerProducerViewSet, BeerStyleViewSet from vrobbler.apps.beers.api.views import (
BeerViewSet,
BeerProducerViewSet,
BeerStyleViewSet,
)
from vrobbler.apps.puzzles import urls as puzzles_urls from vrobbler.apps.puzzles import urls as puzzles_urls
from vrobbler.apps.puzzles.api.views import PuzzleViewSet, PuzzleManufacturerViewSet from vrobbler.apps.puzzles.api.views import (
PuzzleViewSet,
PuzzleManufacturerViewSet,
)
from vrobbler.apps.videogames import urls as videogame_urls from vrobbler.apps.videogames import urls as videogame_urls
from vrobbler.apps.videos import urls as video_urls from vrobbler.apps.videos import urls as video_urls
from vrobbler.apps.videos.api.views import ChannelViewSet, SeriesViewSet, VideoViewSet from vrobbler.apps.videos.api.views import (
ChannelViewSet,
SeriesViewSet,
VideoViewSet,
)
from vrobbler.apps.bricksets import urls as bricksets_urls from vrobbler.apps.bricksets import urls as bricksets_urls
from vrobbler.apps.bricksets.api.views import BrickSetViewSet from vrobbler.apps.bricksets.api.views import BrickSetViewSet
@ -149,3 +171,10 @@ urlpatterns = [
"", scrobbles_views.RecentScrobbleList.as_view(), name="vrobbler-home" "", scrobbles_views.RecentScrobbleList.as_view(), name="vrobbler-home"
), ),
] ]
if settings.DEBUG:
urlpatterns = urlpatterns + static(
settings.STATIC_URL, document_root=settings.STATIC_ROOT
)
urlpatterns = urlpatterns + static(
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
)