Browse Source

Add update_roms command and game system list view

Colin Powell 3 years ago
parent
commit
bf3af2041a

+ 7 - 3
emus/settings.py

@@ -36,9 +36,9 @@ CELERY_DEFAULT_QUEUE = "emus"
 CELERY_TASK_ALWAYS_EAGER = os.getenv("EMUS_SKIP_CELERY", False)
 CELERY_TASK_ALWAYS_EAGER = os.getenv("EMUS_SKIP_CELERY", False)
 CELERY_BROKER_URL = os.getenv("EMUS_CELERY_BROKER_URL", "memory://localhost/")
 CELERY_BROKER_URL = os.getenv("EMUS_CELERY_BROKER_URL", "memory://localhost/")
 CELERY_RESULT_BACKEND = "django-db"
 CELERY_RESULT_BACKEND = "django-db"
-CELERY_ACCEPT_CONTENT = ['application/json']
-CELERY_TASK_SERIALIZER = 'json'
-CELERY_RESULT_SERIALIZER = 'json'
+CELERY_ACCEPT_CONTENT = ["application/json"]
+CELERY_TASK_SERIALIZER = "json"
+CELERY_RESULT_SERIALIZER = "json"
 CELERY_TIMEZONE = os.getenv("EMUS_TIME_ZONE", "EST")
 CELERY_TIMEZONE = os.getenv("EMUS_TIME_ZONE", "EST")
 CELERY_TASK_TRACK_STARTED = True
 CELERY_TASK_TRACK_STARTED = True
 
 
@@ -257,6 +257,10 @@ GAME_SYSTEM_DEFAULTS = {
         "name": "Colecovision",
         "name": "Colecovision",
         "retroarch_core": "bluemsx",
         "retroarch_core": "bluemsx",
     },
     },
