utils.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import os
  2. import xml.etree.ElementTree as ET
  3. import subprocess
  4. from dateutil import parser
  5. from django.conf import settings
  6. from .models import Developer, Game, GameSystem, Genre, Publisher
  7. import logging
  8. logger = logging.Logger(__name__)
  9. US_STRINGS = ["(u)", "(usa)", "(us)"]
  10. JP_STRINGS = ["(j)", "japan", "jp", "jpn"]
  11. EU_STRINGS = ["(e)", "eur", "europe", "pal"]
  12. def update_media_root_for_import(file_path):
  13. """Given a file path, re-write it for our app MEDIA_ROOT"""
  14. suffix = ""
  15. if file_path:
  16. suffix = file_path.split("/media/")[-1]
  17. return suffix
  18. def import_gamelist_file_to_db_for_system(
  19. game_system_slug, file_path=None, full_scan=False
  20. ):
  21. imported_games = []
  22. not_imported_games = []
  23. if not file_path:
  24. file_path = os.path.join(settings.ROMS_DIR, game_system_slug, "gamelist.xml")
  25. if not os.path.exists(file_path):
  26. logger.info(
  27. "File path for {game_system_slug} had no gamelist.xml file, run a scraper first!"
  28. )
  29. return
  30. gamelist = ET.parse(file_path)
  31. game_system = GameSystem.objects.filter(retropie_slug=game_system_slug).first()
  32. if not game_system:
  33. defaults = settings.GAME_SYSTEM_DEFAULTS.get(game_system_slug, None)
  34. full_system_name = game_system_slug
  35. if defaults:
  36. full_system_name = defaults.get("name", game_system_slug)
  37. game_system = GameSystem.objects.create(
  38. name=full_system_name, retropie_slug=game_system_slug
  39. )
  40. games = gamelist.findall("game")
  41. for game in games:
  42. name = game.find("name").text
  43. game_path = game.find("path").text.lower()
  44. obj, created = Game.objects.get_or_create(name=name, game_system=game_system)
  45. if not created and not full_scan:
  46. not_imported_games.append(game)
  47. logger.info(f"Found game {game} and not doing full scan, so skipping")
  48. continue
  49. english_patched = "patched" in game_path
  50. undub = "undub" in game_path
  51. hack = "hack" in game_path
  52. region = None
  53. if any(us in game_path for us in US_STRINGS):
  54. region = Game.Region.US.name
  55. if any(jp in game_path for jp in JP_STRINGS):
  56. region = Game.Region.JP.name
  57. if any(eu in game_path for eu in EU_STRINGS):
  58. region = Game.Region.EU.name
  59. release_date_str = game.find("releasedate").text
  60. developer_str = game.find("developer").text
  61. publisher_str = game.find("publisher").text
  62. genres_str = game.find("genre").text
  63. rating_str = game.find("rating").text
  64. players_str = "1"
  65. if game.find("players"):
  66. players_str = game.find("players").text
  67. try:
  68. kid_game = game.find("kidgame").text == "true"
  69. except AttributeError:
  70. kid_game = False
  71. genre_list = []
  72. if genres_str:
  73. genre_str_list = genres_str.split(", ")
  74. for genre_str in genre_str_list:
  75. genre, _created = Genre.objects.get_or_create(name=genre_str)
  76. genre_list.append(genre)
  77. players = int(players_str) if players_str else 1
  78. rating = float(rating_str) if rating_str else None
  79. publisher = None
  80. if publisher_str:
  81. publisher, _created = Publisher.objects.get_or_create(name=publisher_str)
  82. developer = None
  83. if developer_str:
  84. developer, _created = Developer.objects.get_or_create(name=developer_str)
  85. release_date = parser.parse(release_date_str) if release_date_str else None
  86. description = game.find("desc").text
  87. screenshot_path = update_media_root_for_import(game.find("image").text)
  88. rom_path = update_media_root_for_import(game.find("path").text)
  89. video_path_elem = game.find("video")
  90. video_path = ""
  91. if video_path_elem:
  92. video_path = update_media_root_for_import(video_path_elem.text)
  93. marquee_path = update_media_root_for_import(game.find("marquee").text)
  94. obj.game_system = game_system
  95. obj.developer = developer
  96. obj.publisher = publisher
  97. obj.players = players
  98. obj.description = description
  99. obj.release_date = release_date
  100. obj.rating = rating
  101. obj.genre.set(genre_list)
  102. obj.screenshot = screenshot_path
  103. obj.rom_file = rom_path
  104. obj.video = video_path
  105. obj.marquee = marquee_path
  106. obj.kid_game = kid_game
  107. obj.english_patched = english_patched
  108. obj.hack = hack
  109. obj.undub = undub
  110. obj.region = region
  111. obj.save()
  112. imported_games.append(game)
  113. return {"imported": imported_games, "not_imported": not_imported_games}
  114. def export_gamelist_file_to_path_for_system(game_system_slug, file_path=None):
  115. exported_games = []
  116. game_system = GameSystem.objects.get(retropie_slug=game_system_slug)
  117. if not file_path:
  118. file_path = f"/tmp/{game_system_slug}-gamelist.xml"
  119. # file_path = os.path.join(settings.ROMS_DIR, game_system_slug, "gamelist.xml")
  120. root = ET.Element("gameList")
  121. tree = ET.ElementTree(root)
  122. tree.write(file_path)
  123. games = Game.objects.filter(game_system=game_system)
  124. for game in games:
  125. game_node = ET.SubElement(root, "game")
  126. genre_str = ", ".join(game.genre.all().values_list("name", flat=True))
  127. release_date_str = ""
  128. if game.release_date:
  129. release_date_str = game.release_date.strftime("%Y%m%dT00000")
  130. ET.SubElement(game_node, "path").text = (
  131. game.rom_file.path if game.rom_file else ""
  132. )
  133. ET.SubElement(game_node, "name").text = game.name
  134. ET.SubElement(game_node, "thumbnail").text = ""
  135. ET.SubElement(game_node, "image").text = (
  136. game.screenshot.path if game.screenshot else ""
  137. )
  138. ET.SubElement(game_node, "marquee").text = (
  139. game.marquee.path if game.marquee else ""
  140. )
  141. ET.SubElement(game_node, "video").text = game.video.path if game.video else ""
  142. ET.SubElement(game_node, "rating").text = str(game.rating)
  143. ET.SubElement(game_node, "desc").text = game.description
  144. ET.SubElement(game_node, "releasedate").text = release_date_str
  145. ET.SubElement(game_node, "developer").text = str(game.developer)
  146. ET.SubElement(game_node, "publisher").text = str(game.publisher)
  147. ET.SubElement(game_node, "genre").text = genre_str
  148. ET.SubElement(game_node, "players").text = str(game.players)
  149. if game.kid_game:
  150. ET.SubElement(game_node, "kidgame").text = "true"
  151. exported_games.append(game)
  152. tree = ET.ElementTree(root)
  153. tree.write(file_path, xml_declaration=True, encoding="utf-8")
  154. return exported_games, file_path
  155. def skyscrape_console(game_system_slug):
  156. scraper_config = settings.SCRAPER_CONFIG_FILE
  157. scraper_binary = settings.SCRAPER_BIN_PATH
  158. scraper_site = settings.SCRAPER_SITE
  159. # If the config file is relative, append our base dir
  160. if scraper_config[0] != "/":
  161. scraper_config = os.path.join(settings.BASE_DIR, scraper_config)
  162. if not os.path.exists(scraper_config):
  163. logger.info(f"Config file not found at {scraper_config}")
  164. return
  165. logger.info(f"Scraping game info using configuration file from {scraper_config}")
  166. scrape_output = subprocess.run(
  167. [
  168. scraper_binary,
  169. "-c",
  170. f"{scraper_config}",
  171. "-s",
  172. f"{scraper_site}",
  173. "-f",
  174. "emulationstation",
  175. "-p",
  176. f"{game_system_slug}",
  177. ],
  178. capture_output=True,
  179. )
  180. load_output = subprocess.run(
  181. [
  182. scraper_binary,
  183. "-c",
  184. f"{scraper_config}",
  185. "-f",
  186. "emulationstation",
  187. "-p",
  188. f"{game_system_slug}",
  189. ],
  190. capture_output=True,
  191. )
  192. # TODO We should progressively pipe output to a log file instead of this
  193. print(scrape_output)
  194. print(load_output)
  195. return scrape_output, load_output