StreamingCommunity 2.5.0__py3-none-any.whl → 2.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of StreamingCommunity might be problematic. Click here for more details.

Files changed (263) hide show
  1. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/js_parser.cpython-313.pyc +0 -0
  2. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/js_parser.cpython-39.pyc +0 -0
  3. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/util.cpython-313.pyc +0 -0
  4. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/util.cpython-39.pyc +0 -0
  5. StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py +143 -0
  6. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +136 -0
  7. StreamingCommunity/Api/Player/__pycache__/ddl.cpython-313.pyc +0 -0
  8. StreamingCommunity/Api/Player/__pycache__/ddl.cpython-39.pyc +0 -0
  9. StreamingCommunity/Api/Player/__pycache__/maxstream.cpython-313.pyc +0 -0
  10. StreamingCommunity/Api/Player/__pycache__/maxstream.cpython-39.pyc +0 -0
  11. StreamingCommunity/Api/Player/__pycache__/supervideo.cpython-313.pyc +0 -0
  12. StreamingCommunity/Api/Player/__pycache__/supervideo.cpython-39.pyc +0 -0
  13. StreamingCommunity/Api/Player/__pycache__/vixcloud.cpython-313.pyc +0 -0
  14. StreamingCommunity/Api/Player/__pycache__/vixcloud.cpython-39.pyc +0 -0
  15. StreamingCommunity/Api/Player/ddl.py +89 -0
  16. StreamingCommunity/Api/Player/maxstream.py +151 -0
  17. StreamingCommunity/Api/Player/supervideo.py +194 -0
  18. StreamingCommunity/Api/Player/vixcloud.py +273 -0
  19. StreamingCommunity/Api/Site/1337xx/__init__.py +51 -0
  20. StreamingCommunity/Api/Site/1337xx/__pycache__/__init__.cpython-313.pyc +0 -0
  21. StreamingCommunity/Api/Site/1337xx/__pycache__/__init__.cpython-39.pyc +0 -0
  22. StreamingCommunity/Api/Site/1337xx/__pycache__/costant.cpython-313.pyc +0 -0
  23. StreamingCommunity/Api/Site/1337xx/__pycache__/costant.cpython-39.pyc +0 -0
  24. StreamingCommunity/Api/Site/1337xx/__pycache__/site.cpython-313.pyc +0 -0
  25. StreamingCommunity/Api/Site/1337xx/__pycache__/site.cpython-39.pyc +0 -0
  26. StreamingCommunity/Api/Site/1337xx/__pycache__/title.cpython-313.pyc +0 -0
  27. StreamingCommunity/Api/Site/1337xx/__pycache__/title.cpython-39.pyc +0 -0
  28. StreamingCommunity/Api/Site/1337xx/costant.py +15 -0
  29. StreamingCommunity/Api/Site/1337xx/site.py +89 -0
  30. StreamingCommunity/Api/Site/1337xx/title.py +64 -0
  31. StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py +51 -0
  32. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/__init__.cpython-313.pyc +0 -0
  33. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/__init__.cpython-39.pyc +0 -0
  34. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/costant.cpython-313.pyc +0 -0
  35. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/costant.cpython-39.pyc +0 -0
  36. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/film.cpython-313.pyc +0 -0
  37. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/film.cpython-39.pyc +0 -0
  38. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/site.cpython-313.pyc +0 -0
  39. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/site.cpython-39.pyc +0 -0
  40. StreamingCommunity/Api/Site/altadefinizionegratis/costant.py +19 -0
  41. StreamingCommunity/Api/Site/altadefinizionegratis/film.py +72 -0
  42. StreamingCommunity/Api/Site/altadefinizionegratis/site.py +95 -0
  43. StreamingCommunity/Api/Site/animeunity/__init__.py +51 -0
  44. StreamingCommunity/Api/Site/animeunity/__pycache__/__init__.cpython-313.pyc +0 -0
  45. StreamingCommunity/Api/Site/animeunity/__pycache__/__init__.cpython-39.pyc +0 -0
  46. StreamingCommunity/Api/Site/animeunity/__pycache__/costant.cpython-313.pyc +0 -0
  47. StreamingCommunity/Api/Site/animeunity/__pycache__/costant.cpython-39.pyc +0 -0
  48. StreamingCommunity/Api/Site/animeunity/__pycache__/film_serie.cpython-313.pyc +0 -0
  49. StreamingCommunity/Api/Site/animeunity/__pycache__/film_serie.cpython-39.pyc +0 -0
  50. StreamingCommunity/Api/Site/animeunity/__pycache__/site.cpython-313.pyc +0 -0
  51. StreamingCommunity/Api/Site/animeunity/__pycache__/site.cpython-39.pyc +0 -0
  52. StreamingCommunity/Api/Site/animeunity/costant.py +19 -0
  53. StreamingCommunity/Api/Site/animeunity/film_serie.py +135 -0
  54. StreamingCommunity/Api/Site/animeunity/site.py +175 -0
  55. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +97 -0
  56. StreamingCommunity/Api/Site/animeunity/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  57. StreamingCommunity/Api/Site/animeunity/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  58. StreamingCommunity/Api/Site/cb01new/__init__.py +52 -0
  59. StreamingCommunity/Api/Site/cb01new/__pycache__/__init__.cpython-313.pyc +0 -0
  60. StreamingCommunity/Api/Site/cb01new/__pycache__/__init__.cpython-39.pyc +0 -0
  61. StreamingCommunity/Api/Site/cb01new/__pycache__/costant.cpython-313.pyc +0 -0
  62. StreamingCommunity/Api/Site/cb01new/__pycache__/costant.cpython-39.pyc +0 -0
  63. StreamingCommunity/Api/Site/cb01new/__pycache__/film.cpython-313.pyc +0 -0
  64. StreamingCommunity/Api/Site/cb01new/__pycache__/film.cpython-39.pyc +0 -0
  65. StreamingCommunity/Api/Site/cb01new/__pycache__/site.cpython-313.pyc +0 -0
  66. StreamingCommunity/Api/Site/cb01new/__pycache__/site.cpython-39.pyc +0 -0
  67. StreamingCommunity/Api/Site/cb01new/costant.py +19 -0
  68. StreamingCommunity/Api/Site/cb01new/film.py +71 -0
  69. StreamingCommunity/Api/Site/cb01new/site.py +83 -0
  70. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +56 -0
  71. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/__init__.cpython-313.pyc +0 -0
  72. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/__init__.cpython-39.pyc +0 -0
  73. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/costant.cpython-313.pyc +0 -0
  74. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/costant.cpython-39.pyc +0 -0
  75. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/series.cpython-313.pyc +0 -0
  76. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/series.cpython-39.pyc +0 -0
  77. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/site.cpython-313.pyc +0 -0
  78. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/site.cpython-39.pyc +0 -0
  79. StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +20 -0
  80. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +145 -0
  81. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +99 -0
  82. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +85 -0
  83. StreamingCommunity/Api/Site/ddlstreamitaly/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  84. StreamingCommunity/Api/Site/ddlstreamitaly/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  85. StreamingCommunity/Api/Site/guardaserie/__init__.py +51 -0
  86. StreamingCommunity/Api/Site/guardaserie/__pycache__/__init__.cpython-313.pyc +0 -0
  87. StreamingCommunity/Api/Site/guardaserie/__pycache__/__init__.cpython-39.pyc +0 -0
  88. StreamingCommunity/Api/Site/guardaserie/__pycache__/costant.cpython-313.pyc +0 -0
  89. StreamingCommunity/Api/Site/guardaserie/__pycache__/costant.cpython-39.pyc +0 -0
  90. StreamingCommunity/Api/Site/guardaserie/__pycache__/series.cpython-313.pyc +0 -0
  91. StreamingCommunity/Api/Site/guardaserie/__pycache__/series.cpython-39.pyc +0 -0
  92. StreamingCommunity/Api/Site/guardaserie/__pycache__/site.cpython-313.pyc +0 -0
  93. StreamingCommunity/Api/Site/guardaserie/__pycache__/site.cpython-39.pyc +0 -0
  94. StreamingCommunity/Api/Site/guardaserie/costant.py +19 -0
  95. StreamingCommunity/Api/Site/guardaserie/series.py +198 -0
  96. StreamingCommunity/Api/Site/guardaserie/site.py +90 -0
  97. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +110 -0
  98. StreamingCommunity/Api/Site/guardaserie/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  99. StreamingCommunity/Api/Site/guardaserie/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  100. StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +52 -0
  101. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/__init__.cpython-313.pyc +0 -0
  102. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/__init__.cpython-39.pyc +0 -0
  103. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/costant.cpython-313.pyc +0 -0
  104. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/costant.cpython-39.pyc +0 -0
  105. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/site.cpython-313.pyc +0 -0
  106. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/site.cpython-39.pyc +0 -0
  107. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/title.cpython-313.pyc +0 -0
  108. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/title.cpython-39.pyc +0 -0
  109. StreamingCommunity/Api/Site/ilcorsaronero/costant.py +19 -0
  110. StreamingCommunity/Api/Site/ilcorsaronero/site.py +72 -0
  111. StreamingCommunity/Api/Site/ilcorsaronero/title.py +44 -0
  112. StreamingCommunity/Api/Site/ilcorsaronero/util/__pycache__/ilCorsarScraper.cpython-313.pyc +0 -0
  113. StreamingCommunity/Api/Site/ilcorsaronero/util/__pycache__/ilCorsarScraper.cpython-39.pyc +0 -0
  114. StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py +149 -0
  115. StreamingCommunity/Api/Site/mostraguarda/__init__.py +49 -0
  116. StreamingCommunity/Api/Site/mostraguarda/__pycache__/__init__.cpython-313.pyc +0 -0
  117. StreamingCommunity/Api/Site/mostraguarda/__pycache__/__init__.cpython-39.pyc +0 -0
  118. StreamingCommunity/Api/Site/mostraguarda/__pycache__/costant.cpython-313.pyc +0 -0
  119. StreamingCommunity/Api/Site/mostraguarda/__pycache__/costant.cpython-39.pyc +0 -0
  120. StreamingCommunity/Api/Site/mostraguarda/__pycache__/film.cpython-313.pyc +0 -0
  121. StreamingCommunity/Api/Site/mostraguarda/__pycache__/film.cpython-39.pyc +0 -0
  122. StreamingCommunity/Api/Site/mostraguarda/costant.py +19 -0
  123. StreamingCommunity/Api/Site/mostraguarda/film.py +101 -0
  124. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +56 -0
  125. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/__init__.cpython-313.pyc +0 -0
  126. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/__init__.cpython-39.pyc +0 -0
  127. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/costant.cpython-313.pyc +0 -0
  128. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/costant.cpython-39.pyc +0 -0
  129. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/film.cpython-313.pyc +0 -0
  130. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/film.cpython-39.pyc +0 -0
  131. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/series.cpython-313.pyc +0 -0
  132. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/series.cpython-39.pyc +0 -0
  133. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/site.cpython-313.pyc +0 -0
  134. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/site.cpython-39.pyc +0 -0
  135. StreamingCommunity/Api/Site/streamingcommunity/costant.py +19 -0
  136. StreamingCommunity/Api/Site/streamingcommunity/film.py +75 -0
  137. StreamingCommunity/Api/Site/streamingcommunity/series.py +207 -0
  138. StreamingCommunity/Api/Site/streamingcommunity/site.py +143 -0
  139. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +124 -0
  140. StreamingCommunity/Api/Site/streamingcommunity/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  141. StreamingCommunity/Api/Site/streamingcommunity/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  142. StreamingCommunity/Api/Template/Class/SearchType.py +101 -0
  143. StreamingCommunity/Api/Template/Class/__pycache__/SearchType.cpython-313.pyc +0 -0
  144. StreamingCommunity/Api/Template/Class/__pycache__/SearchType.cpython-39.pyc +0 -0
  145. StreamingCommunity/Api/Template/Util/__init__.py +5 -0
  146. StreamingCommunity/Api/Template/Util/__pycache__/__init__.cpython-313.pyc +0 -0
  147. StreamingCommunity/Api/Template/Util/__pycache__/__init__.cpython-39.pyc +0 -0
  148. StreamingCommunity/Api/Template/Util/__pycache__/get_domain.cpython-313.pyc +0 -0
  149. StreamingCommunity/Api/Template/Util/__pycache__/get_domain.cpython-39.pyc +0 -0
  150. StreamingCommunity/Api/Template/Util/__pycache__/manage_ep.cpython-313.pyc +0 -0
  151. StreamingCommunity/Api/Template/Util/__pycache__/manage_ep.cpython-39.pyc +0 -0
  152. StreamingCommunity/Api/Template/Util/__pycache__/recall_search.cpython-313.pyc +0 -0
  153. StreamingCommunity/Api/Template/Util/__pycache__/recall_search.cpython-39.pyc +0 -0
  154. StreamingCommunity/Api/Template/Util/get_domain.py +202 -0
  155. StreamingCommunity/Api/Template/Util/manage_ep.py +179 -0
  156. StreamingCommunity/Api/Template/Util/recall_search.py +37 -0
  157. StreamingCommunity/Api/Template/__init__.py +3 -0
  158. StreamingCommunity/Api/Template/__pycache__/__init__.cpython-313.pyc +0 -0
  159. StreamingCommunity/Api/Template/__pycache__/__init__.cpython-39.pyc +0 -0
  160. StreamingCommunity/Api/Template/__pycache__/site.cpython-313.pyc +0 -0
  161. StreamingCommunity/Api/Template/__pycache__/site.cpython-39.pyc +0 -0
  162. StreamingCommunity/Api/Template/site.py +87 -0
  163. StreamingCommunity/Lib/Downloader/HLS/__pycache__/downloader.cpython-313.pyc +0 -0
  164. StreamingCommunity/Lib/Downloader/HLS/__pycache__/downloader.cpython-39.pyc +0 -0
  165. StreamingCommunity/Lib/Downloader/HLS/__pycache__/proxyes.cpython-313.pyc +0 -0
  166. StreamingCommunity/Lib/Downloader/HLS/__pycache__/proxyes.cpython-39.pyc +0 -0
  167. StreamingCommunity/Lib/Downloader/HLS/__pycache__/segments.cpython-313.pyc +0 -0
  168. StreamingCommunity/Lib/Downloader/HLS/__pycache__/segments.cpython-39.pyc +0 -0
  169. StreamingCommunity/Lib/Downloader/HLS/downloader.py +1008 -0
  170. StreamingCommunity/Lib/Downloader/HLS/proxyes.py +110 -0
  171. StreamingCommunity/Lib/Downloader/HLS/segments.py +573 -0
  172. StreamingCommunity/Lib/Downloader/MP4/__pycache__/downloader.cpython-313.pyc +0 -0
  173. StreamingCommunity/Lib/Downloader/MP4/__pycache__/downloader.cpython-39.pyc +0 -0
  174. StreamingCommunity/Lib/Downloader/MP4/downloader.py +155 -0
  175. StreamingCommunity/Lib/Downloader/TOR/__pycache__/downloader.cpython-313.pyc +0 -0
  176. StreamingCommunity/Lib/Downloader/TOR/__pycache__/downloader.cpython-39.pyc +0 -0
  177. StreamingCommunity/Lib/Downloader/TOR/downloader.py +296 -0
  178. StreamingCommunity/Lib/Downloader/__init__.py +5 -0
  179. StreamingCommunity/Lib/Downloader/__pycache__/__init__.cpython-313.pyc +0 -0
  180. StreamingCommunity/Lib/Downloader/__pycache__/__init__.cpython-39.pyc +0 -0
  181. StreamingCommunity/Lib/FFmpeg/__init__.py +4 -0
  182. StreamingCommunity/Lib/FFmpeg/__pycache__/__init__.cpython-313.pyc +0 -0
  183. StreamingCommunity/Lib/FFmpeg/__pycache__/__init__.cpython-39.pyc +0 -0
  184. StreamingCommunity/Lib/FFmpeg/__pycache__/capture.cpython-313.pyc +0 -0
  185. StreamingCommunity/Lib/FFmpeg/__pycache__/capture.cpython-39.pyc +0 -0
  186. StreamingCommunity/Lib/FFmpeg/__pycache__/command.cpython-313.pyc +0 -0
  187. StreamingCommunity/Lib/FFmpeg/__pycache__/command.cpython-39.pyc +0 -0
  188. StreamingCommunity/Lib/FFmpeg/__pycache__/util.cpython-313.pyc +0 -0
  189. StreamingCommunity/Lib/FFmpeg/__pycache__/util.cpython-39.pyc +0 -0
  190. StreamingCommunity/Lib/FFmpeg/capture.py +170 -0
  191. StreamingCommunity/Lib/FFmpeg/command.py +296 -0
  192. StreamingCommunity/Lib/FFmpeg/util.py +249 -0
  193. StreamingCommunity/Lib/M3U8/__init__.py +6 -0
  194. StreamingCommunity/Lib/M3U8/__pycache__/__init__.cpython-313.pyc +0 -0
  195. StreamingCommunity/Lib/M3U8/__pycache__/__init__.cpython-39.pyc +0 -0
  196. StreamingCommunity/Lib/M3U8/__pycache__/decryptor.cpython-313.pyc +0 -0
  197. StreamingCommunity/Lib/M3U8/__pycache__/decryptor.cpython-39.pyc +0 -0
  198. StreamingCommunity/Lib/M3U8/__pycache__/estimator.cpython-313.pyc +0 -0
  199. StreamingCommunity/Lib/M3U8/__pycache__/estimator.cpython-39.pyc +0 -0
  200. StreamingCommunity/Lib/M3U8/__pycache__/parser.cpython-313.pyc +0 -0
  201. StreamingCommunity/Lib/M3U8/__pycache__/parser.cpython-39.pyc +0 -0
  202. StreamingCommunity/Lib/M3U8/__pycache__/url_fixer.cpython-313.pyc +0 -0
  203. StreamingCommunity/Lib/M3U8/__pycache__/url_fixer.cpython-39.pyc +0 -0
  204. StreamingCommunity/Lib/M3U8/decryptor.py +165 -0
  205. StreamingCommunity/Lib/M3U8/estimator.py +229 -0
  206. StreamingCommunity/Lib/M3U8/parser.py +666 -0
  207. StreamingCommunity/Lib/M3U8/url_fixer.py +58 -0
  208. StreamingCommunity/Lib/TMBD/__init__.py +2 -0
  209. StreamingCommunity/Lib/TMBD/__pycache__/__init__.cpython-313.pyc +0 -0
  210. StreamingCommunity/Lib/TMBD/__pycache__/__init__.cpython-39.pyc +0 -0
  211. StreamingCommunity/Lib/TMBD/__pycache__/obj_tmbd.cpython-313.pyc +0 -0
  212. StreamingCommunity/Lib/TMBD/__pycache__/obj_tmbd.cpython-39.pyc +0 -0
  213. StreamingCommunity/Lib/TMBD/__pycache__/tmdb.cpython-313.pyc +0 -0
  214. StreamingCommunity/Lib/TMBD/__pycache__/tmdb.cpython-39.pyc +0 -0
  215. StreamingCommunity/Lib/TMBD/obj_tmbd.py +39 -0
  216. StreamingCommunity/Lib/TMBD/tmdb.py +346 -0
  217. StreamingCommunity/Upload/__pycache__/update.cpython-313.pyc +0 -0
  218. StreamingCommunity/Upload/__pycache__/update.cpython-39.pyc +0 -0
  219. StreamingCommunity/Upload/__pycache__/version.cpython-313.pyc +0 -0
  220. StreamingCommunity/Upload/__pycache__/version.cpython-39.pyc +0 -0
  221. StreamingCommunity/Upload/update.py +67 -0
  222. StreamingCommunity/Upload/version.py +5 -0
  223. StreamingCommunity/Util/__pycache__/_jsonConfig.cpython-313.pyc +0 -0
  224. StreamingCommunity/Util/__pycache__/_jsonConfig.cpython-39.pyc +0 -0
  225. StreamingCommunity/Util/__pycache__/call_stack.cpython-313.pyc +0 -0
  226. StreamingCommunity/Util/__pycache__/call_stack.cpython-39.pyc +0 -0
  227. StreamingCommunity/Util/__pycache__/color.cpython-313.pyc +0 -0
  228. StreamingCommunity/Util/__pycache__/color.cpython-39.pyc +0 -0
  229. StreamingCommunity/Util/__pycache__/console.cpython-313.pyc +0 -0
  230. StreamingCommunity/Util/__pycache__/console.cpython-39.pyc +0 -0
  231. StreamingCommunity/Util/__pycache__/ffmpeg_installer.cpython-313.pyc +0 -0
  232. StreamingCommunity/Util/__pycache__/ffmpeg_installer.cpython-39.pyc +0 -0
  233. StreamingCommunity/Util/__pycache__/headers.cpython-313.pyc +0 -0
  234. StreamingCommunity/Util/__pycache__/headers.cpython-39.pyc +0 -0
  235. StreamingCommunity/Util/__pycache__/logger.cpython-313.pyc +0 -0
  236. StreamingCommunity/Util/__pycache__/logger.cpython-39.pyc +0 -0
  237. StreamingCommunity/Util/__pycache__/message.cpython-313.pyc +0 -0
  238. StreamingCommunity/Util/__pycache__/message.cpython-39.pyc +0 -0
  239. StreamingCommunity/Util/__pycache__/os.cpython-313.pyc +0 -0
  240. StreamingCommunity/Util/__pycache__/os.cpython-39.pyc +0 -0
  241. StreamingCommunity/Util/__pycache__/table.cpython-313.pyc +0 -0
  242. StreamingCommunity/Util/__pycache__/table.cpython-39.pyc +0 -0
  243. StreamingCommunity/Util/_jsonConfig.py +228 -0
  244. StreamingCommunity/Util/call_stack.py +42 -0
  245. StreamingCommunity/Util/color.py +20 -0
  246. StreamingCommunity/Util/console.py +12 -0
  247. StreamingCommunity/Util/ffmpeg_installer.py +371 -0
  248. StreamingCommunity/Util/headers.py +160 -0
  249. StreamingCommunity/Util/logger.py +62 -0
  250. StreamingCommunity/Util/message.py +64 -0
  251. StreamingCommunity/Util/os.py +507 -0
  252. StreamingCommunity/Util/table.py +229 -0
  253. StreamingCommunity/__pycache__/__init__.cpython-313.pyc +0 -0
  254. StreamingCommunity/__pycache__/__init__.cpython-39.pyc +0 -0
  255. StreamingCommunity/__pycache__/run.cpython-313.pyc +0 -0
  256. StreamingCommunity/__pycache__/run.cpython-39.pyc +0 -0
  257. {StreamingCommunity-2.5.0.dist-info → StreamingCommunity-2.5.2.dist-info}/METADATA +1 -1
  258. StreamingCommunity-2.5.2.dist-info/RECORD +264 -0
  259. StreamingCommunity-2.5.0.dist-info/RECORD +0 -8
  260. {StreamingCommunity-2.5.0.dist-info → StreamingCommunity-2.5.2.dist-info}/LICENSE +0 -0
  261. {StreamingCommunity-2.5.0.dist-info → StreamingCommunity-2.5.2.dist-info}/WHEEL +0 -0
  262. {StreamingCommunity-2.5.0.dist-info → StreamingCommunity-2.5.2.dist-info}/entry_points.txt +0 -0
  263. {StreamingCommunity-2.5.0.dist-info → StreamingCommunity-2.5.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,228 @@
1
+ # 29.01.24
2
+
3
+ import os
4
+ import sys
5
+ import json
6
+ import httpx
7
+ import logging
8
+ from typing import Any, List
9
+
10
+
11
+ class ConfigManager:
12
+ def __init__(self, file_path: str = 'config.json') -> None:
13
+ """Initialize the ConfigManager.
14
+
15
+ Parameters:
16
+ - file_path (str, optional): The path to the configuration file. Default is 'config.json'.
17
+ """
18
+ self.file_path = file_path
19
+ self.config = {}
20
+ self.cache = {}
21
+
22
+ def read_config(self) -> None:
23
+ """Read the configuration file."""
24
+ try:
25
+ logging.info(f"Reading file: {self.file_path}")
26
+
27
+ # Check if file exists
28
+ if os.path.exists(self.file_path):
29
+ with open(self.file_path, 'r') as f:
30
+ self.config = json.load(f)
31
+ logging.info("Configuration file loaded successfully.")
32
+
33
+ # Download config.json if it doesn't exist locally
34
+ else:
35
+ logging.info("Configuration file does not exist. Downloading...")
36
+ self.download_requirements(
37
+ 'https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/config.json',
38
+ self.file_path
39
+ )
40
+
41
+ # Load the downloaded config.json into the config attribute
42
+ with open(self.file_path, 'r') as f:
43
+ self.config = json.load(f)
44
+ logging.info("Configuration file downloaded and saved.")
45
+
46
+ logging.info("Configuration file processed successfully.")
47
+
48
+ except Exception as e:
49
+ logging.error(f"Error reading configuration file: {e}")
50
+
51
+ def download_requirements(self, url: str, filename: str):
52
+ """
53
+ Download the requirements.txt file from the specified URL if not found locally using requests.
54
+
55
+ Args:
56
+ url (str): The URL to download the requirements file from.
57
+ filename (str): The local filename to save the requirements file as.
58
+ """
59
+ try:
60
+ import requests
61
+
62
+ logging.info(f"{filename} not found locally. Downloading from {url}...")
63
+ response = requests.get(url)
64
+
65
+ if response.status_code == 200:
66
+ with open(filename, 'wb') as f:
67
+ f.write(response.content)
68
+
69
+ else:
70
+ logging.error(f"Failed to download {filename}. HTTP Status code: {response.status_code}")
71
+ sys.exit(0)
72
+
73
+ except Exception as e:
74
+ logging.error(f"Failed to download {filename}: {e}")
75
+ sys.exit(0)
76
+
77
+ def read_key(self, section: str, key: str, data_type: type = str) -> Any:
78
+ """Read a key from the configuration file.
79
+
80
+ Parameters:
81
+ - section (str): The section in the configuration file.
82
+ - key (str): The key to be read.
83
+ - data_type (type, optional): The expected data type of the key's value. Default is str.
84
+
85
+ Returns:
86
+ The value of the key converted to the specified data type.
87
+ """
88
+ cache_key = f"{section}.{key}"
89
+ logging.info(f"Read key: {cache_key}")
90
+
91
+ if cache_key in self.cache:
92
+ return self.cache[cache_key]
93
+
94
+ if section in self.config and key in self.config[section]:
95
+ value = self.config[section][key]
96
+ else:
97
+ raise ValueError(f"Key '{key}' not found in section '{section}'")
98
+
99
+ value = self._convert_to_data_type(value, data_type)
100
+ self.cache[cache_key] = value
101
+
102
+ return value
103
+
104
+ def _convert_to_data_type(self, value: str, data_type: type) -> Any:
105
+ """Convert the value to the specified data type.
106
+
107
+ Parameters:
108
+ - value (str): The value to be converted.
109
+ - data_type (type): The expected data type.
110
+
111
+ Returns:
112
+ The value converted to the specified data type.
113
+ """
114
+ if data_type == int:
115
+ return int(value)
116
+ elif data_type == bool:
117
+ return bool(value)
118
+ elif data_type == list:
119
+ return value if isinstance(value, list) else [item.strip() for item in value.split(',')]
120
+ elif data_type == type(None):
121
+ return None
122
+ else:
123
+ return value
124
+
125
+ def get(self, section: str, key: str) -> Any:
126
+ """Read a value from the configuration file.
127
+
128
+ Parameters:
129
+ - section (str): The section in the configuration file.
130
+ - key (str): The key to be read.
131
+
132
+ Returns:
133
+ The value associated with the key.
134
+ """
135
+ return self.read_key(section, key)
136
+
137
+ def get_int(self, section: str, key: str) -> int:
138
+ """Read an integer value from the configuration file.
139
+
140
+ Parameters:
141
+ - section (str): The section in the configuration file.
142
+ - key (str): The key to be read.
143
+
144
+ Returns:
145
+ int: The integer value.
146
+ """
147
+ return self.read_key(section, key, int)
148
+
149
+ def get_float(self, section: str, key: str) -> int:
150
+ """Read an float value from the configuration file.
151
+
152
+ Parameters:
153
+ - section (str): The section in the configuration file.
154
+ - key (str): The key to be read.
155
+
156
+ Returns:
157
+ float: The float value.
158
+ """
159
+ return self.read_key(section, key, float)
160
+
161
+ def get_bool(self, section: str, key: str) -> bool:
162
+ """Read a boolean value from the configuration file.
163
+
164
+ Parameters:
165
+ - section (str): The section in the configuration file.
166
+ - key (str): The key to be read.
167
+
168
+ Returns:
169
+ bool: The boolean value.
170
+ """
171
+ return self.read_key(section, key, bool)
172
+
173
+ def get_list(self, section: str, key: str) -> List[str]:
174
+ """Read a list value from the configuration file.
175
+
176
+ Parameters:
177
+ - section (str): The section in the configuration file.
178
+ - key (str): The key to be read.
179
+
180
+ Returns:
181
+ list: The list value.
182
+ """
183
+ return self.read_key(section, key, list)
184
+
185
+ def get_dict(self, section: str, key: str) -> dict:
186
+ """Read a dictionary value from the configuration file.
187
+
188
+ Parameters:
189
+ - section (str): The section in the configuration file.
190
+ - key (str): The key to be read.
191
+
192
+ Returns:
193
+ dict: The dictionary value.
194
+ """
195
+ return self.read_key(section, key, dict)
196
+
197
+ def set_key(self, section: str, key: str, value: Any) -> None:
198
+ """Set a key in the configuration file.
199
+
200
+ Parameters:
201
+ - section (str): The section in the configuration file.
202
+ - key (str): The key to be set.
203
+ - value (Any): The value to be associated with the key.
204
+ """
205
+ try:
206
+ if section not in self.config:
207
+ self.config[section] = {}
208
+
209
+ self.config[section][key] = value
210
+ cache_key = f"{section}.{key}"
211
+ self.cache[cache_key] = value
212
+ self.write_config()
213
+
214
+ except Exception as e:
215
+ print(f"Error setting key '{key}' in section '{section}': {e}")
216
+
217
+ def write_config(self) -> None:
218
+ """Write the configuration to the file."""
219
+ try:
220
+ with open(self.file_path, 'w') as f:
221
+ json.dump(self.config, f, indent=4)
222
+ except Exception as e:
223
+ print(f"Error writing configuration file: {e}")
224
+
225
+
226
+ # Initialize
227
+ config_manager = ConfigManager()
228
+ config_manager.read_config()
@@ -0,0 +1,42 @@
1
+ # 21.06.24
2
+
3
+ import os
4
+ import inspect
5
+
6
+
7
+ def get_call_stack():
8
+ """
9
+ Retrieves the current call stack with details about each call.
10
+
11
+ This function inspects the current call stack and returns a list of dictionaries,
12
+ where each dictionary contains details about a function call in the stack.
13
+
14
+ Returns:
15
+ list: A list of dictionaries, each containing the following keys:
16
+ - function (str): The name of the function.
17
+ - folder (str): The directory path of the script containing the function.
18
+ - folder_base (str): The base name of the directory path.
19
+ - script (str): The name of the script file containing the function.
20
+ - line (int): The line number in the script where the function is defined.
21
+ """
22
+
23
+ stack = inspect.stack()
24
+ call_stack = []
25
+
26
+ for frame_info in stack:
27
+ function_name = frame_info.function
28
+ filename = frame_info.filename
29
+ lineno = frame_info.lineno
30
+ folder_name = os.path.dirname(filename)
31
+ folder_base = os.path.basename(folder_name)
32
+ script_name = os.path.basename(filename)
33
+
34
+ call_stack.append({
35
+ "function": function_name,
36
+ "folder": folder_name,
37
+ "folder_base": folder_base,
38
+ "script": script_name,
39
+ "line": lineno
40
+ })
41
+
42
+ return call_stack
@@ -0,0 +1,20 @@
1
+ # 24.05.24
2
+
3
+ class Colors:
4
+ BLACK = "\033[30m"
5
+ RED = "\033[31m"
6
+ GREEN = "\033[32m"
7
+ YELLOW = "\033[33m"
8
+ BLUE = "\033[34m"
9
+ MAGENTA = "\033[35m"
10
+ CYAN = "\033[36m"
11
+ LIGHT_GRAY = "\033[37m"
12
+ DARK_GRAY = "\033[90m"
13
+ LIGHT_RED = "\033[91m"
14
+ LIGHT_GREEN = "\033[92m"
15
+ LIGHT_YELLOW = "\033[93m"
16
+ LIGHT_BLUE = "\033[94m"
17
+ LIGHT_MAGENTA = "\033[95m"
18
+ LIGHT_CYAN = "\033[96m"
19
+ WHITE = "\033[97m"
20
+ RESET = "\033[0m"
@@ -0,0 +1,12 @@
1
+ # 24.02.24
2
+
3
+ from rich.console import Console
4
+ from rich.prompt import Prompt, Confirm
5
+ from rich.panel import Panel
6
+ from rich.table import Table
7
+ from rich.text import Text
8
+
9
+
10
+ # Variable
11
+ msg = Prompt()
12
+ console = Console()
@@ -0,0 +1,371 @@
1
+ # 24.01.2024
2
+
3
+ import os
4
+ import platform
5
+ import subprocess
6
+ import zipfile
7
+ import tarfile
8
+ import logging
9
+ import requests
10
+ import shutil
11
+ import glob
12
+ from typing import Optional, Tuple
13
+
14
+
15
+ # External library
16
+ from rich.console import Console
17
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
18
+
19
+
20
+ # Variable
21
+ console = Console()
22
+
23
+ # https://github.com/eugeneware/ffmpeg-static/releases
24
+ # https://github.com/GyanD/codexffmpeg/releases/
25
+ FFMPEG_CONFIGURATION = {
26
+ 'windows': {
27
+ 'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary'),
28
+ 'download_url': 'https://github.com/GyanD/codexffmpeg/releases/download/{version}/ffmpeg-{version}-full_build.zip',
29
+ 'file_extension': '.zip',
30
+ 'executables': ['ffmpeg.exe', 'ffprobe.exe', 'ffplay.exe']
31
+ },
32
+ 'darwin': {
33
+ 'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
34
+ 'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/download/b{version}/ffmpeg-macOS-{arch}.zip',
35
+ 'file_extension': '.zip',
36
+ 'executables': ['ffmpeg', 'ffprobe', 'ffplay']
37
+ },
38
+ 'linux': {
39
+ 'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
40
+ 'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/download/b{version}/ffmpeg-linux-{arch}.tar.xz',
41
+ 'file_extension': '.tar.xz',
42
+ 'executables': ['ffmpeg', 'ffprobe', 'ffplay']
43
+ }
44
+ }
45
+
46
+
47
+ class FFMPEGDownloader:
48
+ """
49
+ A class for downloading and managing FFmpeg executables.
50
+
51
+ This class handles the detection of the operating system, downloading of FFmpeg binaries,
52
+ and management of the FFmpeg executables (ffmpeg, ffprobe, and ffplay).
53
+
54
+ Attributes:
55
+ os_name (str): The detected operating system name
56
+ arch (str): The system architecture (e.g., x86_64, arm64)
57
+ home_dir (str): User's home directory path
58
+ base_dir (str): Base directory for storing FFmpeg binaries
59
+ """
60
+
61
+ def __init__(self):
62
+ self.os_name = self._detect_system()
63
+ self.arch = self._detect_arch()
64
+ self.home_dir = os.path.expanduser('~')
65
+ self.base_dir = self._get_base_directory()
66
+
67
+ def _detect_system(self) -> str:
68
+ """
69
+ Detect and normalize the operating system name.
70
+
71
+ Returns:
72
+ str: Normalized operating system name ('windows', 'darwin', or 'linux')
73
+
74
+ Raises:
75
+ ValueError: If the operating system is not supported
76
+ """
77
+ system = platform.system().lower()
78
+ if system in FFMPEG_CONFIGURATION:
79
+ return system
80
+ raise ValueError(f"Unsupported operating system: {system}")
81
+
82
+ def _detect_arch(self) -> str:
83
+ """
84
+ Detect and normalize the system architecture.
85
+
86
+ Returns:
87
+ str: Normalized architecture name (e.g., 'x86_64', 'arm64')
88
+
89
+ The method normalizes various architecture names to consistent values:
90
+ - amd64/x86_64/x64 -> x86_64
91
+ - arm64/aarch64 -> arm64
92
+ """
93
+ machine = platform.machine().lower()
94
+ arch_map = {
95
+ 'amd64': 'x86_64',
96
+ 'x86_64': 'x86_64',
97
+ 'x64': 'x86_64',
98
+ 'arm64': 'arm64',
99
+ 'aarch64': 'arm64'
100
+ }
101
+ return arch_map.get(machine, machine)
102
+
103
+ def _get_base_directory(self) -> str:
104
+ """
105
+ Get and create the base directory for storing FFmpeg binaries.
106
+
107
+ Returns:
108
+ str: Path to the base directory
109
+
110
+ The directory location varies by operating system:
111
+ - Windows: C:\\binary
112
+ - macOS: ~/Applications/binary
113
+ - Linux: ~/.local/bin/binary
114
+ """
115
+ base_dir = FFMPEG_CONFIGURATION[self.os_name]['base_dir'](self.home_dir)
116
+ os.makedirs(base_dir, exist_ok=True)
117
+ return base_dir
118
+
119
+ def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
120
+ """
121
+ Check if FFmpeg binaries already exist in the base directory.
122
+ Enhanced to check both the binary directory and system paths on macOS.
123
+ """
124
+ config = FFMPEG_CONFIGURATION[self.os_name]
125
+ executables = config['executables']
126
+ found_executables = []
127
+
128
+ # For macOS, check both binary directory and system paths
129
+ if self.os_name == 'darwin':
130
+ potential_paths = [
131
+ '/usr/local/bin',
132
+ '/opt/homebrew/bin',
133
+ '/usr/bin',
134
+ self.base_dir
135
+ ]
136
+
137
+ for executable in executables:
138
+ found = None
139
+ for path in potential_paths:
140
+ full_path = os.path.join(path, executable)
141
+ if os.path.exists(full_path) and os.access(full_path, os.X_OK):
142
+ found = full_path
143
+ break
144
+ found_executables.append(found)
145
+ else:
146
+
147
+ # Original behavior for other operating systems
148
+ for executable in executables:
149
+ exe_paths = glob.glob(os.path.join(self.base_dir, executable))
150
+ found_executables.append(exe_paths[0] if exe_paths else None)
151
+
152
+ return tuple(found_executables) if len(found_executables) == 3 else (None, None, None)
153
+
154
+ def _get_latest_version(self, repo: str) -> Optional[str]:
155
+ """
156
+ Get the latest FFmpeg version from the GitHub releases page.
157
+
158
+ Returns:
159
+ Optional[str]: The latest version string, or None if retrieval fails.
160
+
161
+ Raises:
162
+ requests.exceptions.RequestException: If there are network-related errors.
163
+ """
164
+ try:
165
+ # Use GitHub API to fetch the latest release
166
+ response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest')
167
+ response.raise_for_status()
168
+ latest_release = response.json()
169
+
170
+ # Extract the tag name or version from the release
171
+ return latest_release.get('tag_name')
172
+
173
+ except Exception as e:
174
+ logging.error(f"Unable to get version from GitHub: {e}")
175
+ return None
176
+
177
+ def _download_file(self, url: str, destination: str) -> bool:
178
+ """
179
+ Download a file from URL with a Rich progress bar display.
180
+
181
+ Parameters:
182
+ url (str): The URL to download the file from. Should be a direct download link.
183
+ destination (str): Local file path where the downloaded file will be saved.
184
+
185
+ Returns:
186
+ bool: True if download was successful, False otherwise.
187
+
188
+ Raises:
189
+ requests.exceptions.RequestException: If there are network-related errors
190
+ IOError: If there are issues writing to the destination file
191
+ """
192
+ try:
193
+ response = requests.get(url, stream=True)
194
+ response.raise_for_status()
195
+ total_size = int(response.headers.get('content-length', 0))
196
+
197
+ with open(destination, 'wb') as file, \
198
+ Progress(
199
+ SpinnerColumn(),
200
+ TextColumn("[progress.description]{task.description}"),
201
+ BarColumn(),
202
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
203
+ TimeRemainingColumn()
204
+ ) as progress:
205
+
206
+ download_task = progress.add_task("[green]Downloading FFmpeg", total=total_size)
207
+ for chunk in response.iter_content(chunk_size=8192):
208
+ size = file.write(chunk)
209
+ progress.update(download_task, advance=size)
210
+ return True
211
+ except Exception as e:
212
+ logging.error(f"Download error: {e}")
213
+ return False
214
+
215
+ def _extract_and_copy_binaries(self, archive_path: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
216
+ """
217
+ Extract FFmpeg binaries from the downloaded archive and copy them to the base directory.
218
+
219
+ Parameters:
220
+ archive_path (str): Path to the downloaded archive file
221
+
222
+ Returns:
223
+ Tuple[Optional[str], Optional[str], Optional[str]]: Paths to the extracted ffmpeg,
224
+ ffprobe, and ffplay executables. Returns None for each executable that couldn't be extracted.
225
+ """
226
+ try:
227
+ extraction_path = os.path.join(self.base_dir, 'temp_extract')
228
+ os.makedirs(extraction_path, exist_ok=True)
229
+
230
+ if archive_path.endswith('.zip'):
231
+ with zipfile.ZipFile(archive_path, 'r') as zip_ref:
232
+ zip_ref.extractall(extraction_path)
233
+ elif archive_path.endswith('.tar.xz'):
234
+ with tarfile.open(archive_path) as tar_ref:
235
+ tar_ref.extractall(extraction_path)
236
+
237
+ config = FFMPEG_CONFIGURATION[self.os_name]
238
+ executables = config['executables']
239
+ found_paths = []
240
+
241
+ for executable in executables:
242
+ exe_paths = glob.glob(os.path.join(extraction_path, '**', executable), recursive=True)
243
+
244
+ if exe_paths:
245
+ dest_path = os.path.join(self.base_dir, executable)
246
+ shutil.copy2(exe_paths[0], dest_path)
247
+
248
+ if self.os_name != 'windows':
249
+ os.chmod(dest_path, 0o755)
250
+
251
+ found_paths.append(dest_path)
252
+ else:
253
+ found_paths.append(None)
254
+
255
+ shutil.rmtree(extraction_path, ignore_errors=True)
256
+ os.remove(archive_path)
257
+
258
+ return tuple(found_paths) if len(found_paths) == 3 else (None, None, None)
259
+
260
+ except Exception as e:
261
+ logging.error(f"Extraction/copy error: {e}")
262
+ return None, None, None
263
+
264
+ def download(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
265
+ """
266
+ Main method to download and set up FFmpeg executables.
267
+
268
+ Returns:
269
+ Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
270
+ Returns None for each executable that couldn't be downloaded or set up.
271
+ """
272
+ existing_ffmpeg, existing_ffprobe, existing_ffplay = self._check_existing_binaries()
273
+ if all([existing_ffmpeg, existing_ffprobe, existing_ffplay]):
274
+ return existing_ffmpeg, existing_ffprobe, existing_ffplay
275
+
276
+ repo = 'GyanD/codexffmpeg' if self._detect_system() == 'windows' else 'eugeneware/ffmpeg-static'
277
+ console.print(f"[red]Use {repo} repo for downloading ffmpeg.")
278
+ version = self._get_latest_version(repo)
279
+
280
+ if not version:
281
+ logging.error("Cannot proceed: version not found")
282
+ return None, None, None
283
+
284
+ config = FFMPEG_CONFIGURATION[self.os_name]
285
+ download_url = config['download_url'].format(
286
+ version=version,
287
+ arch=self.arch
288
+ )
289
+
290
+ download_path = os.path.join(
291
+ self.base_dir,
292
+ f'ffmpeg-{version}{config["file_extension"]}'
293
+ )
294
+
295
+ console.print(f"[bold blue]Downloading FFmpeg from:[/] {download_url}")
296
+ if not self._download_file(download_url, download_path):
297
+ return None, None, None
298
+
299
+ return self._extract_and_copy_binaries(download_path)
300
+
301
+ def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
302
+ """
303
+ Check for FFmpeg executables in the system and download them if not found.
304
+ Enhanced detection for macOS systems.
305
+
306
+ Returns:
307
+ Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
308
+ """
309
+ try:
310
+ system_platform = platform.system().lower()
311
+
312
+ # Special handling for macOS
313
+ if system_platform == 'darwin':
314
+
315
+ # Common installation paths on macOS
316
+ potential_paths = [
317
+ '/usr/local/bin', # Homebrew default
318
+ '/opt/homebrew/bin', # Apple Silicon Homebrew
319
+ '/usr/bin', # System default
320
+ os.path.expanduser('~/Applications/binary'), # Custom installation
321
+ '/Applications/binary' # Custom installation
322
+ ]
323
+
324
+ for path in potential_paths:
325
+ ffmpeg_path = os.path.join(path, 'ffmpeg')
326
+ ffprobe_path = os.path.join(path, 'ffprobe')
327
+ ffplay_path = os.path.join(path, 'ffplay')
328
+
329
+ if (os.path.exists(ffmpeg_path) and os.path.exists(ffprobe_path) and
330
+ os.access(ffmpeg_path, os.X_OK) and os.access(ffprobe_path, os.X_OK)):
331
+
332
+ # Return found executables, with ffplay being optional
333
+ ffplay_path = ffplay_path if os.path.exists(ffplay_path) else None
334
+ return ffmpeg_path, ffprobe_path, ffplay_path
335
+
336
+ # Windows detection
337
+ elif system_platform == 'windows':
338
+ try:
339
+ ffmpeg_path = subprocess.check_output(
340
+ ['where', 'ffmpeg'], stderr=subprocess.DEVNULL, text=True
341
+ ).strip().split('\n')[0]
342
+
343
+ ffprobe_path = subprocess.check_output(
344
+ ['where', 'ffprobe'], stderr=subprocess.DEVNULL, text=True
345
+ ).strip().split('\n')[0]
346
+
347
+ ffplay_path = subprocess.check_output(
348
+ ['where', 'ffplay'], stderr=subprocess.DEVNULL, text=True
349
+ ).strip().split('\n')[0]
350
+
351
+ return ffmpeg_path, ffprobe_path, ffplay_path
352
+
353
+ except subprocess.CalledProcessError:
354
+ logging.warning("One or more FFmpeg binaries were not found with command where")
355
+
356
+ # Linux detection
357
+ else:
358
+ ffmpeg_path = shutil.which('ffmpeg')
359
+ ffprobe_path = shutil.which('ffprobe')
360
+ ffplay_path = shutil.which('ffplay')
361
+
362
+ if ffmpeg_path and ffprobe_path:
363
+ return ffmpeg_path, ffprobe_path, ffplay_path
364
+
365
+ # If executables were not found, attempt to download FFmpeg
366
+ downloader = FFMPEGDownloader()
367
+ return downloader.download()
368
+
369
+ except Exception as e:
370
+ logging.error(f"Error checking or downloading FFmpeg executables: {e}")
371
+ return None, None, None