StreamingCommunity 2.5.2__py3-none-any.whl → 2.5.6__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 +172 -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 +150 -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 +200 -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 +260 -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.6.dist-info}/LICENSE +674 -674
  91. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.6.dist-info}/METADATA +601 -543
  92. StreamingCommunity-2.5.6.dist-info/RECORD +96 -0
  93. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.6.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.6.dist-info}/WHEEL +0 -0
  268. {StreamingCommunity-2.5.2.dist-info → StreamingCommunity-2.5.6.dist-info}/top_level.txt +0 -0
@@ -1,507 +1,500 @@
1
- # 24.01.24
2
-
3
- import io
4
- import os
5
- import sys
6
- import time
7
- import shutil
8
- import hashlib
9
- import logging
10
- import platform
11
- import subprocess
12
- import contextlib
13
- import urllib.request
14
- import importlib.metadata
15
-
16
-
17
- # External library
18
- import httpx
19
- from unidecode import unidecode
20
- from pathvalidate import sanitize_filename, sanitize_filepath
21
-
22
-
23
- # Internal utilities
24
- from .ffmpeg_installer import check_ffmpeg
25
- from StreamingCommunity.Util.console import console, msg
26
-
27
-
28
-
29
- class OsManager:
30
- def __init__(self):
31
- self.system = self._detect_system()
32
- self.max_length = self._get_max_length()
33
-
34
- def _detect_system(self) -> str:
35
- """Detect and normalize operating system name."""
36
- system = platform.system().lower()
37
- if system not in ['windows', 'darwin', 'linux']:
38
- raise ValueError(f"Unsupported operating system: {system}")
39
- return system
40
-
41
- def _get_max_length(self) -> int:
42
- """Get max filename length based on OS."""
43
- return 255 if self.system == 'windows' else 4096
44
-
45
- def _normalize_windows_path(self, path: str) -> str:
46
- """Normalize Windows paths."""
47
- if not path or self.system != 'windows':
48
- return path
49
-
50
- # Preserve network paths (UNC and IP-based)
51
- if path.startswith('\\\\') or path.startswith('//'):
52
- return path.replace('/', '\\')
53
-
54
- # Handle drive letters
55
- if len(path) >= 2 and path[1] == ':':
56
- drive = path[0:2]
57
- rest = path[2:].replace('/', '\\').lstrip('\\')
58
- return f"{drive}\\{rest}"
59
-
60
- return path.replace('/', '\\')
61
-
62
- def _normalize_mac_path(self, path: str) -> str:
63
- """Normalize macOS paths."""
64
- if not path or self.system != 'darwin':
65
- return path
66
-
67
- # Convert Windows separators to Unix
68
- normalized = path.replace('\\', '/')
69
-
70
- # Ensure absolute paths start with /
71
- if normalized.startswith('/'):
72
- return os.path.normpath(normalized)
73
-
74
- return normalized
75
-
76
- def get_sanitize_file(self, filename: str) -> str:
77
- """Sanitize filename."""
78
- if not filename:
79
- return filename
80
-
81
- # Decode and sanitize
82
- decoded = unidecode(filename)
83
- sanitized = sanitize_filename(decoded)
84
-
85
- # Split name and extension
86
- name, ext = os.path.splitext(sanitized)
87
-
88
- # Calculate available length for name considering the '...' and extension
89
- max_name_length = self.max_length - len('...') - len(ext)
90
-
91
- # Truncate name if it exceeds the max name length
92
- if len(name) > max_name_length:
93
- name = name[:max_name_length] + '...'
94
-
95
- # Ensure the final file name includes the extension
96
- return name + ext
97
-
98
- def get_sanitize_path(self, path: str) -> str:
99
- """Sanitize complete path."""
100
- if not path:
101
- return path
102
-
103
- # Decode unicode characters
104
- decoded = unidecode(path)
105
-
106
- # Basic path sanitization
107
- sanitized = sanitize_filepath(decoded)
108
-
109
- if self.system == 'windows':
110
- # Handle network paths (UNC or IP-based)
111
- if path.startswith('\\\\') or path.startswith('//'):
112
- parts = path.replace('/', '\\').split('\\')
113
- # Keep server/IP and share name as is
114
- sanitized_parts = parts[:4]
115
- # Sanitize remaining parts
116
- if len(parts) > 4:
117
- sanitized_parts.extend([
118
- self.get_sanitize_file(part)
119
- for part in parts[4:]
120
- if part
121
- ])
122
- return '\\'.join(sanitized_parts)
123
-
124
- # Handle drive letters
125
- elif len(path) >= 2 and path[1] == ':':
126
- drive = path[:2]
127
- rest = path[2:].lstrip('\\').lstrip('/')
128
- path_parts = [drive] + [
129
- self.get_sanitize_file(part)
130
- for part in rest.replace('/', '\\').split('\\')
131
- if part
132
- ]
133
- return '\\'.join(path_parts)
134
-
135
- # Regular path
136
- else:
137
- parts = path.replace('/', '\\').split('\\')
138
- return '\\'.join(p for p in parts if p)
139
- else:
140
- # Handle Unix-like paths (Linux and macOS)
141
- is_absolute = path.startswith('/')
142
- parts = path.replace('\\', '/').split('/')
143
- sanitized_parts = [
144
- self.get_sanitize_file(part)
145
- for part in parts
146
- if part
147
- ]
148
-
149
- result = '/'.join(sanitized_parts)
150
- if is_absolute:
151
- result = '/' + result
152
-
153
- return result
154
-
155
- def create_path(self, path: str, mode: int = 0o755) -> bool:
156
- """
157
- Create directory path with specified permissions.
158
-
159
- Args:
160
- path (str): Path to create.
161
- mode (int, optional): Directory permissions. Defaults to 0o755.
162
-
163
- Returns:
164
- bool: True if path created successfully, False otherwise.
165
- """
166
- try:
167
- sanitized_path = self.get_sanitize_path(path)
168
- os.makedirs(sanitized_path, mode=mode, exist_ok=True)
169
- return True
170
-
171
- except Exception as e:
172
- logging.error(f"Path creation error: {e}")
173
- return False
174
-
175
- def remove_folder(self, folder_path: str) -> bool:
176
- """
177
- Safely remove a folder.
178
-
179
- Args:
180
- folder_path (str): Path of directory to remove.
181
-
182
- Returns:
183
- bool: Removal status.
184
- """
185
- try:
186
- shutil.rmtree(folder_path)
187
- return True
188
-
189
- except OSError as e:
190
- logging.error(f"Folder removal error: {e}")
191
- return False
192
-
193
- def remove_files_except_one(self, folder_path: str, keep_file: str) -> None:
194
- """
195
- Delete all files in a folder except for one specified file.
196
-
197
- Parameters:
198
- - folder_path (str): The path to the folder containing the files.
199
- - keep_file (str): The filename to keep in the folder.
200
- """
201
-
202
- try:
203
- # List all files in the folder
204
- files_in_folder = os.listdir(folder_path)
205
-
206
- # Iterate over each file in the folder
207
- for file_name in files_in_folder:
208
- file_path = os.path.join(folder_path, file_name)
209
-
210
- # Check if the file is not the one to keep and is a regular file
211
- if file_name != keep_file and os.path.isfile(file_path):
212
- os.remove(file_path) # Delete the file
213
-
214
- except Exception as e:
215
- logging.error(f"An error occurred: {e}")
216
- raise
217
-
218
- def check_file(self, file_path: str) -> bool:
219
- """
220
- Check if a file exists at the given file path.
221
-
222
- Parameters:
223
- file_path (str): The path to the file.
224
-
225
- Returns:
226
- bool: True if the file exists, False otherwise.
227
- """
228
- try:
229
- logging.info(f"Check if file exists: {file_path}")
230
- return os.path.exists(file_path)
231
-
232
- except Exception as e:
233
- logging.error(f"An error occurred while checking file existence: {e}")
234
- return False
235
-
236
-
237
- class InternManager():
238
-
239
- def format_file_size(self, size_bytes: float) -> str:
240
- """
241
- Formats a file size from bytes into a human-readable string representation.
242
-
243
- Parameters:
244
- size_bytes (float): Size in bytes to be formatted.
245
-
246
- Returns:
247
- str: Formatted string representing the file size with appropriate unit (B, KB, MB, GB, TB).
248
- """
249
- if size_bytes <= 0:
250
- return "0B"
251
-
252
- units = ['B', 'KB', 'MB', 'GB', 'TB']
253
- unit_index = 0
254
-
255
- while size_bytes >= 1024 and unit_index < len(units) - 1:
256
- size_bytes /= 1024
257
- unit_index += 1
258
-
259
- return f"{size_bytes:.2f} {units[unit_index]}"
260
-
261
- def format_transfer_speed(self, bytes: float) -> str:
262
- """
263
- Formats a transfer speed from bytes per second into a human-readable string representation.
264
-
265
- Parameters:
266
- bytes (float): Speed in bytes per second to be formatted.
267
-
268
- Returns:
269
- str: Formatted string representing the transfer speed with appropriate unit (Bytes/s, KB/s, MB/s).
270
- """
271
- if bytes < 1024:
272
- return f"{bytes:.2f} Bytes/s"
273
- elif bytes < 1024 * 1024:
274
- return f"{bytes / 1024:.2f} KB/s"
275
- else:
276
- return f"{bytes / (1024 * 1024):.2f} MB/s"
277
-
278
- @staticmethod
279
- def check_internet():
280
- while True:
281
- try:
282
- httpx.get("https://www.google.com", timeout=15)
283
- break
284
-
285
- except urllib.error.URLError:
286
- console.log("[bold red]Internet is not available. Waiting...[/bold red]")
287
- time.sleep(5)
288
-
289
-
290
- class OsSummary:
291
-
292
- def __init__(self):
293
- self.ffmpeg_path = None
294
- self.ffprobe_path = None
295
- self.ffplay_path = None
296
-
297
- def get_executable_version(self, command: list):
298
- """
299
- Get the version of a given command-line executable.
300
-
301
- Args:
302
- command (list): The command to run, e.g., `['ffmpeg', '-version']`.
303
-
304
- Returns:
305
- str: The version string of the executable.
306
- """
307
- try:
308
- version_output = subprocess.check_output(command, stderr=subprocess.STDOUT).decode().split('\n')[0]
309
- return version_output.split(" ")[2]
310
-
311
- except (FileNotFoundError, subprocess.CalledProcessError):
312
- console.print(f"{command[0]} not found", style="bold red")
313
- sys.exit(0)
314
-
315
- def check_ffmpeg_location(self, command: list) -> str:
316
- """
317
- Check if a specific executable (ffmpeg or ffprobe) is located using the given command.
318
- Returns the path of the executable or None if not found.
319
- """
320
- try:
321
- result = subprocess.check_output(command, text=True).strip()
322
- return result.split('\n')[0] if result else None
323
-
324
- except subprocess.CalledProcessError:
325
- return None
326
-
327
- def get_library_version(self, lib_name: str):
328
- """
329
- Retrieve the version of a Python library.
330
-
331
- Args:
332
- lib_name (str): The name of the Python library.
333
-
334
- Returns:
335
- str: The library name followed by its version, or `-not installed` if not found.
336
- """
337
- try:
338
- version = importlib.metadata.version(lib_name)
339
- return f"{lib_name}-{version}"
340
-
341
- except importlib.metadata.PackageNotFoundError:
342
- return f"{lib_name}-not installed"
343
-
344
- def download_requirements(self, url: str, filename: str):
345
- """
346
- Download the requirements.txt file from the specified URL if not found locally using requests.
347
-
348
- Args:
349
- url (str): The URL to download the requirements file from.
350
- filename (str): The local filename to save the requirements file as.
351
- """
352
- try:
353
- import requests
354
-
355
- logging.info(f"{filename} not found locally. Downloading from {url}...")
356
- response = requests.get(url)
357
-
358
- if response.status_code == 200:
359
- with open(filename, 'wb') as f:
360
- f.write(response.content)
361
-
362
- else:
363
- logging.error(f"Failed to download {filename}. HTTP Status code: {response.status_code}")
364
- sys.exit(0)
365
-
366
- except Exception as e:
367
- logging.error(f"Failed to download {filename}: {e}")
368
- sys.exit(0)
369
-
370
- def install_library(self, lib_name: str):
371
- """
372
- Install a Python library using pip.
373
-
374
- Args:
375
- lib_name (str): The name of the library to install.
376
- """
377
- try:
378
- console.print(f"Installing {lib_name}...", style="bold yellow")
379
- subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
380
- console.print(f"{lib_name} installed successfully!", style="bold green")
381
-
382
- except subprocess.CalledProcessError as e:
383
- console.print(f"Failed to install {lib_name}: {e}", style="bold red")
384
- sys.exit(1)
385
-
386
- def check_python_version(self):
387
- """
388
- Check if the installed Python is the official CPython distribution.
389
- Exits with a message if not the official version.
390
- """
391
- python_implementation = platform.python_implementation()
392
-
393
- if python_implementation != "CPython":
394
- console.print(f"[bold red]Warning: You are using a non-official Python distribution: {python_implementation}.[/bold red]")
395
- console.print("Please install the official Python from [bold blue]https://www.python.org[/bold blue] and try again.", style="bold yellow")
396
- sys.exit(0)
397
-
398
- def get_system_summary(self):
399
- """
400
- Generate a summary of the system environment.
401
-
402
- Includes:
403
- - Python version and implementation details.
404
- - Operating system and architecture.
405
- - Versions of `ffmpeg` and `ffprobe` executables.
406
- - Installed Python libraries as listed in `requirements.txt`.
407
- """
408
-
409
- # Check if Python is the official CPython
410
- self.check_python_version()
411
-
412
- # Check internet connectivity
413
- InternManager().check_internet()
414
- console.print("[bold blue]System Summary[/bold blue][white]:")
415
-
416
- # Python version and platform
417
- python_version = sys.version.split()[0]
418
- python_implementation = platform.python_implementation()
419
- arch = platform.machine()
420
- os_info = platform.platform()
421
- glibc_version = 'glibc ' + '.'.join(map(str, platform.libc_ver()[1]))
422
-
423
- console.print(f"[cyan]Python[white]: [bold red]{python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})[/bold red]")
424
- logging.info(f"Python: {python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})")
425
-
426
- # Use the 'where' command on Windows and 'which' command on Unix-like systems
427
- system_platform = platform.system().lower()
428
- command = 'where' if system_platform == 'windows' else 'which'
429
-
430
- # Locate ffmpeg and ffprobe from the PATH environment
431
- if self.ffmpeg_path is not None and "binary" not in self.ffmpeg_path:
432
- self.ffmpeg_path = self.check_ffmpeg_location([command, 'ffmpeg'])
433
-
434
- if self.ffprobe_path is not None and "binary" not in self.ffprobe_path:
435
- self.ffprobe_path = self.check_ffmpeg_location([command, 'ffprobe'])
436
-
437
- # If ffmpeg or ffprobe is not located, fall back to using the check_ffmpeg function
438
- if self.ffmpeg_path is None or self.ffprobe_path is None:
439
- self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
440
-
441
- # If still not found, print error and exit
442
- if self.ffmpeg_path is None or self.ffprobe_path is None:
443
- console.log("[red]Can't locate ffmpeg or ffprobe")
444
- sys.exit(0)
445
-
446
- ffmpeg_version = self.get_executable_version([self.ffprobe_path, '-version'])
447
- ffprobe_version = self.get_executable_version([self.ffprobe_path, '-version'])
448
-
449
- console.print(f"[cyan]Path[white]: [red]ffmpeg [bold yellow]'{self.ffmpeg_path}'[/bold yellow][white], [red]ffprobe '[bold yellow]{self.ffprobe_path}'[/bold yellow]")
450
- console.print(f"[cyan]Exe versions[white]: [bold red]ffmpeg {ffmpeg_version}, ffprobe {ffprobe_version}[/bold red]")
451
-
452
- # Check if requirements.txt exists, if not on pyinstaller
453
- if not getattr(sys, 'frozen', False):
454
- requirements_file = 'requirements.txt'
455
-
456
- if not os.path.exists(requirements_file):
457
- self.download_requirements(
458
- 'https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/requirements.txt',
459
- requirements_file
460
- )
461
-
462
- # Read the optional libraries from the requirements file, get only name without version if "library==1.0.0"
463
- optional_libraries = [line.strip().split("=")[0] for line in open(requirements_file, 'r', encoding='utf-8-sig')]
464
-
465
- # Check if libraries are installed and prompt to install missing ones
466
- for lib in optional_libraries:
467
- installed_version = self.get_library_version(lib)
468
-
469
- if 'not installed' in installed_version:
470
- user_response = msg.ask(f"{lib} is not installed. Do you want to install it? (yes/no)", default="y")
471
-
472
- if user_response.lower().strip() in ["yes", "y"]:
473
- self.install_library(lib)
474
-
475
- else:
476
- logging.info(f"Library: {installed_version}")
477
-
478
- console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
479
- logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
480
-
481
-
482
- # OTHER
483
- os_manager = OsManager()
484
- internet_manager = InternManager()
485
- os_summary = OsSummary()
486
-
487
-
488
- @contextlib.contextmanager
489
- def suppress_output():
490
- with contextlib.redirect_stdout(io.StringIO()):
491
- yield
492
-
493
- def compute_sha1_hash(input_string: str) -> str:
494
- """
495
- Computes the SHA-1 hash of the input string.
496
-
497
- Parameters:
498
- - input_string (str): The string to be hashed.
499
-
500
- Returns:
501
- str: The SHA-1 hash of the input string.
502
- """
503
- # Compute the SHA-1 hash
504
- hashed_string = hashlib.sha1(input_string.encode()).hexdigest()
505
-
506
- # Return the hashed string
507
- return hashed_string
1
+ # 24.01.24
2
+
3
+ import io
4
+ import os
5
+ import glob
6
+ import sys
7
+ import time
8
+ import shutil
9
+ import hashlib
10
+ import logging
11
+ import platform
12
+ import subprocess
13
+ import contextlib
14
+ import urllib.request
15
+ import importlib.metadata
16
+
17
+
18
+ # External library
19
+ import httpx
20
+ from unidecode import unidecode
21
+ from pathvalidate import sanitize_filename, sanitize_filepath
22
+
23
+
24
+ # Internal utilities
25
+ from .ffmpeg_installer import check_ffmpeg
26
+ from StreamingCommunity.Util.console import console, msg
27
+
28
+
29
+
30
+ class OsManager:
31
+ def __init__(self):
32
+ self.system = self._detect_system()
33
+ self.max_length = self._get_max_length()
34
+
35
+ def _detect_system(self) -> str:
36
+ """Detect and normalize operating system name."""
37
+ system = platform.system().lower()
38
+ if system not in ['windows', 'darwin', 'linux']:
39
+ raise ValueError(f"Unsupported operating system: {system}")
40
+ return system
41
+
42
+ def _get_max_length(self) -> int:
43
+ """Get max filename length based on OS."""
44
+ return 255 if self.system == 'windows' else 4096
45
+
46
+ def _normalize_windows_path(self, path: str) -> str:
47
+ """Normalize Windows paths."""
48
+ if not path or self.system != 'windows':
49
+ return path
50
+
51
+ # Preserve network paths (UNC and IP-based)
52
+ if path.startswith('\\\\') or path.startswith('//'):
53
+ return path.replace('/', '\\')
54
+
55
+ # Handle drive letters
56
+ if len(path) >= 2 and path[1] == ':':
57
+ drive = path[0:2]
58
+ rest = path[2:].replace('/', '\\').lstrip('\\')
59
+ return f"{drive}\\{rest}"
60
+
61
+ return path.replace('/', '\\')
62
+
63
+ def _normalize_mac_path(self, path: str) -> str:
64
+ """Normalize macOS paths."""
65
+ if not path or self.system != 'darwin':
66
+ return path
67
+
68
+ # Convert Windows separators to Unix
69
+ normalized = path.replace('\\', '/')
70
+
71
+ # Ensure absolute paths start with /
72
+ if normalized.startswith('/'):
73
+ return os.path.normpath(normalized)
74
+
75
+ return normalized
76
+
77
+ def get_sanitize_file(self, filename: str) -> str:
78
+ """Sanitize filename."""
79
+ if not filename:
80
+ return filename
81
+
82
+ # Decode and sanitize
83
+ decoded = unidecode(filename)
84
+ sanitized = sanitize_filename(decoded)
85
+
86
+ # Split name and extension
87
+ name, ext = os.path.splitext(sanitized)
88
+
89
+ # Calculate available length for name considering the '...' and extension
90
+ max_name_length = self.max_length - len('...') - len(ext)
91
+
92
+ # Truncate name if it exceeds the max name length
93
+ if len(name) > max_name_length:
94
+ name = name[:max_name_length] + '...'
95
+
96
+ # Ensure the final file name includes the extension
97
+ return name + ext
98
+
99
+ def get_sanitize_path(self, path: str) -> str:
100
+ """Sanitize complete path."""
101
+ if not path:
102
+ return path
103
+
104
+ # Decode unicode characters
105
+ decoded = unidecode(path)
106
+
107
+ # Basic path sanitization
108
+ sanitized = sanitize_filepath(decoded)
109
+
110
+ if self.system == 'windows':
111
+ # Handle network paths (UNC or IP-based)
112
+ if path.startswith('\\\\') or path.startswith('//'):
113
+ parts = path.replace('/', '\\').split('\\')
114
+ # Keep server/IP and share name as is
115
+ sanitized_parts = parts[:4]
116
+ # Sanitize remaining parts
117
+ if len(parts) > 4:
118
+ sanitized_parts.extend([
119
+ self.get_sanitize_file(part)
120
+ for part in parts[4:]
121
+ if part
122
+ ])
123
+ return '\\'.join(sanitized_parts)
124
+
125
+ # Handle drive letters
126
+ elif len(path) >= 2 and path[1] == ':':
127
+ drive = path[:2]
128
+ rest = path[2:].lstrip('\\').lstrip('/')
129
+ path_parts = [drive] + [
130
+ self.get_sanitize_file(part)
131
+ for part in rest.replace('/', '\\').split('\\')
132
+ if part
133
+ ]
134
+ return '\\'.join(path_parts)
135
+
136
+ # Regular path
137
+ else:
138
+ parts = path.replace('/', '\\').split('\\')
139
+ return '\\'.join(p for p in parts if p)
140
+ else:
141
+ # Handle Unix-like paths (Linux and macOS)
142
+ is_absolute = path.startswith('/')
143
+ parts = path.replace('\\', '/').split('/')
144
+ sanitized_parts = [
145
+ self.get_sanitize_file(part)
146
+ for part in parts
147
+ if part
148
+ ]
149
+
150
+ result = '/'.join(sanitized_parts)
151
+ if is_absolute:
152
+ result = '/' + result
153
+
154
+ return result
155
+
156
+ def create_path(self, path: str, mode: int = 0o755) -> bool:
157
+ """
158
+ Create directory path with specified permissions.
159
+
160
+ Args:
161
+ path (str): Path to create.
162
+ mode (int, optional): Directory permissions. Defaults to 0o755.
163
+
164
+ Returns:
165
+ bool: True if path created successfully, False otherwise.
166
+ """
167
+ try:
168
+ sanitized_path = self.get_sanitize_path(path)
169
+ os.makedirs(sanitized_path, mode=mode, exist_ok=True)
170
+ return True
171
+
172
+ except Exception as e:
173
+ logging.error(f"Path creation error: {e}")
174
+ return False
175
+
176
+ def remove_folder(self, folder_path: str) -> bool:
177
+ """
178
+ Safely remove a folder.
179
+
180
+ Args:
181
+ folder_path (str): Path of directory to remove.
182
+
183
+ Returns:
184
+ bool: Removal status.
185
+ """
186
+ try:
187
+ shutil.rmtree(folder_path)
188
+ return True
189
+
190
+ except OSError as e:
191
+ logging.error(f"Folder removal error: {e}")
192
+ return False
193
+
194
+ def remove_files_except_one(self, folder_path: str, keep_file: str) -> None:
195
+ """
196
+ Delete all files in a folder except for one specified file.
197
+
198
+ Parameters:
199
+ - folder_path (str): The path to the folder containing the files.
200
+ - keep_file (str): The filename to keep in the folder.
201
+ """
202
+
203
+ try:
204
+ # List all files in the folder
205
+ files_in_folder = os.listdir(folder_path)
206
+
207
+ # Iterate over each file in the folder
208
+ for file_name in files_in_folder:
209
+ file_path = os.path.join(folder_path, file_name)
210
+
211
+ # Check if the file is not the one to keep and is a regular file
212
+ if file_name != keep_file and os.path.isfile(file_path):
213
+ os.remove(file_path) # Delete the file
214
+
215
+ except Exception as e:
216
+ logging.error(f"An error occurred: {e}")
217
+ raise
218
+
219
+ def check_file(self, file_path: str) -> bool:
220
+ """
221
+ Check if a file exists at the given file path.
222
+
223
+ Parameters:
224
+ file_path (str): The path to the file.
225
+
226
+ Returns:
227
+ bool: True if the file exists, False otherwise.
228
+ """
229
+ try:
230
+ logging.info(f"Check if file exists: {file_path}")
231
+ return os.path.exists(file_path)
232
+
233
+ except Exception as e:
234
+ logging.error(f"An error occurred while checking file existence: {e}")
235
+ return False
236
+
237
+
238
+ class InternManager():
239
+
240
+ def format_file_size(self, size_bytes: float) -> str:
241
+ """
242
+ Formats a file size from bytes into a human-readable string representation.
243
+
244
+ Parameters:
245
+ size_bytes (float): Size in bytes to be formatted.
246
+
247
+ Returns:
248
+ str: Formatted string representing the file size with appropriate unit (B, KB, MB, GB, TB).
249
+ """
250
+ if size_bytes <= 0:
251
+ return "0B"
252
+
253
+ units = ['B', 'KB', 'MB', 'GB', 'TB']
254
+ unit_index = 0
255
+
256
+ while size_bytes >= 1024 and unit_index < len(units) - 1:
257
+ size_bytes /= 1024
258
+ unit_index += 1
259
+
260
+ return f"{size_bytes:.2f} {units[unit_index]}"
261
+
262
+ def format_transfer_speed(self, bytes: float) -> str:
263
+ """
264
+ Formats a transfer speed from bytes per second into a human-readable string representation.
265
+
266
+ Parameters:
267
+ bytes (float): Speed in bytes per second to be formatted.
268
+
269
+ Returns:
270
+ str: Formatted string representing the transfer speed with appropriate unit (Bytes/s, KB/s, MB/s).
271
+ """
272
+ if bytes < 1024:
273
+ return f"{bytes:.2f} Bytes/s"
274
+ elif bytes < 1024 * 1024:
275
+ return f"{bytes / 1024:.2f} KB/s"
276
+ else:
277
+ return f"{bytes / (1024 * 1024):.2f} MB/s"
278
+
279
+ @staticmethod
280
+ def check_internet():
281
+ while True:
282
+ try:
283
+ httpx.get("https://www.google.com", timeout=15)
284
+ break
285
+
286
+ except urllib.error.URLError:
287
+ console.log("[bold red]Internet is not available. Waiting...[/bold red]")
288
+ time.sleep(5)
289
+
290
+
291
+ class OsSummary:
292
+
293
+ def __init__(self):
294
+ self.ffmpeg_path = None
295
+ self.ffprobe_path = None
296
+ self.ffplay_path = None
297
+
298
+ def get_binary_directory(self):
299
+ """Get the binary directory based on OS."""
300
+ system = platform.system().lower()
301
+ home = os.path.expanduser('~')
302
+
303
+ if system == 'windows':
304
+ return os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary')
305
+ elif system == 'darwin':
306
+ return os.path.join(home, 'Applications', 'binary')
307
+ else: # linux
308
+ return os.path.join(home, '.local', 'bin', 'binary')
309
+
310
+ def check_ffmpeg_location(self, command: list) -> str:
311
+ """
312
+ Check if a specific executable (ffmpeg or ffprobe) is located using the given command.
313
+ Returns the path of the executable or None if not found.
314
+ """
315
+ try:
316
+ result = subprocess.check_output(command, text=True).strip()
317
+ return result.split('\n')[0] if result else None
318
+
319
+ except subprocess.CalledProcessError:
320
+ return None
321
+
322
+ def get_library_version(self, lib_name: str):
323
+ """
324
+ Retrieve the version of a Python library.
325
+
326
+ Args:
327
+ lib_name (str): The name of the Python library.
328
+
329
+ Returns:
330
+ str: The library name followed by its version, or `-not installed` if not found.
331
+ """
332
+ try:
333
+ version = importlib.metadata.version(lib_name)
334
+ return f"{lib_name}-{version}"
335
+
336
+ except importlib.metadata.PackageNotFoundError:
337
+ return f"{lib_name}-not installed"
338
+
339
+ def download_requirements(self, url: str, filename: str):
340
+ """
341
+ Download the requirements.txt file from the specified URL if not found locally using requests.
342
+
343
+ Args:
344
+ url (str): The URL to download the requirements file from.
345
+ filename (str): The local filename to save the requirements file as.
346
+ """
347
+ try:
348
+ import requests
349
+
350
+ logging.info(f"{filename} not found locally. Downloading from {url}...")
351
+ response = requests.get(url)
352
+
353
+ if response.status_code == 200:
354
+ with open(filename, 'wb') as f:
355
+ f.write(response.content)
356
+
357
+ else:
358
+ logging.error(f"Failed to download {filename}. HTTP Status code: {response.status_code}")
359
+ sys.exit(0)
360
+
361
+ except Exception as e:
362
+ logging.error(f"Failed to download {filename}: {e}")
363
+ sys.exit(0)
364
+
365
+ def install_library(self, lib_name: str):
366
+ """
367
+ Install a Python library using pip.
368
+
369
+ Args:
370
+ lib_name (str): The name of the library to install.
371
+ """
372
+ try:
373
+ console.print(f"Installing {lib_name}...", style="bold yellow")
374
+ subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
375
+ console.print(f"{lib_name} installed successfully!", style="bold green")
376
+
377
+ except subprocess.CalledProcessError as e:
378
+ console.print(f"Failed to install {lib_name}: {e}", style="bold red")
379
+ sys.exit(1)
380
+
381
+ def check_python_version(self):
382
+ """
383
+ Check if the installed Python is the official CPython distribution.
384
+ Exits with a message if not the official version.
385
+ """
386
+ python_implementation = platform.python_implementation()
387
+
388
+ if python_implementation != "CPython":
389
+ console.print(f"[bold red]Warning: You are using a non-official Python distribution: {python_implementation}.[/bold red]")
390
+ console.print("Please install the official Python from [bold blue]https://www.python.org[/bold blue] and try again.", style="bold yellow")
391
+ sys.exit(0)
392
+
393
+ def get_system_summary(self):
394
+ self.check_python_version()
395
+ InternManager().check_internet()
396
+
397
+ # Python info
398
+ python_version = sys.version.split()[0]
399
+ python_implementation = platform.python_implementation()
400
+ arch = platform.machine()
401
+ os_info = platform.platform()
402
+ glibc_version = 'glibc ' + '.'.join(map(str, platform.libc_ver()[1]))
403
+
404
+ console.print(f"[cyan]Python[white]: [bold red]{python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})[/bold red]")
405
+ logging.info(f"Python: {python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})")
406
+
407
+ # FFmpeg detection
408
+ binary_dir = self.get_binary_directory()
409
+ system = platform.system().lower()
410
+ arch = platform.machine().lower()
411
+
412
+ # Map architecture names
413
+ arch_map = {
414
+ 'amd64': 'x64',
415
+ 'x86_64': 'x64',
416
+ 'x64': 'x64',
417
+ 'arm64': 'arm64',
418
+ 'aarch64': 'arm64',
419
+ 'armv7l': 'arm',
420
+ 'i386': 'ia32',
421
+ 'i686': 'ia32'
422
+ }
423
+ arch = arch_map.get(arch, arch)
424
+
425
+ # Check binary directory
426
+ if os.path.exists(binary_dir):
427
+
428
+ # Search for any file containing 'ffmpeg' and the architecture
429
+ ffmpeg_files = glob.glob(os.path.join(binary_dir, f'*ffmpeg*{arch}*'))
430
+ ffprobe_files = glob.glob(os.path.join(binary_dir, f'*ffprobe*{arch}*'))
431
+
432
+ if ffmpeg_files and ffprobe_files:
433
+ self.ffmpeg_path = ffmpeg_files[0]
434
+ self.ffprobe_path = ffprobe_files[0]
435
+
436
+ # Set executable permissions if needed
437
+ if system != 'windows':
438
+ os.chmod(self.ffmpeg_path, 0o755)
439
+ os.chmod(self.ffprobe_path, 0o755)
440
+ else:
441
+ self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
442
+ else:
443
+ self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
444
+
445
+ if not self.ffmpeg_path or not self.ffprobe_path:
446
+ console.log("[red]Can't locate ffmpeg or ffprobe")
447
+ sys.exit(0)
448
+
449
+ console.print(f"[cyan]Path[white]: [red]ffmpeg [bold yellow]'{self.ffmpeg_path}'[/bold yellow][white], [red]ffprobe '[bold yellow]{self.ffprobe_path}'[/bold yellow]")
450
+
451
+ # Handle requirements.txt
452
+ if not getattr(sys, 'frozen', False):
453
+ requirements_file = 'requirements.txt'
454
+
455
+ if not os.path.exists(requirements_file):
456
+ self.download_requirements(
457
+ 'https://raw.githubusercontent.com/Arrowar/StreamingCommunity/refs/heads/main/requirements.txt',
458
+ requirements_file
459
+ )
460
+
461
+ optional_libraries = [line.strip().split("=")[0] for line in open(requirements_file, 'r', encoding='utf-8-sig')]
462
+
463
+ for lib in optional_libraries:
464
+ installed_version = self.get_library_version(lib)
465
+ if 'not installed' in installed_version:
466
+ user_response = msg.ask(f"{lib} is not installed. Do you want to install it? (yes/no)", default="y")
467
+ if user_response.lower().strip() in ["yes", "y"]:
468
+ self.install_library(lib)
469
+ else:
470
+ logging.info(f"Library: {installed_version}")
471
+
472
+ #console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
473
+ logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
474
+
475
+
476
+ os_manager = OsManager()
477
+ internet_manager = InternManager()
478
+ os_summary = OsSummary()
479
+
480
+
481
+ @contextlib.contextmanager
482
+ def suppress_output():
483
+ with contextlib.redirect_stdout(io.StringIO()):
484
+ yield
485
+
486
+ def compute_sha1_hash(input_string: str) -> str:
487
+ """
488
+ Computes the SHA-1 hash of the input string.
489
+
490
+ Parameters:
491
+ - input_string (str): The string to be hashed.
492
+
493
+ Returns:
494
+ str: The SHA-1 hash of the input string.
495
+ """
496
+ # Compute the SHA-1 hash
497
+ hashed_string = hashlib.sha1(input_string.encode()).hexdigest()
498
+
499
+ # Return the hashed string
500
+ return hashed_string