StreamingCommunity 2.5.2__py3-none-any.whl → 2.5.5__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 (268) hide show
  1. StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py +143 -143
  2. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +136 -136
  3. StreamingCommunity/Api/Player/ddl.py +89 -89
  4. StreamingCommunity/Api/Player/maxstream.py +151 -151
  5. StreamingCommunity/Api/Player/supervideo.py +193 -193
  6. StreamingCommunity/Api/Player/vixcloud.py +272 -272
  7. StreamingCommunity/Api/Site/1337xx/__init__.py +51 -50
  8. StreamingCommunity/Api/Site/1337xx/costant.py +14 -14
  9. StreamingCommunity/Api/Site/1337xx/site.py +87 -89
  10. StreamingCommunity/Api/Site/1337xx/title.py +63 -64
  11. StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py +74 -50
  12. StreamingCommunity/Api/Site/altadefinizionegratis/costant.py +21 -19
  13. StreamingCommunity/Api/Site/altadefinizionegratis/film.py +81 -72
  14. StreamingCommunity/Api/Site/altadefinizionegratis/site.py +116 -94
  15. StreamingCommunity/Api/Site/animeunity/__init__.py +75 -50
  16. StreamingCommunity/Api/Site/animeunity/costant.py +21 -19
  17. StreamingCommunity/Api/Site/animeunity/film_serie.py +171 -134
  18. StreamingCommunity/Api/Site/animeunity/site.py +191 -174
  19. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +97 -97
  20. StreamingCommunity/Api/Site/cb01new/__init__.py +51 -51
  21. StreamingCommunity/Api/Site/cb01new/costant.py +19 -19
  22. StreamingCommunity/Api/Site/cb01new/film.py +61 -71
  23. StreamingCommunity/Api/Site/cb01new/site.py +82 -82
  24. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +55 -55
  25. StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +20 -20
  26. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +149 -145
  27. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +98 -98
  28. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +84 -84
  29. StreamingCommunity/Api/Site/guardaserie/__init__.py +50 -50
  30. StreamingCommunity/Api/Site/guardaserie/costant.py +19 -19
  31. StreamingCommunity/Api/Site/guardaserie/series.py +199 -198
  32. StreamingCommunity/Api/Site/guardaserie/site.py +89 -89
  33. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +110 -110
  34. StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +51 -51
  35. StreamingCommunity/Api/Site/ilcorsaronero/costant.py +18 -18
  36. StreamingCommunity/Api/Site/ilcorsaronero/site.py +71 -71
  37. StreamingCommunity/Api/Site/ilcorsaronero/title.py +44 -44
  38. StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py +149 -149
  39. StreamingCommunity/Api/Site/mostraguarda/__init__.py +48 -48
  40. StreamingCommunity/Api/Site/mostraguarda/costant.py +18 -18
  41. StreamingCommunity/Api/Site/mostraguarda/film.py +90 -101
  42. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +79 -55
  43. StreamingCommunity/Api/Site/streamingcommunity/costant.py +21 -19
  44. StreamingCommunity/Api/Site/streamingcommunity/film.py +86 -75
  45. StreamingCommunity/Api/Site/streamingcommunity/series.py +259 -207
  46. StreamingCommunity/Api/Site/streamingcommunity/site.py +156 -142
  47. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +124 -124
  48. StreamingCommunity/Api/Template/Class/SearchType.py +101 -101
  49. StreamingCommunity/Api/Template/Util/__init__.py +4 -4
  50. StreamingCommunity/Api/Template/Util/get_domain.py +201 -201
  51. StreamingCommunity/Api/Template/Util/manage_ep.py +178 -178
  52. StreamingCommunity/Api/Template/Util/recall_search.py +37 -37
  53. StreamingCommunity/Api/Template/__init__.py +2 -2
  54. StreamingCommunity/Api/Template/site.py +87 -87
  55. StreamingCommunity/Lib/Downloader/HLS/downloader.py +529 -1008
  56. StreamingCommunity/Lib/Downloader/HLS/proxyes.py +110 -110
  57. StreamingCommunity/Lib/Downloader/HLS/segments.py +446 -573
  58. StreamingCommunity/Lib/Downloader/MP4/downloader.py +181 -155
  59. StreamingCommunity/Lib/Downloader/TOR/downloader.py +297 -295
  60. StreamingCommunity/Lib/Downloader/__init__.py +4 -4
  61. StreamingCommunity/Lib/FFmpeg/__init__.py +4 -4
  62. StreamingCommunity/Lib/FFmpeg/capture.py +170 -170
  63. StreamingCommunity/Lib/FFmpeg/command.py +264 -296
  64. StreamingCommunity/Lib/FFmpeg/util.py +248 -248
  65. StreamingCommunity/Lib/M3U8/__init__.py +5 -5
  66. StreamingCommunity/Lib/M3U8/decryptor.py +164 -164
  67. StreamingCommunity/Lib/M3U8/estimator.py +146 -228
  68. StreamingCommunity/Lib/M3U8/parser.py +666 -666
  69. StreamingCommunity/Lib/M3U8/url_fixer.py +57 -57
  70. StreamingCommunity/Lib/TMBD/__init__.py +1 -1
  71. StreamingCommunity/Lib/TMBD/obj_tmbd.py +39 -39
  72. StreamingCommunity/Lib/TMBD/tmdb.py +345 -345
  73. StreamingCommunity/TelegramHelp/__init__.py +0 -0
  74. StreamingCommunity/TelegramHelp/request_manager.py +82 -0
  75. StreamingCommunity/TelegramHelp/session.py +56 -0
  76. StreamingCommunity/TelegramHelp/telegram_bot.py +561 -0
  77. StreamingCommunity/Upload/update.py +75 -67
  78. StreamingCommunity/Upload/version.py +5 -5
  79. StreamingCommunity/Util/_jsonConfig.py +227 -228
  80. StreamingCommunity/Util/call_stack.py +42 -42
  81. StreamingCommunity/Util/color.py +20 -20
  82. StreamingCommunity/Util/console.py +12 -12
  83. StreamingCommunity/Util/ffmpeg_installer.py +342 -370
  84. StreamingCommunity/Util/headers.py +159 -159
  85. StreamingCommunity/Util/logger.py +61 -61
  86. StreamingCommunity/Util/message.py +36 -64
  87. StreamingCommunity/Util/os.py +500 -507
  88. StreamingCommunity/Util/table.py +271 -228
  89. StreamingCommunity/run.py +352 -245
  90. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.5.dist-info}/LICENSE +674 -674
  91. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.5.dist-info}/METADATA +601 -543
  92. StreamingCommunity-2.5.5.dist-info/RECORD +96 -0
  93. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.5.dist-info}/entry_points.txt +0 -1
  94. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/js_parser.cpython-313.pyc +0 -0
  95. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/js_parser.cpython-39.pyc +0 -0
  96. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/util.cpython-313.pyc +0 -0
  97. StreamingCommunity/Api/Player/Helper/Vixcloud/__pycache__/util.cpython-39.pyc +0 -0
  98. StreamingCommunity/Api/Player/__pycache__/ddl.cpython-313.pyc +0 -0
  99. StreamingCommunity/Api/Player/__pycache__/ddl.cpython-39.pyc +0 -0
  100. StreamingCommunity/Api/Player/__pycache__/maxstream.cpython-313.pyc +0 -0
  101. StreamingCommunity/Api/Player/__pycache__/maxstream.cpython-39.pyc +0 -0
  102. StreamingCommunity/Api/Player/__pycache__/supervideo.cpython-313.pyc +0 -0
  103. StreamingCommunity/Api/Player/__pycache__/supervideo.cpython-39.pyc +0 -0
  104. StreamingCommunity/Api/Player/__pycache__/vixcloud.cpython-313.pyc +0 -0
  105. StreamingCommunity/Api/Player/__pycache__/vixcloud.cpython-39.pyc +0 -0
  106. StreamingCommunity/Api/Site/1337xx/__pycache__/__init__.cpython-313.pyc +0 -0
  107. StreamingCommunity/Api/Site/1337xx/__pycache__/__init__.cpython-39.pyc +0 -0
  108. StreamingCommunity/Api/Site/1337xx/__pycache__/costant.cpython-313.pyc +0 -0
  109. StreamingCommunity/Api/Site/1337xx/__pycache__/costant.cpython-39.pyc +0 -0
  110. StreamingCommunity/Api/Site/1337xx/__pycache__/site.cpython-313.pyc +0 -0
  111. StreamingCommunity/Api/Site/1337xx/__pycache__/site.cpython-39.pyc +0 -0
  112. StreamingCommunity/Api/Site/1337xx/__pycache__/title.cpython-313.pyc +0 -0
  113. StreamingCommunity/Api/Site/1337xx/__pycache__/title.cpython-39.pyc +0 -0
  114. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/__init__.cpython-313.pyc +0 -0
  115. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/__init__.cpython-39.pyc +0 -0
  116. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/costant.cpython-313.pyc +0 -0
  117. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/costant.cpython-39.pyc +0 -0
  118. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/film.cpython-313.pyc +0 -0
  119. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/film.cpython-39.pyc +0 -0
  120. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/site.cpython-313.pyc +0 -0
  121. StreamingCommunity/Api/Site/altadefinizionegratis/__pycache__/site.cpython-39.pyc +0 -0
  122. StreamingCommunity/Api/Site/animeunity/__pycache__/__init__.cpython-313.pyc +0 -0
  123. StreamingCommunity/Api/Site/animeunity/__pycache__/__init__.cpython-39.pyc +0 -0
  124. StreamingCommunity/Api/Site/animeunity/__pycache__/costant.cpython-313.pyc +0 -0
  125. StreamingCommunity/Api/Site/animeunity/__pycache__/costant.cpython-39.pyc +0 -0
  126. StreamingCommunity/Api/Site/animeunity/__pycache__/film_serie.cpython-313.pyc +0 -0
  127. StreamingCommunity/Api/Site/animeunity/__pycache__/film_serie.cpython-39.pyc +0 -0
  128. StreamingCommunity/Api/Site/animeunity/__pycache__/site.cpython-313.pyc +0 -0
  129. StreamingCommunity/Api/Site/animeunity/__pycache__/site.cpython-39.pyc +0 -0
  130. StreamingCommunity/Api/Site/animeunity/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  131. StreamingCommunity/Api/Site/animeunity/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  132. StreamingCommunity/Api/Site/cb01new/__pycache__/__init__.cpython-313.pyc +0 -0
  133. StreamingCommunity/Api/Site/cb01new/__pycache__/__init__.cpython-39.pyc +0 -0
  134. StreamingCommunity/Api/Site/cb01new/__pycache__/costant.cpython-313.pyc +0 -0
  135. StreamingCommunity/Api/Site/cb01new/__pycache__/costant.cpython-39.pyc +0 -0
  136. StreamingCommunity/Api/Site/cb01new/__pycache__/film.cpython-313.pyc +0 -0
  137. StreamingCommunity/Api/Site/cb01new/__pycache__/film.cpython-39.pyc +0 -0
  138. StreamingCommunity/Api/Site/cb01new/__pycache__/site.cpython-313.pyc +0 -0
  139. StreamingCommunity/Api/Site/cb01new/__pycache__/site.cpython-39.pyc +0 -0
  140. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/__init__.cpython-313.pyc +0 -0
  141. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/__init__.cpython-39.pyc +0 -0
  142. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/costant.cpython-313.pyc +0 -0
  143. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/costant.cpython-39.pyc +0 -0
  144. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/series.cpython-313.pyc +0 -0
  145. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/series.cpython-39.pyc +0 -0
  146. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/site.cpython-313.pyc +0 -0
  147. StreamingCommunity/Api/Site/ddlstreamitaly/__pycache__/site.cpython-39.pyc +0 -0
  148. StreamingCommunity/Api/Site/ddlstreamitaly/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  149. StreamingCommunity/Api/Site/ddlstreamitaly/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  150. StreamingCommunity/Api/Site/guardaserie/__pycache__/__init__.cpython-313.pyc +0 -0
  151. StreamingCommunity/Api/Site/guardaserie/__pycache__/__init__.cpython-39.pyc +0 -0
  152. StreamingCommunity/Api/Site/guardaserie/__pycache__/costant.cpython-313.pyc +0 -0
  153. StreamingCommunity/Api/Site/guardaserie/__pycache__/costant.cpython-39.pyc +0 -0
  154. StreamingCommunity/Api/Site/guardaserie/__pycache__/series.cpython-313.pyc +0 -0
  155. StreamingCommunity/Api/Site/guardaserie/__pycache__/series.cpython-39.pyc +0 -0
  156. StreamingCommunity/Api/Site/guardaserie/__pycache__/site.cpython-313.pyc +0 -0
  157. StreamingCommunity/Api/Site/guardaserie/__pycache__/site.cpython-39.pyc +0 -0
  158. StreamingCommunity/Api/Site/guardaserie/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  159. StreamingCommunity/Api/Site/guardaserie/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  160. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/__init__.cpython-313.pyc +0 -0
  161. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/__init__.cpython-39.pyc +0 -0
  162. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/costant.cpython-313.pyc +0 -0
  163. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/costant.cpython-39.pyc +0 -0
  164. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/site.cpython-313.pyc +0 -0
  165. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/site.cpython-39.pyc +0 -0
  166. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/title.cpython-313.pyc +0 -0
  167. StreamingCommunity/Api/Site/ilcorsaronero/__pycache__/title.cpython-39.pyc +0 -0
  168. StreamingCommunity/Api/Site/ilcorsaronero/util/__pycache__/ilCorsarScraper.cpython-313.pyc +0 -0
  169. StreamingCommunity/Api/Site/ilcorsaronero/util/__pycache__/ilCorsarScraper.cpython-39.pyc +0 -0
  170. StreamingCommunity/Api/Site/mostraguarda/__pycache__/__init__.cpython-313.pyc +0 -0
  171. StreamingCommunity/Api/Site/mostraguarda/__pycache__/__init__.cpython-39.pyc +0 -0
  172. StreamingCommunity/Api/Site/mostraguarda/__pycache__/costant.cpython-313.pyc +0 -0
  173. StreamingCommunity/Api/Site/mostraguarda/__pycache__/costant.cpython-39.pyc +0 -0
  174. StreamingCommunity/Api/Site/mostraguarda/__pycache__/film.cpython-313.pyc +0 -0
  175. StreamingCommunity/Api/Site/mostraguarda/__pycache__/film.cpython-39.pyc +0 -0
  176. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/__init__.cpython-313.pyc +0 -0
  177. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/__init__.cpython-39.pyc +0 -0
  178. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/costant.cpython-313.pyc +0 -0
  179. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/costant.cpython-39.pyc +0 -0
  180. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/film.cpython-313.pyc +0 -0
  181. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/film.cpython-39.pyc +0 -0
  182. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/series.cpython-313.pyc +0 -0
  183. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/series.cpython-39.pyc +0 -0
  184. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/site.cpython-313.pyc +0 -0
  185. StreamingCommunity/Api/Site/streamingcommunity/__pycache__/site.cpython-39.pyc +0 -0
  186. StreamingCommunity/Api/Site/streamingcommunity/util/__pycache__/ScrapeSerie.cpython-313.pyc +0 -0
  187. StreamingCommunity/Api/Site/streamingcommunity/util/__pycache__/ScrapeSerie.cpython-39.pyc +0 -0
  188. StreamingCommunity/Api/Template/Class/__pycache__/SearchType.cpython-313.pyc +0 -0
  189. StreamingCommunity/Api/Template/Class/__pycache__/SearchType.cpython-39.pyc +0 -0
  190. StreamingCommunity/Api/Template/Util/__pycache__/__init__.cpython-313.pyc +0 -0
  191. StreamingCommunity/Api/Template/Util/__pycache__/__init__.cpython-39.pyc +0 -0
  192. StreamingCommunity/Api/Template/Util/__pycache__/get_domain.cpython-313.pyc +0 -0
  193. StreamingCommunity/Api/Template/Util/__pycache__/get_domain.cpython-39.pyc +0 -0
  194. StreamingCommunity/Api/Template/Util/__pycache__/manage_ep.cpython-313.pyc +0 -0
  195. StreamingCommunity/Api/Template/Util/__pycache__/manage_ep.cpython-39.pyc +0 -0
  196. StreamingCommunity/Api/Template/Util/__pycache__/recall_search.cpython-313.pyc +0 -0
  197. StreamingCommunity/Api/Template/Util/__pycache__/recall_search.cpython-39.pyc +0 -0
  198. StreamingCommunity/Api/Template/__pycache__/__init__.cpython-313.pyc +0 -0
  199. StreamingCommunity/Api/Template/__pycache__/__init__.cpython-39.pyc +0 -0
  200. StreamingCommunity/Api/Template/__pycache__/site.cpython-313.pyc +0 -0
  201. StreamingCommunity/Api/Template/__pycache__/site.cpython-39.pyc +0 -0
  202. StreamingCommunity/Lib/Downloader/HLS/__pycache__/downloader.cpython-313.pyc +0 -0
  203. StreamingCommunity/Lib/Downloader/HLS/__pycache__/downloader.cpython-39.pyc +0 -0
  204. StreamingCommunity/Lib/Downloader/HLS/__pycache__/proxyes.cpython-313.pyc +0 -0
  205. StreamingCommunity/Lib/Downloader/HLS/__pycache__/proxyes.cpython-39.pyc +0 -0
  206. StreamingCommunity/Lib/Downloader/HLS/__pycache__/segments.cpython-313.pyc +0 -0
  207. StreamingCommunity/Lib/Downloader/HLS/__pycache__/segments.cpython-39.pyc +0 -0
  208. StreamingCommunity/Lib/Downloader/MP4/__pycache__/downloader.cpython-313.pyc +0 -0
  209. StreamingCommunity/Lib/Downloader/MP4/__pycache__/downloader.cpython-39.pyc +0 -0
  210. StreamingCommunity/Lib/Downloader/TOR/__pycache__/downloader.cpython-313.pyc +0 -0
  211. StreamingCommunity/Lib/Downloader/TOR/__pycache__/downloader.cpython-39.pyc +0 -0
  212. StreamingCommunity/Lib/Downloader/__pycache__/__init__.cpython-313.pyc +0 -0
  213. StreamingCommunity/Lib/Downloader/__pycache__/__init__.cpython-39.pyc +0 -0
  214. StreamingCommunity/Lib/FFmpeg/__pycache__/__init__.cpython-313.pyc +0 -0
  215. StreamingCommunity/Lib/FFmpeg/__pycache__/__init__.cpython-39.pyc +0 -0
  216. StreamingCommunity/Lib/FFmpeg/__pycache__/capture.cpython-313.pyc +0 -0
  217. StreamingCommunity/Lib/FFmpeg/__pycache__/capture.cpython-39.pyc +0 -0
  218. StreamingCommunity/Lib/FFmpeg/__pycache__/command.cpython-313.pyc +0 -0
  219. StreamingCommunity/Lib/FFmpeg/__pycache__/command.cpython-39.pyc +0 -0
  220. StreamingCommunity/Lib/FFmpeg/__pycache__/util.cpython-313.pyc +0 -0
  221. StreamingCommunity/Lib/FFmpeg/__pycache__/util.cpython-39.pyc +0 -0
  222. StreamingCommunity/Lib/M3U8/__pycache__/__init__.cpython-313.pyc +0 -0
  223. StreamingCommunity/Lib/M3U8/__pycache__/__init__.cpython-39.pyc +0 -0
  224. StreamingCommunity/Lib/M3U8/__pycache__/decryptor.cpython-313.pyc +0 -0
  225. StreamingCommunity/Lib/M3U8/__pycache__/decryptor.cpython-39.pyc +0 -0
  226. StreamingCommunity/Lib/M3U8/__pycache__/estimator.cpython-313.pyc +0 -0
  227. StreamingCommunity/Lib/M3U8/__pycache__/estimator.cpython-39.pyc +0 -0
  228. StreamingCommunity/Lib/M3U8/__pycache__/parser.cpython-313.pyc +0 -0
  229. StreamingCommunity/Lib/M3U8/__pycache__/parser.cpython-39.pyc +0 -0
  230. StreamingCommunity/Lib/M3U8/__pycache__/url_fixer.cpython-313.pyc +0 -0
  231. StreamingCommunity/Lib/M3U8/__pycache__/url_fixer.cpython-39.pyc +0 -0
  232. StreamingCommunity/Lib/TMBD/__pycache__/__init__.cpython-313.pyc +0 -0
  233. StreamingCommunity/Lib/TMBD/__pycache__/__init__.cpython-39.pyc +0 -0
  234. StreamingCommunity/Lib/TMBD/__pycache__/obj_tmbd.cpython-313.pyc +0 -0
  235. StreamingCommunity/Lib/TMBD/__pycache__/obj_tmbd.cpython-39.pyc +0 -0
  236. StreamingCommunity/Lib/TMBD/__pycache__/tmdb.cpython-313.pyc +0 -0
  237. StreamingCommunity/Lib/TMBD/__pycache__/tmdb.cpython-39.pyc +0 -0
  238. StreamingCommunity/Upload/__pycache__/update.cpython-313.pyc +0 -0
  239. StreamingCommunity/Upload/__pycache__/update.cpython-39.pyc +0 -0
  240. StreamingCommunity/Upload/__pycache__/version.cpython-313.pyc +0 -0
  241. StreamingCommunity/Upload/__pycache__/version.cpython-39.pyc +0 -0
  242. StreamingCommunity/Util/__pycache__/_jsonConfig.cpython-313.pyc +0 -0
  243. StreamingCommunity/Util/__pycache__/_jsonConfig.cpython-39.pyc +0 -0
  244. StreamingCommunity/Util/__pycache__/call_stack.cpython-313.pyc +0 -0
  245. StreamingCommunity/Util/__pycache__/call_stack.cpython-39.pyc +0 -0
  246. StreamingCommunity/Util/__pycache__/color.cpython-313.pyc +0 -0
  247. StreamingCommunity/Util/__pycache__/color.cpython-39.pyc +0 -0
  248. StreamingCommunity/Util/__pycache__/console.cpython-313.pyc +0 -0
  249. StreamingCommunity/Util/__pycache__/console.cpython-39.pyc +0 -0
  250. StreamingCommunity/Util/__pycache__/ffmpeg_installer.cpython-313.pyc +0 -0
  251. StreamingCommunity/Util/__pycache__/ffmpeg_installer.cpython-39.pyc +0 -0
  252. StreamingCommunity/Util/__pycache__/headers.cpython-313.pyc +0 -0
  253. StreamingCommunity/Util/__pycache__/headers.cpython-39.pyc +0 -0
  254. StreamingCommunity/Util/__pycache__/logger.cpython-313.pyc +0 -0
  255. StreamingCommunity/Util/__pycache__/logger.cpython-39.pyc +0 -0
  256. StreamingCommunity/Util/__pycache__/message.cpython-313.pyc +0 -0
  257. StreamingCommunity/Util/__pycache__/message.cpython-39.pyc +0 -0
  258. StreamingCommunity/Util/__pycache__/os.cpython-313.pyc +0 -0
  259. StreamingCommunity/Util/__pycache__/os.cpython-39.pyc +0 -0
  260. StreamingCommunity/Util/__pycache__/table.cpython-313.pyc +0 -0
  261. StreamingCommunity/Util/__pycache__/table.cpython-39.pyc +0 -0
  262. StreamingCommunity/__pycache__/__init__.cpython-313.pyc +0 -0
  263. StreamingCommunity/__pycache__/__init__.cpython-39.pyc +0 -0
  264. StreamingCommunity/__pycache__/run.cpython-313.pyc +0 -0
  265. StreamingCommunity/__pycache__/run.cpython-39.pyc +0 -0
  266. StreamingCommunity-2.5.2.dist-info/RECORD +0 -264
  267. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.5.dist-info}/WHEEL +0 -0
  268. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.5.dist-info}/top_level.txt +0 -0
