Fix redundant tick field

This commit is contained in:
2023-03-12 10:54:09 -04:00
parent f6c1a459d4
commit 95b625cec2
31 changed files with 336 additions and 104 deletions

View File

@ -98,15 +98,13 @@ def process_koreader_sqlite_file(sqlite_file_path, user_id):
if created:
pages = book_row[KoReaderBookColumn.PAGES.value]
run_time = pages * book.AVG_PAGE_READING_SECONDS
run_time_ticks = run_time * 1000
book_dict = {
"title": book_row[KoReaderBookColumn.TITLE.value],
"pages": book_row[KoReaderBookColumn.PAGES.value],
"koreader_md5": book_row[KoReaderBookColumn.MD5.value],
"koreader_id": int(book_row[KoReaderBookColumn.ID.value]),
"koreader_authors": book_row[KoReaderBookColumn.AUTHORS.value],
"run_time": run_time,
"run_time_ticks": run_time_ticks,
"run_time_seconds": run_time,
}
Book.objects.filter(pk=book.id).update(**book_dict)
book.fix_metadata()
@ -114,10 +112,9 @@ def process_koreader_sqlite_file(sqlite_file_path, user_id):
if author_list:
book.authors.add(*[a.id for a in author_list])
playback_position = int(
playback_position_seconds = int(
book_row[KoReaderBookColumn.TOTAL_READ_TIME.value]
)
playback_position_ticks = playback_position * 1000
pages_read = int(book_row[KoReaderBookColumn.TOTAL_READ_PAGES.value])
timestamp = datetime.utcfromtimestamp(
book_row[KoReaderBookColumn.LAST_OPEN.value]
@ -129,8 +126,7 @@ def process_koreader_sqlite_file(sqlite_file_path, user_id):
user_id=user_id,
source="KOReader",
timestamp=timestamp,
playback_position_ticks=playback_position_ticks,
playback_position=playback_position,
playback_position_seconds=playback_position_seconds,
played_to_completion=True,
in_progress=False,
book_pages_read=pages_read,

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("books", "0008_book_first_sentence"),
]
operations = [
migrations.AlterField(
model_name="book",
name="run_time",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("books", "0009_alter_book_run_time"),
]
operations = [
migrations.RenameField(
model_name="book",
old_name="run_time",
new_name="run_time_seconds",
),
]

View File

@ -131,8 +131,9 @@ class Book(LongPlayScrobblableMixin):
self.cover.save(fname, ContentFile(r.content), save=True)
if self.pages:
self.run_time = self.pages * int(self.AVG_PAGE_READING_SECONDS)
self.run_time_ticks = int(self.run_time) * 1000
self.run_time_seconds = self.pages * int(
self.AVG_PAGE_READING_SECONDS
)
self.save()

View File

@ -51,7 +51,6 @@ class TrackAdmin(admin.ModelAdmin):
"title",
"album",
"artist",
"run_time",
"musicbrainz_id",
)
list_filter = ("album", "artist")

View File

@ -1,6 +1,5 @@
JELLYFIN_POST_KEYS = {
"ITEM_TYPE": "ItemType",
"RUN_TIME_TICKS": "RunTimeTicks",
"RUN_TIME": "RunTime",
"TITLE": "Name",
"TIMESTAMP": "UtcTimestamp",

View File

@ -107,13 +107,11 @@ class LastFM:
for scrobble in found_scrobbles:
run_time = None
run_time_ticks = None
mbid = None
artist = None
try:
run_time_ticks = scrobble.track.get_duration()
run_time = int(run_time_ticks / 1000)
run_time = int(scrobble.track.get_duration() / 1000)
mbid = scrobble.track.get_mbid()
artist = scrobble.track.get_artist().name
except pylast.MalformedResponseError as e:

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("music", "0014_album_theaudiodb_year_released"),
]
operations = [
migrations.AlterField(
model_name="track",
name="run_time",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("music", "0015_alter_track_run_time"),
]
operations = [
migrations.RenameField(
model_name="track",
old_name="run_time",
new_name="run_time_seconds",
),
]

View File

@ -42,7 +42,7 @@ def lookup_album_from_mb(musicbrainz_id: str) -> dict:
{
"title": recording["title"],
"musicbrainz_id": recording["id"],
"run_time_ticks": track["length"],
"run_time": track["length"] / 1000,
}
)

View File

@ -72,8 +72,7 @@ def get_or_create_track(
artist: Artist,
album: Album,
mbid: str = None,
run_time=None,
run_time_ticks=None,
run_time_seconds=None,
) -> Track:
track = None
if not mbid:
@ -91,8 +90,7 @@ def get_or_create_track(
artist=artist,
album=album,
musicbrainz_id=mbid,
run_time=run_time,
run_time_ticks=run_time_ticks,
run_time_seconds=run_time_seconds,
)
return track

View File

@ -27,7 +27,6 @@ class EpisodeAdmin(admin.ModelAdmin):
list_display = (
"title",
"podcast",
"run_time",
)
list_filter = ("podcast",)
ordering = ("-created",)

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("podcasts", "0005_alter_episode_options_alter_episode_title"),
]
operations = [
migrations.AlterField(
model_name="episode",
name="run_time",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("podcasts", "0006_alter_episode_run_time"),
]
operations = [
migrations.RenameField(
model_name="episode",
old_name="run_time",
new_name="run_time_seconds",
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("scrobbles", "0030_scrobble_notes"),
]
operations = [
migrations.AlterField(
model_name="scrobble",
name="playback_position",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("scrobbles", "0031_alter_scrobble_playback_position"),
]
operations = [
migrations.RenameField(
model_name="scrobble",
old_name="playback_position",
new_name="playback_position_seconds",
),
]

View File

@ -17,7 +17,7 @@ class ScrobblableMixin(TimeStampedModel):
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
title = models.CharField(max_length=255, **BNULL)
run_time = models.CharField(max_length=8, **BNULL)
run_time_seconds = models.IntegerField(**BNULL)
run_time_ticks = models.PositiveBigIntegerField(**BNULL)
class Meta:

View File

@ -406,7 +406,7 @@ class Scrobble(TimeStampedModel):
# Time keeping
timestamp = models.DateTimeField(**BNULL)
playback_position_ticks = models.PositiveBigIntegerField(**BNULL)
playback_position = models.CharField(max_length=10, **BNULL)
playback_position_seconds = models.IntegerField(**BNULL)
# Status indicators
is_paused = models.BooleanField(default=False)
@ -476,26 +476,25 @@ class Scrobble(TimeStampedModel):
if not self.media_obj:
return 0
if self.media_obj and not self.media_obj.run_time_ticks:
if self.media_obj and not self.media_obj.run_time_seconds:
return 100
if not self.playback_position_ticks and self.played_to_completion:
if not self.playback_position_seconds and self.played_to_completion:
return 100
playback_ticks = self.playback_position_ticks
if not playback_ticks:
playback_ticks = (timezone.now() - self.timestamp).seconds * 1000
playback_seconds = self.playback_position_seconds
if not playback_seconds:
playback_seconds = (timezone.now() - self.timestamp).seconds
percent = int((playback_ticks / self.media_obj.run_time_ticks) * 100)
run_time_secs = self.media_obj.run_time_seconds
percent = int((playback_seconds / run_time_secs) * 100)
if self.is_long_play:
run_time_secs = int(self.media_obj.run_time)
playback_secs = (timezone.now() - self.timestamp).seconds
long_play_secs = 0
if self.previous and not self.previous.long_play_complete:
long_play_secs = self.previous.long_play_seconds or 0
percent = int(
((playback_secs + long_play_secs) / run_time_secs) * 100
((playback_seconds + long_play_secs) / run_time_secs) * 100
)
# if percent > 100:
@ -635,9 +634,7 @@ class Scrobble(TimeStampedModel):
if class_name in LONG_PLAY_MEDIA.values():
now = timezone.now()
self.played_to_completion = True
self.playback_position = (now - self.timestamp).seconds
# TODO Refactor ticks outta here, this is silly
self.playback_position_ticks = int(self.playback_position) * 1000
self.playback_position_seconds = (now - self.timestamp).seconds
media_filter = models.Q(video_game=self.video_game)
if class_name == "Book":
@ -650,16 +647,15 @@ class Scrobble(TimeStampedModel):
played_to_completion=True,
long_play_complete=False,
).last()
self.long_play_seconds = self.playback_position
self.long_play_seconds = self.playback_position_seconds
if last_scrobble:
self.long_play_seconds = int(
last_scrobble.playback_position
) + int(self.playback_position)
last_scrobble.playback_position_seconds
) + int(self.playback_position_seconds)
self.save(
update_fields=[
"playback_position",
"playback_position_ticks",
"playback_position_seconds",
"played_to_completion",
"long_play_seconds",
]
@ -688,11 +684,8 @@ class Scrobble(TimeStampedModel):
self.delete()
def update_ticks(self, data) -> None:
self.playback_position_ticks = data.get("playback_position_ticks")
self.playback_position = data.get("playback_position")
self.playback_position_seconds = data.get("playback_position_seconds")
logger.info(
f"{self.id} - {self.playback_position_ticks} - {self.source}"
)
self.save(
update_fields=["playback_position_ticks", "playback_position"]
f"{self.id} - {self.playback_position_seconds} - {self.source}"
)
self.save(update_fields=["playback_position_seconds"])

View File

@ -37,8 +37,7 @@ def mopidy_scrobble_podcast(
episode_name = parsed_data.get("episode_filename")
episode_dict = {
"title": episode_name,
"run_time_ticks": data_dict.get("run_time_ticks"),
"run_time": data_dict.get("run_time"),
"run_time_seconds": data_dict.get("run_time"),
"number": parsed_data.get("episode_num"),
"pub_date": parsed_data.get("pub_date"),
"mopidy_uri": mopidy_uri,
@ -50,7 +49,7 @@ def mopidy_scrobble_podcast(
mopidy_data = {
"user_id": user_id,
"timestamp": timezone.now(),
"playback_position_ticks": data_dict.get("playback_time_ticks"),
"playback_position_seconds": data_dict.get("playback_time_ticks"),
"source": "Mopidy",
"mopidy_status": data_dict.get("status"),
}
@ -78,15 +77,15 @@ def mopidy_scrobble_track(
mbid=data_dict.get("musicbrainz_track_id"),
artist=artist,
album=album,
run_time_ticks=data_dict.get("run_time_ticks"),
run_time=data_dict.get("run_time"),
run_time_seconds=data_dict.get("run_time"),
)
# Now we run off a scrobble
playback_seconds = data_dict.get("playback_time_ticks") / 1000
mopidy_data = {
"user_id": user_id,
"timestamp": timezone.now(),
"playback_position_ticks": data_dict.get("playback_time_ticks"),
"playback_position_seconds": playback_seconds,
"source": "Mopidy",
"mopidy_status": data_dict.get("status"),
}
@ -103,15 +102,10 @@ def build_scrobble_dict(data_dict: dict, user_id: int) -> dict:
elif data_dict.get("NotificationType") == "PlaybackStop":
jellyfin_status = "stopped"
playback_ticks = data_dict.get("PlaybackPositionTicks", "")
if playback_ticks:
playback_ticks = playback_ticks // 10000
return {
"user_id": user_id,
"timestamp": parse(data_dict.get("UtcTimestamp")),
"playback_position_ticks": playback_ticks,
"playback_position": data_dict.get("PlaybackPosition", ""),
"playback_position_seconds": data_dict.get("PlaybackPosition", ""),
"source": data_dict.get("ClientName", "Vrobbler"),
"source_id": data_dict.get("MediaSourceId"),
"jellyfin_status": jellyfin_status,
@ -142,9 +136,6 @@ def jellyfin_scrobble_track(
mbid=data_dict.get(JELLYFIN_POST_KEYS["ALBUM_MB_ID"]),
)
run_time_ticks = (
data_dict.get(JELLYFIN_POST_KEYS["RUN_TIME_TICKS"]) // 10000
)
run_time = convert_to_seconds(
data_dict.get(JELLYFIN_POST_KEYS["RUN_TIME"])
)
@ -152,15 +143,13 @@ def jellyfin_scrobble_track(
title=data_dict.get("Name"),
artist=artist,
album=album,
run_time_ticks=run_time_ticks,
run_time=run_time,
run_time_seconds=run_time,
)
scrobble_dict = build_scrobble_dict(data_dict, user_id)
# A hack to make Jellyfin work more like Mopidy for music tracks
scrobble_dict["playback_position_ticks"] = 0
scrobble_dict["playback_position"] = ""
scrobble_dict["playback_position_seconds"] = 0
return Scrobble.create_or_update(track, user_id, scrobble_dict)
@ -210,8 +199,7 @@ def manual_scrobble_video_game(data_dict: dict, user_id: Optional[int]):
scrobble_dict = {
"user_id": user_id,
"timestamp": timezone.now(),
"playback_position_ticks": None, # int(start_playback_position) * 1000,
"playback_position": 0,
"playback_position_seconds": 0,
"source": "Vrobbler",
"long_play_complete": False,
}
@ -225,8 +213,7 @@ def manual_scrobble_book(data_dict: dict, user_id: Optional[int]):
scrobble_dict = {
"user_id": user_id,
"timestamp": timezone.now(),
"playback_position_ticks": None,
"playback_position": 0,
"playback_position_seconds": 0,
"source": "Vrobbler",
"long_play_complete": False,
}

View File

@ -78,18 +78,16 @@ def check_scrobble_for_finish(
completion_percent = scrobble.media_obj.COMPLETION_PERCENT
if scrobble.percent_played >= completion_percent or force_finish:
logger.info(f"{scrobble.id} {completion_percent} met, finishing")
if (
scrobble.playback_position_ticks
and scrobble.media_obj.run_time_ticks
and force_to_100
scrobble.playback_position_seconds
and scrobble.media_obj.run_time_seconds
):
scrobble.playback_position_ticks = (
scrobble.media_obj.run_time_ticks
logger.info(f"{scrobble.id} {completion_percent} met, finishing")
scrobble.playback_position_seconds = (
scrobble.media_obj.run_time_seconds
)
logger.info(
f"{scrobble.playback_position_ticks} set to {scrobble.media_obj.run_time_ticks}"
f"{scrobble.playback_position_seconds} set to {scrobble.media_obj.run_time_seconds}"
)
scrobble.in_progress = False
@ -101,7 +99,7 @@ def check_scrobble_for_finish(
"in_progress",
"is_paused",
"played_to_completion",
"playback_position_ticks",
"playback_position_seconds",
]
)

View File

@ -0,0 +1,46 @@
# Generated by Django 4.1.5 on 2023-03-12 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("sports", "0008_sportevent_thesportsdb_id"),
]
operations = [
migrations.AlterField(
model_name="sport",
name="default_event_type",
field=models.CharField(
choices=[
("UK", "Event"),
("GA", "Game"),
("RA", "Race"),
("MA", "Match"),
],
default="UK",
max_length=2,
),
),
migrations.AlterField(
model_name="sportevent",
name="event_type",
field=models.CharField(
choices=[
("UK", "Event"),
("GA", "Game"),
("RA", "Race"),
("MA", "Match"),
],
default="UK",
max_length=2,
),
),
migrations.AlterField(
model_name="sportevent",
name="run_time",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("sports", "0009_alter_sport_default_event_type_and_more"),
]
operations = [
migrations.RenameField(
model_name="sportevent",
old_name="run_time",
new_name="run_time_seconds",
),
]

View File

@ -41,9 +41,6 @@ class Sport(TheSportsDbMixin):
default=SportEventType.UNKNOWN,
)
# TODO Add these to the default run_time for Football
# run_time_seconds = 11700
# run_time_ticks = run_time_seconds * 1000
@property
def default_event_run_time_ticks(self):
default_run_time = getattr(
@ -51,7 +48,7 @@ class Sport(TheSportsDbMixin):
)
if self.default_event_run_time:
default_run_time = self.default_event_run_time
return default_run_time * 1000
return default_run_time
class League(TheSportsDbMixin):
@ -231,8 +228,7 @@ class SportEvent(ScrobblableMixin):
"player_two": player_two,
"start": data_dict["Start"],
"round": round,
"run_time_ticks": data_dict.get("RunTimeTicks"),
"run_time": data_dict.get("RunTime", ""),
"run_time_seconds": data_dict.get("RunTime", 0),
}
event, _created = cls.objects.get_or_create(**event_dict)

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("videogames", "0006_videogame_summary_alter_videogame_platforms"),
]
operations = [
migrations.AlterField(
model_name="videogame",
name="run_time",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("videogames", "0007_alter_videogame_run_time"),
]
operations = [
migrations.RenameField(
model_name="videogame",
old_name="run_time",
new_name="run_time_seconds",
),
]

View File

@ -145,9 +145,8 @@ class VideoGame(LongPlayScrobblableMixin):
load_game_data_from_igdb(self.id)
if (not self.run_time_ticks or force_update) and self.main_story_time:
self.run_time_ticks = self.main_story_time * 1000 # miliseconds
self.run_time = self.main_story_time
self.save(update_fields=["run_time_ticks", "run_time"])
self.run_time_seconds = self.main_story_time
self.save(update_fields=["run_time_seconds"])
@classmethod
def find_or_create(cls, data_dict: dict) -> "Game":

View File

@ -22,9 +22,6 @@ def lookup_video_from_imdb(imdb_id: str) -> dict:
if runtimes:
run_time_seconds = int(runtimes[0]) * 60
# Ticks otherwise known as miliseconds
run_time_ticks = run_time_seconds * 1000 * 1000
item_type = "Movie"
if media.get("series title"):
item_type = "Episode"
@ -45,7 +42,6 @@ def lookup_video_from_imdb(imdb_id: str) -> dict:
"Year": media.get("year"),
"Provider_imdb": imdb_id,
"RunTime": run_time_seconds,
"RunTimeTicks": run_time_ticks,
"SeriesName": media.get("series title"),
"EpisodeNumber": media.get("episode"),
"SeasonNumber": media.get("season"),

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("videos", "0006_alter_video_year"),
]
operations = [
migrations.AlterField(
model_name="video",
name="run_time",
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.5 on 2023-03-12 01:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("videos", "0007_alter_video_run_time"),
]
operations = [
migrations.RenameField(
model_name="video",
old_name="run_time",
new_name="run_time_seconds",
),
]

View File

@ -4,9 +4,9 @@
{% block head_extra %}
<style>
dl { width: 260px; float:left; margin-right: 10px; }
dl { width: 210px; float:left; margin-right: 10px; }
dt a { color:white; text-decoration: none; font-size:smaller; }
img { height:200px; width: 250px; object-fit: cover; }
img { height:200px; width: 200px; object-fit: cover; }
dd .right { float:right; }
</style>
{% endblock %}
@ -18,25 +18,18 @@
{% if view == 'grid' %}
<div>
{% for media in in_progress %}
{% if media.hltb_cover %}
<dl>
<dt><a href="{{media.get_absolute_url}}">{{media.title}}</a></dt>
{% if media.hltb_cover %}
<dd><a href="{{media.get_absolute_url}}"><img src="{{media.hltb_cover.url}}" width=200 height=200 /></a></dd>
<dd>
<a type="button" class="btn btn-sm btn-primary" href="{{media.get_start_url}}">Resume</a>
<a type="button" class="right btn btn-sm " href="{{media.get_longplay_finish_url}}">Finish</a>
</dd>
</dl>
{% elif media.cover %}
<dl>
<dt><a href="{{media.get_absolute_url}}">{{media.title}}</a></dt>
{% else %}
<dd><a href="{{media.get_absolute_url}}"><img src="{{media.cover.url}}" style="width: 200px; height: 200px; object-fit:cover; " /></a></dd>
{% endif %}
<dd>
<a type="button" class="btn btn-sm btn-primary" href="{{media.get_start_url}}">Resume</a>
{% if media.is_long_play_in_progress %}Playing{% else %}<a type="button" class="btn btn-sm btn-primary" href="{{media.get_start_url}}">Resume</a>{% endif %}
<a type="button" class="right btn btn-sm " href="{{media.get_longplay_finish_url}}">Finish</a>
</dd>
</dl>
{% endif %}
{% endfor %}
</div>
{% else %}

View File

@ -62,7 +62,7 @@
<tr>
<td>{{scrobble.timestamp}}</td>
<td>{% if scrobble.long_play_complete == True %}Yes{% endif %}</td>
<td>{% if scrobble.in_progress %}Now playing{% else %}{{scrobble.playback_position|natural_duration}}{% endif %}</td>
<td>{% if scrobble.in_progress %}Now playing{% else %}{{scrobble.playback_position_seconds|natural_duration}}{% endif %}</td>
<td>{% for platform in scrobble.video_game.platforms.all %}<a href="{{platform.get_absolute_url}}">{{platform}}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</td>
</tr>
{% endfor %}