Prechádzať zdrojové kódy

Get initial version of webretro to work

Colin Powell 3 rokov pred
rodič
commit
546523b13b

+ 1 - 1
.gitignore

@@ -1,3 +1,3 @@
 db.sqlite3
 emus.conf
-media/roms/virtualboy
+/media/

+ 7 - 2
emus/settings.py

@@ -26,7 +26,7 @@ SECRET_KEY = "l2-2d4dmvb0un0s)=5z%c87t*tg_hu&bt6*o^ks9r7f-3(mp$$"
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
 
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ["*"]
 
 
 # Application definition
@@ -39,6 +39,7 @@ INSTALLED_APPS = [
     "django.contrib.messages",
     "django.contrib.staticfiles",
     "django_extensions",
+    "emus",
     "games",
     "rest_framework",
 ]
@@ -53,6 +54,8 @@ MIDDLEWARE = [
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
 ]
 
+X_FRAME_OPTIONS = "SAMEORIGIN"
+
 ROOT_URLCONF = "emus.urls"
 
 TEMPLATES = [
@@ -121,7 +124,9 @@ USE_TZ = True
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/3.1/howto/static-files/
 
-STATIC_URL = "/static/"
+STATIC_URL = "static/"
+STATIC_ROOT = os.getenv("EMUS_STATIC_ROOT", os.path.join(BASE_DIR, "static"))
+
 MEDIA_URL = "/media/"
 MEDIA_ROOT = os.getenv("EMUS_MEDIA_ROOT", os.path.join(BASE_DIR, "media"))
 ROMS_DIR = os.path.join(MEDIA_ROOT, "roms")

+ 10 - 4
emus/urls.py

@@ -1,11 +1,13 @@
+from django.conf import settings
+from django.conf.urls.static import static
 from django.contrib import admin
 from django.urls import include, path
-from games import views as games_views
+from games import urls as games_urls
 from games.api.views import (
-    GameViewSet,
-    PublisherViewSet,
     DeveloperViewSet,
     GameSystemViewSet,
+    GameViewSet,
+    PublisherViewSet,
 )
 from rest_framework import routers
 
@@ -19,5 +21,9 @@ urlpatterns = [
     path("admin/", admin.site.urls),
     path("api-auth/", include("rest_framework.urls")),
     path("api/v1/", include(router.urls)),
-    path("", games_views.GameList.as_view()),
+    path("games/", include(games_urls, namespace="games")),
 ]
+
+if settings.DEBUG:
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

+ 0 - 18
emus/utils.py

@@ -1,25 +1,7 @@
-import os
 from enum import Enum
 
-from django.conf import settings
-
 
 class ChoiceEnum(Enum):
     @classmethod
     def choices(cls):
         return tuple((x.name, x.value) for x in cls)
-
-
-def skyscrape_console(game_system_slug):
-    scraper_config = settings.SCRAPER_CONFIG
-    binary = scraper_config["bin_path"]
-    site = scraper_config["site"]
-    user = scraper_config["user"]
-    password = scraper_config["pass"]
-    threads = scraper_config["threads"]
-
-    scrape_cmd = f"{binary} -s {site} -u {user}:{password} -t {threads} -f emulationstation -p {game_system_slug}"
-    load_cmd = f"{binary} -f emulationstation -p {game_system_slug}"
-    scrape_output = os.popen(scrape_cmd)
-    load_output = os.popen(load_cmd)
-    return scrape_output, load_output

+ 13 - 3
games/api/serializers.py

@@ -20,16 +20,26 @@ class GameSerializer(serializers.HyperlinkedModelSerializer):
 class DeveloperSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = Developer
-        fields = ("name",)
+        fields = (
+            "name",
+            "slug",
+        )
 
 
 class PublisherSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = Publisher
-        fields = ("name",)
+        fields = (
+            "name",
+            "slug",
+        )
 
 
 class GameSystemSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = GameSystem
-        fields = ("name", "retropie_slug")
+        fields = (
+            "name",
+            "retropie_slug",
+            "slug",
+        )

+ 14 - 0
games/management/commands/scrape_roms.py

@@ -0,0 +1,14 @@
+from django.core.management.base import BaseCommand, CommandError
+
+from games.utils import skyscrape_console
+
+
+class Command(BaseCommand):
+    help = "Scrape all games found in a given gamelist XML file"
+
+    def add_arguments(self, parser):
+        parser.add_argument("system", type=str)
+
+    def handle(self, *args, **options):
+        scrape_out, load_out = skyscrape_console(options["system"])
+        self.stdout.write(self.style.SUCCESS(f"Successfully scraped roms"))

+ 29 - 0
games/migrations/0007_alter_game_marquee_alter_game_region_and_more.py

@@ -0,0 +1,29 @@
+# Generated by Django 4.0.3 on 2022-04-01 02:15
+
+from django.db import migrations, models
+import games.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('games', '0006_alter_game_region'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='game',
+            name='marquee',
+            field=models.ImageField(blank=True, null=True, upload_to=games.models.get_marquee_upload_path),
+        ),
+        migrations.AlterField(
+            model_name='game',
+            name='region',
+            field=models.CharField(blank=True, choices=[('US', 'USA'), ('EU', 'Europe'), ('JP', 'Japan'), ('X', 'Unknown')], max_length=2, null=True),
+        ),
+        migrations.AlterField(
+            model_name='game',
+            name='screenshot',
+            field=models.ImageField(blank=True, null=True, upload_to=games.models.get_screenshot_upload_path),
+        ),
+    ]

