slidegen.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/env python3
  2. """
  3. Copyright © 2022 Noah Vogt <noah@noahvogt.com>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. """
  15. import re
  16. import sys
  17. import colorama
  18. from wand.image import Image
  19. from utils import (
  20. log,
  21. error_msg,
  22. get_songtext_by_structure,
  23. structure_as_list,
  24. get_unique_structure_elements,
  25. )
  26. from slides import (
  27. ClassicSongTemplate,
  28. ClassicStartSlide,
  29. ClassicSongSlide,
  30. generate_start_slide,
  31. generate_song_slides,
  32. generate_song_template,
  33. )
  34. import config as const
  35. from prompt import parse_input
  36. class Slidegen:
  37. def __init__(
  38. self, song_template_form, start_slide_form, song_slide_form
  39. ) -> None:
  40. self.metadata: dict = {"": ""}
  41. self.songtext: dict = {"": ""}
  42. self.song_file_path: str = ""
  43. self.song_file_content: list = []
  44. self.output_dir: str = ""
  45. self.chosen_structure: list | str = ""
  46. self.generated_slides: list = []
  47. self.song_template_form = song_template_form
  48. self.start_slide_form = start_slide_form
  49. self.song_slide_form = song_slide_form
  50. self.parse_argv()
  51. def execute(self) -> None:
  52. self.parse_file()
  53. self.calculate_desired_structures()
  54. self.generate_slides()
  55. def count_number_of_slides_to_be_generated(self) -> int:
  56. slide_count: int = 0
  57. for structure in self.chosen_structure:
  58. line_count: int = len(self.songtext[structure].splitlines())
  59. if line_count > const.STRUCTURE_ELEMENT_MAX_LINES:
  60. slide_count += (
  61. line_count // const.STRUCTURE_ELEMENT_MAX_LINES + 1
  62. )
  63. else:
  64. slide_count += 1
  65. return slide_count
  66. def generate_slides(self) -> None:
  67. template_img: Image = generate_song_template(self)
  68. slide_count: int = self.count_number_of_slides_to_be_generated()
  69. zfill_length: int = len(str(slide_count))
  70. generate_start_slide(self, template_img, zfill_length)
  71. generate_song_slides(self, slide_count, template_img, zfill_length)
  72. def parse_file(self) -> None:
  73. self.parse_metadata()
  74. self.parse_songtext()
  75. def parse_metadata(self) -> None:
  76. metadata_dict = dict.fromkeys(const.METADATA_STRINGS)
  77. try:
  78. with open(self.song_file_path, mode="r", encoding="utf8") as opener:
  79. content = opener.readlines()
  80. except IOError:
  81. error_msg(
  82. "could not read the the song input file: '{}'".format(
  83. self.song_file_path
  84. )
  85. )
  86. valid_metadata_strings = list(const.METADATA_STRINGS)
  87. for line_nr, line in enumerate(content):
  88. if len(valid_metadata_strings) == 0:
  89. content = content[line_nr:]
  90. break
  91. if not re.match(
  92. r"^(?!structure)\S+: .+|^structure: ([0-9]+|R)(,([0-9]+|R))*$",
  93. line,
  94. ):
  95. if line[-1] == "\n":
  96. line = line[:-1]
  97. missing_metadata_strs = ""
  98. for metadata_str in valid_metadata_strings:
  99. missing_metadata_strs += ", " + metadata_str
  100. missing_metadata_strs = missing_metadata_strs[2:]
  101. error_msg(
  102. "invalid metadata syntax on line {}:\n{}\nThe ".format(
  103. line_nr + 1, line
  104. )
  105. + "following metadata strings are still missing: {}".format(
  106. missing_metadata_strs
  107. )
  108. )
  109. metadata_str = line[: line.index(":")]
  110. if metadata_str in valid_metadata_strings:
  111. metadata_dict[metadata_str] = line[line.index(": ") + 2 : -1]
  112. valid_metadata_strings.remove(metadata_str)
  113. continue
  114. error_msg("invalid metadata string '{}'".format(metadata_str))
  115. self.metadata = metadata_dict
  116. self.song_file_content = content
  117. def parse_songtext(self) -> None:
  118. unique_structures = get_unique_structure_elements(
  119. structure_as_list(self.metadata["structure"])
  120. )
  121. output_dict = dict.fromkeys(unique_structures)
  122. for structure in unique_structures:
  123. output_dict[structure] = get_songtext_by_structure(
  124. self.song_file_content, structure
  125. )
  126. self.songtext = output_dict
  127. def calculate_desired_structures(self) -> None:
  128. self.chosen_structure: list | str = parse_input(
  129. str(self.metadata["structure"]), self.chosen_structure
  130. )
  131. def parse_argv(self) -> None:
  132. try:
  133. self.song_file_path = sys.argv[1]
  134. self.output_dir = sys.argv[2]
  135. except IndexError:
  136. error_msg("incorrect amount of arguments provided, exiting...")
  137. try:
  138. self.chosen_structure = sys.argv[3]
  139. if self.chosen_structure.strip() == "":
  140. self.chosen_structure = ""
  141. except IndexError:
  142. self.chosen_structure = ""
  143. log("parsing {}...".format(self.song_file_path))
  144. def main() -> None:
  145. colorama.init()
  146. slidegen: Slidegen = Slidegen(
  147. ClassicSongTemplate, ClassicStartSlide, ClassicSongSlide
  148. )
  149. slidegen.execute()
  150. if __name__ == "__main__":
  151. main()