utils.py 7.6 KB

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