+ 11 - 8
games/models.py

@@ -1,26 +1,26 @@
 from enum import Enum
 
-from django.conf import settings
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
+from django.urls import reverse
 from django_extensions.db.fields import AutoSlugField
 from emus.utils import ChoiceEnum
 
 
 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}"
 
 
 def get_marquee_upload_path(instance, filename):
-    return f"/{instance.game_system.retropie_slug}/marquee/{filename}"
+    return f"{instance.game_system.retropie_slug}/marquee/{filename}"
 
 
 def get_video_upload_path(instance, filename):
-    return f"/{instance.game_system.retropie_slug}/videos/{filename}"
+    return f"{instance.game_system.retropie_slug}/videos/{filename}"
 
 
 def get_rom_upload_path(instance, filename):
-    return f"/{instance.game_system.retropie_slug}/{filename}"
+    return f"{instance.game_system.retropie_slug}/{filename}"
 
 
 class Region(Enum):
@@ -39,7 +39,7 @@ class BaseModel(models.Model):
         abstract = True
 
     def slugify_function(self, content):
-        return content.replace("_", "-").lower()
+        return content.replace("_", "-").replace(" ", "-").lower()
 
     def __str__(self):
         return self.name
@@ -116,12 +116,12 @@ class Game(BaseModel):
         null=True,
         upload_to=get_video_upload_path,
     )
