This commit is contained in:
16
backlogger/migrations/0010_item_steam_appid.py
Normal file
16
backlogger/migrations/0010_item_steam_appid.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('backlogger', '0009_userprofile'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='steam_appid',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@@ -60,6 +60,9 @@ class Item(models.Model):
|
||||
watched = models.BooleanField(null=True, blank=True)
|
||||
duration_minutes = models.IntegerField(null=True, blank=True)
|
||||
|
||||
# Steam
|
||||
steam_appid = models.IntegerField(null=True, blank=True)
|
||||
|
||||
# HowLongToBeat estimates (games only)
|
||||
hltb_main = models.FloatField(null=True, blank=True)
|
||||
hltb_extra = models.FloatField(null=True, blank=True)
|
||||
|
||||
@@ -202,7 +202,20 @@
|
||||
{% if request.GET.imported %}
|
||||
<span style="font-size:0.82rem;color:#34d399">✓ {{ request.GET.imported }} game{{ request.GET.imported|pluralize }} imported</span>
|
||||
{% endif %}
|
||||
<a href="{% url 'backlogger:steam_login' %}" class="btn btn-outline" style="font-size:0.82rem">▶ Steam</a>
|
||||
{% if request.GET.synced %}
|
||||
<span style="font-size:0.82rem;color:#34d399">✓ {{ request.GET.synced }} game{{ request.GET.synced|pluralize }} synced</span>
|
||||
{% endif %}
|
||||
{% if request.GET.sync_error %}
|
||||
<span style="font-size:0.82rem;color:#f87171">Steam sync failed</span>
|
||||
{% endif %}
|
||||
<a href="{% url 'backlogger:steam_sync_login' %}" class="btn btn-outline" style="font-size:0.82rem" title="Sync hours played from Steam">↻ Sync</a>
|
||||
<a href="{% url 'backlogger:steam_login' %}" class="btn btn-outline" style="font-size:0.82rem">▶ Import</a>
|
||||
{% if debug %}
|
||||
<form method="post" action="{% url 'backlogger:debug_delete_all' %}" onsubmit="return confirm('Delete ALL items?')">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger" style="font-size:0.82rem">🗑 Delete all</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<a href="{% url 'backlogger:add' %}" class="btn btn-primary">+ Add item</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -229,6 +242,7 @@
|
||||
<option value="newest" {% if sort == 'newest' %}selected{% endif %}>Newest first</option>
|
||||
<option value="oldest" {% if sort == 'oldest' %}selected{% endif %}>Oldest first</option>
|
||||
<option value="progress" {% if sort == 'progress' %}selected{% endif %}>Most complete</option>
|
||||
<option value="updated" {% if sort == 'updated' %}selected{% endif %}>Recently updated</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,4 +12,7 @@ urlpatterns = [
|
||||
path('steam/login/', views.steam_login, name='steam_login'),
|
||||
path('steam/callback/', views.steam_callback, name='steam_callback'),
|
||||
path('steam/import/', views.steam_import, name='steam_import'),
|
||||
path('debug/delete-all/', views.debug_delete_all, name='debug_delete_all'),
|
||||
path('steam/sync/', views.steam_sync_login, name='steam_sync_login'),
|
||||
path('steam/sync/callback/', views.steam_sync_callback, name='steam_sync_callback'),
|
||||
]
|
||||
|
||||
@@ -28,6 +28,7 @@ SORT_MAP = {
|
||||
'newest': ['-created_at'],
|
||||
'oldest': ['created_at'],
|
||||
'progress': ['-progress_percent'],
|
||||
'updated': ['-updated_at'],
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +66,7 @@ def item_list(request):
|
||||
'sort': sort,
|
||||
'shelf': shelf,
|
||||
'categories': Item.CATEGORY_CHOICES,
|
||||
'debug': settings.DEBUG,
|
||||
})
|
||||
|
||||
|
||||
@@ -180,9 +182,58 @@ def steam_import(request):
|
||||
name=game['name'],
|
||||
hours_played=hours,
|
||||
progress_percent=progress,
|
||||
steam_appid=game['appid'],
|
||||
)
|
||||
hltb_api.apply_to_item(item)
|
||||
imported += 1
|
||||
|
||||
del request.session['steam_games']
|
||||
return redirect(f"{reverse('backlogger:list')}?category=games&imported={imported}")
|
||||
|
||||
|
||||
@login_required
|
||||
def debug_delete_all(request):
|
||||
if not settings.DEBUG:
|
||||
return redirect('backlogger:list')
|
||||
if request.method == 'POST':
|
||||
Item.objects.filter(user=request.user).delete()
|
||||
return redirect('backlogger:list')
|
||||
|
||||
|
||||
@login_required
|
||||
def steam_sync_login(request):
|
||||
callback = request.build_absolute_uri(reverse('backlogger:steam_sync_callback'))
|
||||
realm = f"{request.scheme}://{request.get_host()}"
|
||||
return redirect(steam_api.build_auth_url(callback, realm))
|
||||
|
||||
|
||||
@login_required
|
||||
def steam_sync_callback(request):
|
||||
steam_id = steam_api.verify_and_get_steam_id(request.GET.dict())
|
||||
if not steam_id:
|
||||
return redirect(f"{reverse('backlogger:list')}?sync_error=1")
|
||||
|
||||
api_key = getattr(settings, 'STEAM_API_KEY', '')
|
||||
if not api_key:
|
||||
return redirect(f"{reverse('backlogger:list')}?sync_error=1")
|
||||
|
||||
try:
|
||||
games = steam_api.get_owned_games(api_key, steam_id)
|
||||
except Exception:
|
||||
return redirect(f"{reverse('backlogger:list')}?sync_error=1")
|
||||
|
||||
hours_by_appid = {
|
||||
g['appid']: round(g.get('playtime_forever', 0) / 60, 1)
|
||||
for g in games
|
||||
}
|
||||
|
||||
steam_items = Item.objects.filter(user=request.user, steam_appid__isnull=False)
|
||||
synced = 0
|
||||
for item in steam_items:
|
||||
new_hours = hours_by_appid.get(item.steam_appid)
|
||||
if new_hours is not None and new_hours != item.hours_played:
|
||||
item.hours_played = new_hours
|
||||
item.save(update_fields=['hours_played', 'updated_at'])
|
||||
synced += 1
|
||||
|
||||
return redirect(f"{reverse('backlogger:list')}?category=games&synced={synced}")
|
||||
|
||||
Reference in New Issue
Block a user