generate_slides.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright © 2024 Noah Vogt <noah@noahvogt.com>
  2. # This program is free software: you can redistribute it and/or modify
  3. # it under the terms of the GNU General Public License as published by
  4. # the Free Software Foundation, either version 3 of the License, or
  5. # (at your option) any later version.
  6. # This program is distributed in the hope that it will be useful,
  7. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. # GNU General Public License for more details.
  10. # You should have received a copy of the GNU General Public License
  11. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. from threading import Thread
  13. from os import path
  14. from wand.exceptions import BlobError
  15. from utils import (
  16. log,
  17. error_msg,
  18. )
  19. import config as const
  20. def generate_slides(
  21. slidegen, slide_count, template_img, zfill_length, disable_async: bool
  22. ) -> None:
  23. log("generating song slides...")
  24. current_slide_index: int = 0
  25. log("spawning subprocess for start slide...", color="yellow")
  26. threads = [
  27. Thread(
  28. target=generate_start_slide,
  29. args=(slidegen, template_img, zfill_length, disable_async),
  30. )
  31. ]
  32. for index, structure in enumerate(slidegen.chosen_structure):
  33. structure_element_splitted: list = slidegen.songtext[
  34. structure
  35. ].splitlines()
  36. line_count = len(structure_element_splitted)
  37. use_line_ranges_per_index = []
  38. use_lines_per_index = []
  39. if line_count <= const.STRUCTURE_ELEMENT_MAX_LINES:
  40. inner_slide_count = 1
  41. else:
  42. inner_slide_count: int = (
  43. line_count // const.STRUCTURE_ELEMENT_MAX_LINES + 1
  44. )
  45. use_lines_per_index = [
  46. line_count // inner_slide_count
  47. ] * inner_slide_count
  48. for inner_slide in range(inner_slide_count):
  49. if sum(use_lines_per_index) == line_count:
  50. break
  51. use_lines_per_index[inner_slide] = (
  52. use_lines_per_index[inner_slide] + 1
  53. )
  54. for inner_slide in range(inner_slide_count):
  55. use_line_ranges_per_index.append(
  56. sum(use_lines_per_index[:inner_slide])
  57. )
  58. for inner_slide in range(inner_slide_count):
  59. current_slide_index += 1
  60. log(
  61. "spawning subprocess for song slide [{} / {}]...".format(
  62. current_slide_index, slide_count
  63. ),
  64. color="yellow",
  65. )
  66. if inner_slide_count == 1:
  67. structure_element_value: str = slidegen.songtext[structure]
  68. else:
  69. splitted_wanted_range: list = structure_element_splitted[
  70. use_line_ranges_per_index[
  71. inner_slide
  72. ] : use_line_ranges_per_index[inner_slide]
  73. + use_lines_per_index[inner_slide]
  74. ]
  75. structure_element_value: str = ""
  76. for element in splitted_wanted_range:
  77. structure_element_value += element + "\n"
  78. structure_element_value = structure_element_value[:-1]
  79. threads.append(
  80. Thread(
  81. target=generate_song_slide,
  82. args=(
  83. slidegen.slide_style.song_slide_form,
  84. template_img,
  85. structure_element_value,
  86. slidegen,
  87. index,
  88. inner_slide_count,
  89. inner_slide,
  90. current_slide_index,
  91. zfill_length,
  92. disable_async,
  93. ),
  94. )
  95. )
  96. for thread in threads:
  97. thread.start()
  98. if disable_async:
  99. for thread in threads:
  100. thread.join()
  101. def generate_start_slide(slidegen, template_img, zfill_length, disable_async):
  102. first_slide = slidegen.slide_style.start_slide_form()
  103. start_slide_img = first_slide.get_slide(
  104. template_img,
  105. slidegen.metadata["book"],
  106. slidegen.metadata["text"],
  107. slidegen.metadata["melody"],
  108. )
  109. start_slide_img.format = const.IMAGE_FORMAT
  110. try:
  111. start_slide_img.save(
  112. filename=path.join(
  113. slidegen.output_dir,
  114. const.FILE_NAMEING
  115. + "1".zfill(zfill_length)
  116. + "."
  117. + const.FILE_EXTENSION,
  118. )
  119. )
  120. if disable_async:
  121. log("start slide generated and saved")
  122. except BlobError:
  123. error_msg("could not write start slide to target directory")
  124. def generate_song_slide(
  125. song_slide,
  126. template_img,
  127. structure_element_value,
  128. slidegen,
  129. index,
  130. inner_slide_count,
  131. inner_slide,
  132. current_slide_index,
  133. zfill_length,
  134. disable_async,
  135. ):
  136. song_slide_img = song_slide.get_slide(
  137. self=slidegen.slide_style.song_slide_form(),
  138. template_img=template_img,
  139. slide_text=structure_element_value,
  140. song_structure=slidegen.chosen_structure,
  141. index=index,
  142. use_arrow=bool(
  143. inner_slide_count != 1 and inner_slide != inner_slide_count - 1
  144. ),
  145. )
  146. song_slide_img.format = const.IMAGE_FORMAT
  147. try:
  148. song_slide_img.save(
  149. filename=path.join(
  150. slidegen.output_dir,
  151. const.FILE_NAMEING
  152. + str(current_slide_index + 1).zfill(zfill_length)
  153. + "."
  154. + const.FILE_EXTENSION,
  155. )
  156. )
  157. if disable_async:
  158. log("song slide {} generated and saved".format(current_slide_index))
  159. except BlobError:
  160. error_msg(
  161. "could not write song slide {} to target directory".format(
  162. current_slide_index
  163. )
  164. )