-    marquee = models.FileField(
+    marquee = models.ImageField(
         blank=True,
         null=True,
         upload_to=get_marquee_upload_path,
     )
-    screenshot = models.FileField(
+    screenshot = models.ImageField(
         blank=True,
         null=True,
         upload_to=get_screenshot_upload_path,
@@ -159,3 +159,6 @@ class Game(BaseModel):
 
     def __str__(self):
         return f"{self.name} for {self.game_system}"
+
+    def get_absolute_url(self):
+        return reverse("games:game_detail", args=[self.slug])

+ 4 - 2
games/urls.py

@@ -3,9 +3,11 @@ from games.api.views import GameViewSet
 from rest_framework import routers
 
 # importing views from views..py
-from .views import CodeList
+from .views import GameList, GameDetail
 
+app_name = "games"
 
 urlpatterns = [
-    path("", CodeList.as_view()),
+    path("", GameList.as_view(), name="game_list"),
+    path("<str:slug>/", GameDetail.as_view(), name="game_detail"),
 ]

+ 46 - 8
games/utils.py

@@ -1,5 +1,6 @@
 import os
 import xml.etree.ElementTree as ET
+import subprocess
 
 from dateutil import parser
 from django.conf import settings
@@ -17,10 +18,8 @@ EU_STRINGS = ["(e)", "eur", "europe", "pal"]
 
 def update_media_root_for_import(file_path):
     """Given a file path, re-write it for our app MEDIA_ROOT"""
-    split_path = file_path.split("/")
-    file_name = split_path[-1]
-    game_system_slug = split_path[-2]
-    return os.path.join(settings.ROMS_DIR, game_system_slug, file_name)
+    split_path = file_path.split("/media/")
+    return split_path[-1]
 
 
 def import_gamelist_file_to_db_for_system(game_system_slug, file_path=None):
@@ -86,11 +85,11 @@ def import_gamelist_file_to_db_for_system(game_system_slug, file_path=None):
         if developer_str:
             developer, _created = Developer.objects.get_or_create(name=developer_str)
         release_date = parser.parse(release_date_str) if release_date_str else None
-        screenshot_path = game.find("image").text
-        rom_path = game.find("path").text
-        video_path = game.find("video").text
-        marquee_path = game.find("marquee").text
         description = game.find("desc").text
+        screenshot_path = update_media_root_for_import(game.find("image").text)
+        rom_path = update_media_root_for_import(game.find("path").text)
+        video_path = update_media_root_for_import(game.find("video").text)
+        marquee_path = update_media_root_for_import(game.find("marquee").text)
 
         obj, created = Game.objects.get_or_create(name=name)
 
@@ -164,3 +163,42 @@ def export_gamelist_file_to_path_for_system(game_system_slug, file_path=None):
     tree.write(file_path, xml_declaration=True, encoding="utf-8")
 
     return exported_games, file_path
+
+
+def skyscrape_console(game_system_slug):
+    scraper_config = settings.SCRAPER_CONFIG
+    binary = scraper_config["bin_path"]
+    site = scraper_config["site"]
+    user = scraper_config["user"]
+    password = scraper_config["pass"]
+    threads = scraper_config["threads"]
+
+    scrape_cmd = f"{binary} -s {site} -u {user}:{password} -t {threads} -f emulationstation -p {game_system_slug}"
+    load_cmd = f"{binary} -f emulationstation -p {game_system_slug}"
+    scrape_output = subprocess.run(
+        [
+            binary,
+            "-s",
+            "{site}",
+            "-u",
+            f"{user}:{password}",
+            "-t",
+            f"{threads}",
+            "-f",
+            "emulationstation",
+            "-p",
+            f"{game_system_slug}",
+        ],
+        capture_output=True,
+    )
+    load_output = subprocess.run(
+        [
+            binary,
+            "-f",
+            "emulationstation",
+            "-p",
+            f"{game_system_slug}",
+        ],
+        capture_output=True,
+    )
+    return scrape_output, load_output

+ 6 - 2
games/views.py

@@ -1,9 +1,13 @@
 from django.shortcuts import render
-
-from django.views.generic import ListView
+from django.views.generic import DetailView, ListView
+from django.views.generic.base import TemplateView
 
 from .models import Game
 
 
 class GameList(ListView):
     model = Game
+
+
+class GameDetail(DetailView):
+    model = Game

+ 53 - 4
poetry.lock

@@ -68,6 +68,9 @@ category = "main"
 optional = false
 python-versions = ">=3.6"
 
+[package.dependencies]
+Django = ">=2.2"
+
 [[package]]
 name = "django-filter"
 version = "21.1"
@@ -121,6 +124,14 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
 [package.extras]
 testing = ["coverage", "pyyaml"]
 
+[[package]]
+name = "pillow"
+version = "9.0.1"
+description = "Python Imaging Library (Fork)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
 [[package]]
 name = "python-dateutil"
 version = "2.8.2"
@@ -198,7 +209,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.8"
-content-hash = "520150e397681966a3c125ac4086c99aa178c92dd8792184168d968c339b41a8"
+content-hash = "5cdcfb1d6774b0b207ca775bed942241f0a1c041c8a4ff2d223a2fbb12e3efeb"
 
 [metadata.files]
 asgiref = [
@@ -235,6 +246,10 @@ django = [
     {file = "Django-4.0.3-py3-none-any.whl", hash = "sha256:1239218849e922033a35d2a2f777cb8bee18bd725416744074f455f34ff50d0c"},
     {file = "Django-4.0.3.tar.gz", hash = "sha256:77ff2e7050e3324c9b67e29b6707754566f58514112a9ac73310f60cd5261930"},
 ]
+django-cors-headers = [
+    {file = "django-cors-headers-3.11.0.tar.gz", hash = "sha256:eb98389bf7a2afc5d374806af4a9149697e3a6955b5a2dc2bf049f7d33647456"},
+    {file = "django_cors_headers-3.11.0-py3-none-any.whl", hash = "sha256:a22be2befd4069c4fc174f11cf067351df5c061a3a5f94a01650b4e928b0372b"},
+]
 django-extensions = [
     {file = "django-extensions-3.1.5.tar.gz", hash = "sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a"},
     {file = "django_extensions-3.1.5-py3-none-any.whl", hash = "sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"},
@@ -243,9 +258,6 @@ django-filter = [
     {file = "django-filter-21.1.tar.gz", hash = "sha256:632a251fa8f1aadb4b8cceff932bb52fe2f826dd7dfe7f3eac40e5c463d6836e"},
     {file = "django_filter-21.1-py3-none-any.whl", hash = "sha256:f4a6737a30104c98d2e2a5fb93043f36dd7978e0c7ddc92f5998e85433ea5063"},
 ]
-django-restframework = [
-    {file = "django-restframework-0.0.1.tar.gz", hash = "sha256:2bec9265463693ada558ad9a58c31f6f3de005a932fbfeaadf983c5c9f3b7058"},
-]
 djangorestframework = [
     {file = "djangorestframework-3.13.1-py3-none-any.whl", hash = "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"},
     {file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"},
@@ -258,6 +270,43 @@ markdown = [
     {file = "Markdown-3.3.6-py3-none-any.whl", hash = "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"},
     {file = "Markdown-3.3.6.tar.gz", hash = "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006"},
 ]
+pillow = [
+    {file = "Pillow-9.0.1-1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4"},
+    {file = "Pillow-9.0.1-1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976"},
+    {file = "Pillow-9.0.1-1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9618823bd237c0d2575283f2939655f54d51b4527ec3972907a927acbcc5bfc"},
+    {file = "Pillow-9.0.1-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9bfdb82cdfeccec50aad441afc332faf8606dfa5e8efd18a6692b5d6e79f00fd"},
+    {file = "Pillow-9.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5100b45a4638e3c00e4d2320d3193bdabb2d75e79793af7c3eb139e4f569f16f"},
+    {file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:528a2a692c65dd5cafc130de286030af251d2ee0483a5bf50c9348aefe834e8a"},
+    {file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f29d831e2151e0b7b39981756d201f7108d3d215896212ffe2e992d06bfe049"},
+    {file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:855c583f268edde09474b081e3ddcd5cf3b20c12f26e0d434e1386cc5d318e7a"},
+    {file = "Pillow-9.0.1-cp310-cp310-win32.whl", hash = "sha256:d9d7942b624b04b895cb95af03a23407f17646815495ce4547f0e60e0b06f58e"},
+    {file = "Pillow-9.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81c4b81611e3a3cb30e59b0cf05b888c675f97e3adb2c8672c3154047980726b"},
+    {file = "Pillow-9.0.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:413ce0bbf9fc6278b2d63309dfeefe452835e1c78398efb431bab0672fe9274e"},
+    {file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80fe64a6deb6fcfdf7b8386f2cf216d329be6f2781f7d90304351811fb591360"},
+    {file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cef9c85ccbe9bee00909758936ea841ef12035296c748aaceee535969e27d31b"},
+    {file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d19397351f73a88904ad1aee421e800fe4bbcd1aeee6435fb62d0a05ccd1030"},
+    {file = "Pillow-9.0.1-cp37-cp37m-win32.whl", hash = "sha256:d21237d0cd37acded35154e29aec853e945950321dd2ffd1a7d86fe686814669"},
+    {file = "Pillow-9.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ede5af4a2702444a832a800b8eb7f0a7a1c0eed55b644642e049c98d589e5092"},
+    {file = "Pillow-9.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b5b3f092fe345c03bca1e0b687dfbb39364b21ebb8ba90e3fa707374b7915204"},
+    {file = "Pillow-9.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:335ace1a22325395c4ea88e00ba3dc89ca029bd66bd5a3c382d53e44f0ccd77e"},
+    {file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db6d9fac65bd08cea7f3540b899977c6dee9edad959fa4eaf305940d9cbd861c"},
+    {file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f154d173286a5d1863637a7dcd8c3437bb557520b01bddb0be0258dcb72696b5"},
+    {file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d4b1341ac07ae07eb2cc682f459bec932a380c3b122f5540432d8977e64eae"},
+    {file = "Pillow-9.0.1-cp38-cp38-win32.whl", hash = "sha256:effb7749713d5317478bb3acb3f81d9d7c7f86726d41c1facca068a04cf5bb4c"},
+    {file = "Pillow-9.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:7f7609a718b177bf171ac93cea9fd2ddc0e03e84d8fa4e887bdfc39671d46b00"},
+    {file = "Pillow-9.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:80ca33961ced9c63358056bd08403ff866512038883e74f3a4bf88ad3eb66838"},
+    {file = "Pillow-9.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c3c33ac69cf059bbb9d1a71eeaba76781b450bc307e2291f8a4764d779a6b28"},
+    {file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12875d118f21cf35604176872447cdb57b07126750a33748bac15e77f90f1f9c"},
+    {file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:514ceac913076feefbeaf89771fd6febde78b0c4c1b23aaeab082c41c694e81b"},
+    {file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c5c79ab7dfce6d88f1ba639b77e77a17ea33a01b07b99840d6ed08031cb2a7"},
+    {file = "Pillow-9.0.1-cp39-cp39-win32.whl", hash = "sha256:718856856ba31f14f13ba885ff13874be7fefc53984d2832458f12c38205f7f7"},
+    {file = "Pillow-9.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:f25ed6e28ddf50de7e7ea99d7a976d6a9c415f03adcaac9c41ff6ff41b6d86ac"},
+    {file = "Pillow-9.0.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:011233e0c42a4a7836498e98c1acf5e744c96a67dd5032a6f666cc1fb97eab97"},
+    {file = "Pillow-9.0.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253e8a302a96df6927310a9d44e6103055e8fb96a6822f8b7f514bb7ef77de56"},
+    {file = "Pillow-9.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6295f6763749b89c994fcb6d8a7f7ce03c3992e695f89f00b741b4580b199b7e"},
+    {file = "Pillow-9.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70"},
+    {file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"},
+]
 python-dateutil = [
     {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
     {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},

+ 1 - 0
pyproject.toml

@@ -15,6 +15,7 @@ colorlog = "^6.6.0"
 djangorestframework = "^3.13.1"
 Markdown = "^3.3.6"
 django-filter = "^21.1"
+Pillow = "^9.0.1"
 
 [tool.poetry.dev-dependencies]
 

+ 23 - 0
templates/games/game_detail.html

@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+{% load static %}
+
+{% block title %}{{object.name}}{% endblock %}
+
+{% block content %}
+    <img src={{object.marquee.url}} />
+    <img src={{object.screenshot.url}} />
+    <ul>
+        <li>{{object.game_system}}</li>
+        <li>{{object.publisher}}</li>
+        <li>{{object.developer}}</li>
+        <li>{{object.players}}</li>
+        <li><a href="{{object.rom_file.url}}">Download ROM</a></li>
+    </ul>
+    <div id="webretro-container" style="width:800px; height:600px">
+    </div>
+    <script type="text/javascript" src="{% static 'js/webretro/embed/embed.js' %}"></script>
+    <script>
+     webretroEmbed(document.getElementById("webretro-container"), "{% static 'js/webretro/index.html' %}", {system: "{{object.game_system.retropie_slug}}", rom:"{{request.scheme}}://{{request.META.HTTP_HOST}}{{object.rom_file.url}}"});
+    </script>
+
+{% endblock %}

+ 3 - 3
templates/games/game_list.html

@@ -1,12 +1,12 @@
 {% extends "base.html" %}
 
-{% block title %}Game list{% endblock %}
+{% block title %}Games{% endblock %}
 
 {% block content %}
 
     <ul>
     {% for game in object_list %}
-        <li>{{game}}</li>
+        <li><a href="{{game.get_absolute_url}}">{{game}}</a></li>
     {% endfor %}
-    </uk>
+    </ul>
 {% endblock %}