diff --git a/backlogger/migrations/0010_item_steam_appid.py b/backlogger/migrations/0010_item_steam_appid.py new file mode 100644 index 0000000..b4eac20 --- /dev/null +++ b/backlogger/migrations/0010_item_steam_appid.py @@ -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), + ), + ] diff --git a/backlogger/models.py b/backlogger/models.py index 2391dc3..0d7b1e4 100644 --- a/backlogger/models.py +++ b/backlogger/models.py @@ -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) diff --git a/backlogger/templates/backlogger/list.html b/backlogger/templates/backlogger/list.html index ddd02ae..75b8490 100644 --- a/backlogger/templates/backlogger/list.html +++ b/backlogger/templates/backlogger/list.html @@ -202,7 +202,20 @@ {% if request.GET.imported %} ✓ {{ request.GET.imported }} game{{ request.GET.imported|pluralize }} imported {% endif %} - ▶ Steam + {% if request.GET.synced %} + ✓ {{ request.GET.synced }} game{{ request.GET.synced|pluralize }} synced + {% endif %} + {% if request.GET.sync_error %} + Steam sync failed + {% endif %} + ↻ Sync + ▶ Import + {% if debug %} +
+ {% csrf_token %} + +
+ {% endif %} + Add item @@ -229,6 +242,7 @@ + diff --git a/backlogger/urls.py b/backlogger/urls.py index efa6eff..20e2f43 100644 --- a/backlogger/urls.py +++ b/backlogger/urls.py @@ -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'), ] diff --git a/backlogger/views.py b/backlogger/views.py index 2732b85..624e6cb 100644 --- a/backlogger/views.py +++ b/backlogger/views.py @@ -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}")