+    "daphne": {
+        "name": "Daphne",
+        "retroarch_core": "daphne",
+    },
     "dreamcast": {
     "dreamcast": {
         "name": "Dreamcast",
         "name": "Dreamcast",
         "color": "ED872D",
         "color": "ED872D",

+ 22 - 0
games/management/commands/update_roms.py

@@ -0,0 +1,22 @@
+from django.core.management.base import BaseCommand, CommandError
+from django.conf import settings
+
+from games.tasks import update_roms
+
+
+class Command(BaseCommand):
+    help = "Scrape + import all games for all systems"
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            "--full-scan",
+            action="store_true",
+            help="Update all files, even ones we already know about",
+        )
+
+    def handle(self, *args, **options):
+        all_slugs = settings.GAME_SYSTEM_DEFAULTS.keys()
+        update_roms(all_slugs, full_scan=options["full_scan"])
+        self.stdout.write(
+            self.style.SUCCESS("Successfully scraped and imported roms")
+        )

+ 14 - 4
games/models.py

@@ -1,16 +1,18 @@
+import logging
 import os
 import os
-from shlex import quote
-
 from enum import Enum
 from enum import Enum
+from shlex import quote
 
 
 from django.conf import settings
 from django.conf import settings
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db import models
 from django.urls import reverse
 from django.urls import reverse
-from django_extensions.db.models import TimeStampedModel
 from django_extensions.db.fields import AutoSlugField
 from django_extensions.db.fields import AutoSlugField
+from django_extensions.db.models import TimeStampedModel
 from emus.utils import ChoiceEnum
 from emus.utils import ChoiceEnum
 
 
+logger = logging.getLogger(__name__)
+
 
 
 def get_screenshot_upload_path(instance, filename):
 def get_screenshot_upload_path(instance, filename):
     return f"{instance.game_system.retropie_slug}/screenshots/{filename}"
     return f"{instance.game_system.retropie_slug}/screenshots/{filename}"
@@ -64,6 +66,7 @@ class StatisticsMixin(models.Model):
             return int(100 * avg)
             return int(100 * avg)
         return 0
         return 0
 
 
+
 class Genre(BaseModel, StatisticsMixin):
 class Genre(BaseModel, StatisticsMixin):
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse("games:genre_detail", args=[self.slug])
         return reverse("games:genre_detail", args=[self.slug])
@@ -255,6 +258,10 @@ class Game(BaseModel):
         if not self.rom_file:
         if not self.rom_file:
             return ""
             return ""
         rom_file = quote(self.rom_file.path)
         rom_file = quote(self.rom_file.path)
+        if not os.path.exists(self.retroarch_core_path):
+            logger.info(f"Missing libretro core file at {self.retroarch_core_path}")
+            return f"Libretro core not found at {self.retroarch_core_path}"
+
         return f"retroarch -L {self.retroarch_core_path} {rom_file} -v"
         return f"retroarch -L {self.retroarch_core_path} {rom_file} -v"
 
 
     def cmd(self, platform="linux"):
     def cmd(self, platform="linux"):
@@ -268,6 +275,7 @@ class Game(BaseModel):
             cmd = f"{emulator} --console --fullscreen --nogui {rom_file}"
             cmd = f"{emulator} --console --fullscreen --nogui {rom_file}"
         return cmd
         return cmd
 
 
+
 class GameCollection(BaseModel):
 class GameCollection(BaseModel):
     games = models.ManyToManyField(Game)
     games = models.ManyToManyField(Game)
     game_system = models.ForeignKey(
     game_system = models.ForeignKey(
@@ -314,7 +322,9 @@ class GameCollection(BaseModel):
 
 
         file_path = f"/tmp/custom-{self.slug}.cfg"
         file_path = f"/tmp/custom-{self.slug}.cfg"
         if not dryrun:
         if not dryrun:
-            file_path = os.path.join(settings.COLLECTIONS_DIR, f"custom-{self.slug}.cfg")
+            file_path = os.path.join(
+                settings.COLLECTIONS_DIR, f"custom-{self.slug}.cfg"
+            )
 
 
         with open(file_path, "w") as outfile:
         with open(file_path, "w") as outfile:
             for game in self.games.all():
             for game in self.games.all():

+ 5 - 0
games/urls.py

@@ -30,6 +30,11 @@ urlpatterns = [
         views.DeveloperList.as_view(),
         views.DeveloperList.as_view(),
         name="developer_list",
         name="developer_list",
     ),
     ),
+    path(
+        "game-system/",
+        views.GameSystemList.as_view(),
+        name="gamesystem_list",
+    ),
     path(
     path(
         "collection/",
         "collection/",
         views.GameCollectionList.as_view(),
         views.GameCollectionList.as_view(),

+ 7 - 2
games/views.py

@@ -40,7 +40,6 @@ class LibraryGameList(ListView, LoginRequiredMixin):
 
 
 
 
 class FilterableBaseListView(ListView, LoginRequiredMixin):
 class FilterableBaseListView(ListView, LoginRequiredMixin):
-
     def get_queryset(self, **kwargs):
     def get_queryset(self, **kwargs):
         order_by = self.request.GET.get("order_by", "name")
         order_by = self.request.GET.get("order_by", "name")
         queryset = super().get_queryset(**kwargs)
         queryset = super().get_queryset(**kwargs)
@@ -77,6 +76,10 @@ class GamePlayDetail(DetailView, LoginRequiredMixin):
     model = Game
     model = Game
 
 
 
 
+class GameSystemList(ListView, LoginRequiredMixin):
+    model = GameSystem
+
+
 class GameSystemDetail(DetailView, MultipleObjectMixin, LoginRequiredMixin):
 class GameSystemDetail(DetailView, MultipleObjectMixin, LoginRequiredMixin):
     model = GameSystem
     model = GameSystem
     paginate_by = 20
     paginate_by = 20
@@ -143,5 +146,7 @@ class GameCollectionDetail(DetailView, LoginRequiredMixin):
         else:
         else:
             object_list = object_list.order_by(F(order_by).asc(nulls_last=True))
             object_list = object_list.order_by(F(order_by).asc(nulls_last=True))
 
 
-        context = super(GameCollectionDetail, self).get_context_data(object_list=object_list, **kwargs)
+        context = super(GameCollectionDetail, self).get_context_data(
+            object_list=object_list, **kwargs
+        )
         return context
         return context

+ 2 - 1
templates/base.html

@@ -71,6 +71,7 @@
                 <li class="nav-item dropdown">
                 <li class="nav-item dropdown">
                     <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Systems</a>
                     <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Systems</a>
                     <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                     <div class="dropdown-menu" aria-labelledby="navbarDropdown">
+                        <a class="dropdown-item" href="{% url "games:gamesystem_list" %}">All</a>
                         {% for system in game_systems %}
                         {% for system in game_systems %}
                         <a class="dropdown-item" href="{{system.get_absolute_url}}">{{system.name}} ({{system.game_set.count}})</a>
                         <a class="dropdown-item" href="{{system.get_absolute_url}}">{{system.name}} ({{system.game_set.count}})</a>
                         {% endfor %}
                         {% endfor %}
@@ -79,7 +80,7 @@
                 <li class="nav-item dropdown">
                 <li class="nav-item dropdown">
                     <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Collections</a>
                     <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Collections</a>
                     <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                     <div class="dropdown-menu" aria-labelledby="navbarDropdown">
-			<a class="dropdown-item" href="{% url "games:gamecollection_list" %}">All</a>
+                        <a class="dropdown-item" href="{% url "games:gamecollection_list" %}">All</a>
                         {% for collection in game_collections %}
                         {% for collection in game_collections %}
                         <a class="dropdown-item" href="{{collection.get_absolute_url}}">{{collection.name}} ({{collection.games.count}})</a>
                         <a class="dropdown-item" href="{{collection.get_absolute_url}}">{{collection.name}} ({{collection.games.count}})</a>
                         {% endfor %}
                         {% endfor %}

+ 27 - 0
templates/games/gamesystem_list.html

@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+{% block page_title %}{{object}}{% endblock %}
+
+{% block title %}{{object}}{% endblock %}
+
+{% block content %}
+    <table class="table table-bordered table">
+    <thead>
+        <tr>
+        <th scope="col">#</th>
+        <th scope="col">Name</th>
+        <th scope="col">Games</th>
+        <th scope="col">Rating</th>
+        </tr>
+    </thead>
+    <tbody>
+        {% for  object in object_list %}
+        <tr>
+        <th scope="row"><a href="{{object.get_absolute_url}}">{{object.id}}</a></th>
+        <td>{{object.name}}</td>
+        <td>{{object.game_set.count}}</td>
+        <td>{{object.rating_avg}}/100</td>
+        </tr>
+        {% endfor %}
+    </tbody>
+    </table>
+{% endblock %}