utils.py 7.8 KB

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