utils.py 8.5 KB

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