@@ -1,371 +1,343 @@
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}")
1
+ # 24.01.2024
2
+
3
+ import os
4
+ import glob
5
+ import gzip
6
+ import shutil
7
+ import tarfile
8
+ import zipfile
9
+ import logging
10
+ import platform
11
+ import subprocess
12
+ from typing import Optional, Tuple
13
+
14
+
15
+ # External library
16
+ import requests
17
+ from rich.console import Console
18
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
19
+
20
+
21
+ # Variable
22
+ console = Console()
23
+
24
+
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/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-win32-{arch}.gz',
29
+ 'file_extension': '.gz',
30
+ 'executables': ['ffmpeg-win32-{arch}', 'ffprobe-win32-{arch}']
31
+ },
32
+ 'darwin': {
33
+ 'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
34
+ 'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-darwin-{arch}.gz',
35
+ 'file_extension': '.gz',
36
+ 'executables': ['ffmpeg-darwin-{arch}', 'ffprobe-darwin-{arch}']
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/latest/download/ffmpeg-linux-{arch}.gz',
41
+ 'file_extension': '.gz',
42
+ 'executables': ['ffmpeg-linux-{arch}', 'ffprobe-linux-{arch}']
43
+ }
44
+ }
45
+
46
+
47
+ class FFMPEGDownloader:
48
+ def __init__(self):
49
+ self.os_name = self._detect_system()
50
+ self.arch = self._detect_arch()
51
+ self.home_dir = os.path.expanduser('~')
52
+ self.base_dir = self._get_base_directory()
53
+
54
+ def _detect_system(self) -> str:
55
+ """
56
+ Detect and normalize the operating system name.
57
+
58
+ Returns:
59
+ str: Normalized operating system name ('windows', 'darwin', or 'linux')
60
+ """
61
+ system = platform.system().lower()
62
+ if system in FFMPEG_CONFIGURATION:
63
+ return system
64
+ raise ValueError(f"Unsupported operating system: {system}")
65
+
66
+ def _detect_arch(self) -> str:
67
+ """
68
+ Detect and normalize the system architecture.
69
+
70
+ Returns:
71
+ str: Normalized architecture name (e.g., 'x86_64', 'arm64')
72
+ """
73
+ machine = platform.machine().lower()
74
+ arch_map = {
75
+ 'amd64': 'x64',
76
+ 'x86_64': 'x64',
77
+ 'x64': 'x64',
78
+ 'arm64': 'arm64',
79
+ 'aarch64': 'arm64',
80
+ 'armv7l': 'arm',
81
+ 'i386': 'ia32',
82
+ 'i686': 'ia32'
83
+ }
84
+ return arch_map.get(machine, machine)
85
+
86
+ def _get_base_directory(self) -> str:
87
+ """
88
+ Get and create the base directory for storing FFmpeg binaries.
89
+
90
+ Returns:
91
+ str: Path to the base directory
92
+ """
93
+ base_dir = FFMPEG_CONFIGURATION[self.os_name]['base_dir'](self.home_dir)
94
+ os.makedirs(base_dir, exist_ok=True)
95
+ return base_dir
96
+
97
+ def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
98
+ """
99
+ Check if FFmpeg binaries already exist in the base directory.
100
+ Enhanced to check both the binary directory and system paths on macOS.
101
+ """
102
+ config = FFMPEG_CONFIGURATION[self.os_name]
103
+ executables = config['executables']
104
+ found_executables = []
105
+
106
+ # For macOS, check both binary directory and system paths
107
+ if self.os_name == 'darwin':
108
+ potential_paths = [
109
+ '/usr/local/bin',
110
+ '/opt/homebrew/bin',
111
+ '/usr/bin',
112
+ self.base_dir
113
+ ]
114
+
115
+ for executable in executables:
116
+ found = None
117
+ for path in potential_paths:
118
+ full_path = os.path.join(path, executable)
119
+ if os.path.exists(full_path) and os.access(full_path, os.X_OK):
120
+ found = full_path
121
+ break
122
+ found_executables.append(found)
123
+ else:
124
+
125
+ # Original behavior for other operating systems
126
+ for executable in executables:
127
+ exe_paths = glob.glob(os.path.join(self.base_dir, executable))
128
+ found_executables.append(exe_paths[0] if exe_paths else None)
129
+
130
+ return tuple(found_executables) if len(found_executables) == 3 else (None, None, None)
131
+
132
+ def _get_latest_version(self, repo: str) -> Optional[str]:
133
+ """
134
+ Get the latest FFmpeg version from the GitHub releases page.
135
+
136
+ Returns:
137
+ Optional[str]: The latest version string, or None if retrieval fails.
138
+ """
139
+ try:
140
+ # Use GitHub API to fetch the latest release
141
+ response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest')
142
+ response.raise_for_status()
143
+ latest_release = response.json()
144
+
145
+ # Extract the tag name or version from the release
146
+ return latest_release.get('tag_name')
147
+
148
+ except Exception as e:
149
+ logging.error(f"Unable to get version from GitHub: {e}")
150
+ return None
151
+
152
+ def _download_file(self, url: str, destination: str) -> bool:
153
+ """
154
+ Download a file from URL with a Rich progress bar display.
155
+
156
+ Parameters:
157
+ url (str): The URL to download the file from. Should be a direct download link.
158
+ destination (str): Local file path where the downloaded file will be saved.
159
+
160
+ Returns:
161
+ bool: True if download was successful, False otherwise.
162
+ """
163
+ try:
164
+ response = requests.get(url, stream=True)
165
+ response.raise_for_status()
166
+ total_size = int(response.headers.get('content-length', 0))
167
+
168
+ with open(destination, 'wb') as file, \
169
+ Progress(
170
+ SpinnerColumn(),
171
+ TextColumn("[progress.description]{task.description}"),
172
+ BarColumn(),
173
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
174
+ TimeRemainingColumn()
175
+ ) as progress:
176
+
177
+ download_task = progress.add_task("[green]Downloading FFmpeg", total=total_size)
178
+ for chunk in response.iter_content(chunk_size=8192):
179
+ size = file.write(chunk)
180
+ progress.update(download_task, advance=size)
181
+ return True
182
+ except Exception as e:
183
+ logging.error(f"Download error: {e}")
184
+ return False
185
+
186
+ def _extract_and_copy_binaries(self, archive_path: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
187
+ """
188
+ Extract FFmpeg binaries from the downloaded archive and copy them to the base directory.
189
+
190
+ Parameters:
191
+ archive_path (str): Path to the downloaded archive file
192
+
193
+ Returns:
194
+ Tuple[Optional[str], Optional[str], Optional[str]]: Paths to the extracted ffmpeg,
195
+ ffprobe, and ffplay executables. Returns None for each executable that couldn't be extracted.
196
+ """
197
+ try:
198
+ extraction_path = os.path.join(self.base_dir, 'temp_extract')
199
+ os.makedirs(extraction_path, exist_ok=True)
200
+
201
+ if archive_path.endswith('.zip'):
202
+ with zipfile.ZipFile(archive_path, 'r') as zip_ref:
203
+ zip_ref.extractall(extraction_path)
204
+ elif archive_path.endswith('.tar.xz'):
205
+ with tarfile.open(archive_path) as tar_ref:
206
+ tar_ref.extractall(extraction_path)
207
+ elif archive_path.endswith('.gz'):
208
+ file_name = os.path.basename(archive_path)[:-3] # Remove extension .gz
209
+ output_path = os.path.join(extraction_path, file_name)
210
+ with gzip.open(archive_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
211
+ shutil.copyfileobj(f_in, f_out)
212
+ else:
213
+ raise ValueError("Unsupported archive format")
214
+
215
+ config = FFMPEG_CONFIGURATION[self.os_name]
216
+ executables = config['executables']
217
+ found_paths = []
218
+
219
+ for executable in executables:
220
+ exe_paths = glob.glob(os.path.join(extraction_path, '**', executable), recursive=True)
221
+ if exe_paths:
222
+ dest_path = os.path.join(self.base_dir, executable)
223
+ shutil.copy2(exe_paths[0], dest_path)
224
+
225
+ if self.os_name != 'windows':
226
+ os.chmod(dest_path, 0o755)
227
+
228
+ found_paths.append(dest_path)
229
+ else:
230
+ found_paths.append(None)
231
+
232
+ shutil.rmtree(extraction_path, ignore_errors=True)
233
+ os.remove(archive_path)
234
+
235
+ return tuple(found_paths) if len(found_paths) == 3 else (None, None, None)
236
+
237
+ except Exception as e:
238
+ logging.error(f"Extraction/copy error: {e}")
239
+ return None, None, None
240
+
241
+ def download(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
242
+ """
243
+ Main method to download and set up FFmpeg executables.
244
+
245
+ Returns:
246
+ Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
247
+ Returns None for each executable that couldn't be downloaded or set up.
248
+ """
249
+ config = FFMPEG_CONFIGURATION[self.os_name]
250
+ executables = [exe.format(arch=self.arch) for exe in config['executables']]
251
+
252
+ for executable in executables:
253
+ download_url = f"https://github.com/eugeneware/ffmpeg-static/releases/latest/download/{executable}.gz"
254
+ download_path = os.path.join(self.base_dir, f"{executable}.gz")
255
+ final_path = os.path.join(self.base_dir, executable)
256
+
257
+ console.print(f"[bold blue]Downloading {executable} from GitHub[/]")
258
+ if not self._download_file(download_url, download_path):
259
+ return None, None, None
260
+
261
+ with gzip.open(download_path, 'rb') as f_in, open(final_path, 'wb') as f_out:
262
+ shutil.copyfileobj(f_in, f_out)
263
+
264
+ os.chmod(final_path, 0o755)
265
+ os.remove(download_path)
266
+
267
+ return (
268
+ os.path.join(self.base_dir, executables[0]),
269
+ os.path.join(self.base_dir, executables[1]),
270
+ None
271
+ )
272
+
273
+ def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
274
+ """
275
+ Check for FFmpeg executables in the system and download them if not found.
276
+ Enhanced detection for macOS systems.
277
+
278
+ Returns:
279
+ Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
280
+ """
281
+ try:
282
+ system_platform = platform.system().lower()
283
+
284
+ # Special handling for macOS
285
+ if system_platform == 'darwin':
286
+
287
+ # Common installation paths on macOS
288
+ potential_paths = [
289
+ '/usr/local/bin', # Homebrew default
290
+ '/opt/homebrew/bin', # Apple Silicon Homebrew
291
+ '/usr/bin', # System default
292
+ os.path.expanduser('~/Applications/binary'), # Custom installation
293
+ '/Applications/binary' # Custom installation
294
+ ]
295
+
296
+ for path in potential_paths:
297
+ ffmpeg_path = os.path.join(path, 'ffmpeg')
298
+ ffprobe_path = os.path.join(path, 'ffprobe')
299
+ ffplay_path = os.path.join(path, 'ffplay')
300
+
301
+ if (os.path.exists(ffmpeg_path) and os.path.exists(ffprobe_path) and
302
+ os.access(ffmpeg_path, os.X_OK) and os.access(ffprobe_path, os.X_OK)):
303
+
304
+ # Return found executables, with ffplay being optional
305
+ ffplay_path = ffplay_path if os.path.exists(ffplay_path) else None
306
+ return ffmpeg_path, ffprobe_path, ffplay_path
307
+
308
+ # Windows detection
309
+ elif system_platform == 'windows':
310
+ try:
311
+ ffmpeg_path = subprocess.check_output(
312
+ ['where', 'ffmpeg'], stderr=subprocess.DEVNULL, text=True
313
+ ).strip().split('\n')[0]
314
+
315
+ ffprobe_path = subprocess.check_output(
316
+ ['where', 'ffprobe'], stderr=subprocess.DEVNULL, text=True
317
+ ).strip().split('\n')[0]
318
+
319
+ ffplay_path = subprocess.check_output(
320
+ ['where', 'ffplay'], stderr=subprocess.DEVNULL, text=True
321
+ ).strip().split('\n')[0]
322
+
323
+ return ffmpeg_path, ffprobe_path, ffplay_path
324
+
325
+ except subprocess.CalledProcessError:
326
+ logging.warning("One or more FFmpeg binaries were not found with command where")
327
+
328
+ # Linux detection
329
+ else:
330
+ ffmpeg_path = shutil.which('ffmpeg')
331
+ ffprobe_path = shutil.which('ffprobe')
332
+ ffplay_path = shutil.which('ffplay')
333
+
334
+ if ffmpeg_path and ffprobe_path:
335
+ return ffmpeg_path, ffprobe_path, ffplay_path
336
+
337
+ # If executables were not found, attempt to download FFmpeg
338
+ downloader = FFMPEGDownloader()
339
+ return downloader.download()
340
+
341
+ except Exception as e:
342
+ logging.error(f"Error checking or downloading FFmpeg executables: {e}")
371
343
  return None, None, None