bitchute_facade.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. import requests
  2. import hogumathi_app.content_system as h_cs
  3. import hogumathi_app.view_model as h_vm
  4. VIDEOS_APP="http://localhost:5006"
  5. def get_video_collections ():
  6. url = f'{VIDEOS_APP}/channels.json'
  7. resp = requests.get(url)
  8. return resp.json()
  9. def get_video_collection (collection_id):
  10. url = f'{VIDEOS_APP}/channel/{collection_id}/videos.json'
  11. resp = requests.get(url)
  12. coll_data = resp.json()['data']
  13. def to_feed_item (c):
  14. if c['media']['is_archived']:
  15. video = h_vm.MediaItem(
  16. type = 'video',
  17. preview_image_url = c['poster'],
  18. url = c['media']['url'],
  19. content_type = c['media']['type'],
  20. width = c['media']['width'] or 480,
  21. height = c['media']['height'] or 360,
  22. duration_ms = int(c['duration']) * 1000
  23. )
  24. else:
  25. video = h_vm.MediaItem(
  26. type = 'video',
  27. preview_image_url = c['poster'],
  28. url = c['media']['embed_url'],
  29. content_type = c['media']['type'],
  30. width = c['media']['width'] or 480,
  31. height = c['media']['height'] or 360,
  32. duration_ms = int(c['duration']) * 1000
  33. )
  34. fi = h_vm.FeedItem(
  35. id = c['guid'],
  36. handle = coll_data['id'],
  37. display_name = c['author'],
  38. created_at = c['pub_date'],
  39. videos = [video],
  40. title = c['title'],
  41. text = c['description'],
  42. url = c['link'],
  43. author_url = c['author_url'],
  44. source_url = c['source_link']
  45. )
  46. return fi
  47. feed_items = list(map(to_feed_item, coll_data['items']))
  48. return feed_items
  49. def test_video_collection ():
  50. colls = get_video_collections()
  51. print(colls['data'][32].keys())
  52. coll = get_video_collection(colls['data'][32]['id'])
  53. return coll
  54. #print(coll['data']['items'][1].keys())
  55. #print(coll['data']['items'][1]['media'])
  56. def search_videos_cards_collection (q, collection_id):
  57. # FIXME cards may not be the best format but it's all we support
  58. url = f'{VIDEOS_APP}/videos/search.cards.json'
  59. params = dict(
  60. channel = collection_id,
  61. q = q
  62. )
  63. # card logic from search note cards.
  64. resp = requests.get(url, params=params)
  65. if resp.status_code != 200:
  66. return
  67. resp_json = resp.json()
  68. cards = resp_json['cards']
  69. def to_feed_item (search_result):
  70. # different from normal collection
  71. # FIXME this uses Os-specific path sep
  72. channel_and_video_id = search_result['id']
  73. # FIXME not provided by videos_app yet
  74. #video_channel = search_result['channel']
  75. c = search_result['card']
  76. card_id = c['id']
  77. # work-around since search result ID also includes channel
  78. video_id = card_id
  79. #content_url = f'{NOTES_APP}/notes/{collection_id}.html#note={note_id}&card={card_id}'
  80. # FIXME we need to update notes app to include the collection_id
  81. #content_url = c['content_source'].replace(':notes-app', NOTES_APP)
  82. #content_url += f'&card={card_id}'
  83. content_url = f'{VIDEOS_APP}/channel/{collection_id}/video/{video_id}.html'
  84. source_url = c['content_source']
  85. fi = h_vm.FeedItem(
  86. id = card_id,
  87. display_name = "Notes App",
  88. handle = collection_id,
  89. created_at = c['created_at'],
  90. title = c['title'],
  91. text = c['content'],
  92. url = content_url,
  93. source_url = source_url
  94. )
  95. return fi
  96. feed_items = list(map(to_feed_item, cards))
  97. return feed_items
  98. NOTES_APP = 'http://localhost:5000'
  99. def get_note_cards_collection (note_id, collection_id='daily'):
  100. """
  101. This is a good use case where we could directly query models,
  102. Rather than over HTTP.
  103. Store or Service Layer needs to support multi-process locking or similar.
  104. """
  105. url = f'{NOTES_APP}/{collection_id}/note/{note_id}/cards'
  106. resp = requests.get(url)
  107. if resp.status_code != 200:
  108. return
  109. resp_json = resp.json()
  110. cards = resp_json['cards']
  111. def to_feed_item (c):
  112. card_id = c['id']
  113. note_id = c['note_id']
  114. content_url = f'{NOTES_APP}/notes/{collection_id}.html#note={note_id}&card={card_id}'
  115. # FIXME we need to update notes app to include the collection_id
  116. #content_url = c['content_source'].replace(':notes-app', NOTES_APP)
  117. #content_url += f'&card={card_id}'
  118. fi = h_vm.FeedItem(
  119. id = card_id,
  120. display_name = "Notes App",
  121. handle = collection_id,
  122. created_at = c['created_at'],
  123. title = c['title'],
  124. text = c['content'],
  125. url = content_url
  126. )
  127. return fi
  128. feed_items = list(map(to_feed_item, cards))
  129. return feed_items
  130. #@h_cs.query("notes:cards:search", weight=1000)
  131. def search_note_cards_collection (q, collection_id='daily'):
  132. url = f'{NOTES_APP}/{collection_id}/notes/cards/search'
  133. params = dict(
  134. q = q
  135. )
  136. resp = requests.get(url, params=params)
  137. if resp.status_code != 200:
  138. return
  139. resp_json = resp.json()
  140. cards = resp_json['cards']
  141. def to_feed_item (search_result):
  142. # different from normal collection
  143. c = search_result['card']
  144. # FIXME we should add this to notes_app
  145. #note_id = search_result['id']
  146. card_id = c['id']
  147. note_id = c['note_id']
  148. content_url = f'{NOTES_APP}/notes/{collection_id}.html#note={note_id}&card={card_id}'
  149. # FIXME we need to update notes app to include the collection_id
  150. #content_url = c['content_source'].replace(':notes-app', NOTES_APP)
  151. #content_url += f'&card={card_id}'
  152. fi = h_vm.FeedItem(
  153. id = card_id,
  154. display_name = "Notes App",
  155. handle = collection_id,
  156. created_at = c['created_at'],
  157. title = c['title'],
  158. text = c['content'],
  159. url = content_url
  160. )
  161. return fi
  162. feed_items = list(map(to_feed_item, cards))
  163. return feed_items
  164. #@h_cs.command("notes:cards:collection:prepend:<note_id>", weight=1000)
  165. def post_notes_card_prepend (note_id, text, collection_id='daily', should_create=True):
  166. """
  167. This is no different than posting a Tweet.
  168. Logically adds an item to the beginning of the collection.
  169. In the near future the intents will be manually approved but for now it's just a normal write.
  170. We might need to add a token of some sort as well for auth.
  171. """
  172. url = f'{NOTES_APP}/{collection_id}/intent/prepend-text/{note_id}'
  173. params = dict(
  174. text = text,
  175. should_create = should_create
  176. )
  177. resp = requests.get(url, params=params)
  178. return
  179. def get_librivox_books ():
  180. """
  181. https://librivox.org/api/info
  182. """
  183. pass
  184. def register_content_sources ():
  185. """
  186. bitchute:channel:
  187. bitchute:video:
  188. bitchute:profile:user:
  189. bitchute:videos:channel:
  190. bitchute:comments:video:
  191. """
  192. h_cs.register_content_source("notes:cards:collection:", get_note_cards_collection, id_pattern="([^:]+)")
  193. h_cs.register_content_source("notes:cards:search", search_note_cards_collection, id_pattern="")
  194. #h_cs.register_content_command("notes:cards:collection:prepend", post_notes_card_prepend)
  195. #h_cs.register_content_query("notes:cards:search", search_note_cards_collection, id_pattern="")
  196. #
  197. h_cs.register_content_source("videos:collection:", get_video_collection, id_pattern="([^:]+)")
  198. h_cs.register_content_source("videos:cards:search", search_videos_cards_collection, id_pattern="")
  199. def get_bitchute_comments ():
  200. """
  201. script
  202. initComments(
  203. 'https://commentfreely.bitchute.com',
  204. 'eyJwcm9maWxlX2lkIjogImFub255bW91cyIsICJvd25lcl9pZCI6ICJ2UkI1eFpXVW5EYlAiLCAiZGlzcGxheV9uYW1lIjogImFub255bW91cyIsICJ0aHJlYWRfaWQiOiAiYmNfMnFDQ3dyWm9kcXVxIiwgImljb25fdXJsIjogIi9zdGF0aWMvdjE0MS9pbWFnZXMvYmxhbmstcHJvZmlsZS5wbmciLCAiY2ZfaXNfYWRtaW4iOiAiZmFsc2UiLCAiY2hhbm5lbF9pZCI6ICJhZ0R1aVcxQWhXeHoifQ== 1f280c339d11ce063d204d66f4fe38fa938474290994899d5128d3a2ee79c471 1679618698',
  205. 'anonymous'
  206. function initComments(cf_url, cf_auth, currentUserId,
  207. profilePictureURL, commentCountDeprecated, refreshAction, isThreadAdmin, isSupporter, isBlocked) {
  208. getComments: function(success, error) {
  209. var isFirstCall = !window.getCommentsTime;
  210. getCommentsTime = Date.now();
  211. $.ajax({
  212. type: 'post',
  213. url: cf_url + '/api/get_comments/',
  214. data: {
  215. cf_auth: cf_auth,
  216. commentCount: (isFirstCall) ? 0 : localCommentCount || -1,
  217. isNameValuesArrays: true
  218. },
  219. success: function(comments) {
  220. localCommentCount = comments.values.length;
  221. commentCount = comments.normalizedCommentCount;
  222. lastCallTime = comments.callTime;
  223. dynamicCacheSeconds = comments.dynamicSeconds;
  224. success(comments)
  225. },
  226. e
  227. Request headers:
  228. authority: commentfreely.bitchute.com
  229. origin: https://www.bitchute.com
  230. referer: https://www.bitchute.com/
  231. content-type: application/x-www-form-urlencoded; charset=UTF-8
  232. Response
  233. {
  234. "names": [
  235. "id",
  236. "parent",
  237. "created",
  238. "modified",
  239. "content",
  240. "pings",
  241. "creator",
  242. "fullname",
  243. "created_by_admin",
  244. "created_by_current_user",
  245. "up_vote_count",
  246. "down_vote_count",
  247. "user_vote",
  248. "is_new",
  249. "profile_picture_url",
  250. "attachments"
  251. ],
  252. "values": [
  253. [
  254. "0LAnLMLmA66dSLbK0ur25CPoPITCsId9eE46",
  255. null,
  256. "2023-03-24 00:19:44.371714+00:00",
  257. null,
  258. "Love you Blue",
  259. [],
  260. "mW6Q8LlCDs7Y",
  261. "Joelkenimer",
  262. false,
  263. false,
  264. 5,
  265. 2,
  266. null,
  267. false,
  268. "/static/v141/images/blank-profile.png",
  269. []
  270. ],
  271. [
  272. "k9KG622jF7IJ6bJvOiqpE9HdYgvl254DbE3M",
  273. null,
  274. "2023-03-24 00:22:12.367094+00:00",
  275. null,
  276. "OK pls send us a link for Odysee if that happens.",
  277. [],
  278. "OTjfEnZ1HrAC",
  279. "danortego",
  280. false,
  281. false,
  282. 8,
  283. 0,
  284. null,
  285. false,
  286. "/static/v141/images/blank-profile.png",
  287. []
  288. ],
  289. [
  290. "z3EdQTt21UCVmqeeymvuN0BV1vnvJpTs4COR",
  291. null,
  292. "2023-03-24 00:23:40.374960+00:00",
  293. null,
  294. "I'm very happy about that C21 and Ryu",
  295. [],
  296. "qgjwPrLKPkDN",
  297. "MayQ",
  298. false,
  299. false,
  300. 4,
  301. 1,
  302. null,
  303. false,
  304. "/static/v141/images/blank-profile.png",
  305. []
  306. ],
  307. [
  308. "wdGCZbdx9aCwZ4fDGvAg0tk58wEKS1L481Te",
  309. null,
  310. "2023-03-24 00:24:06.963831+00:00",
  311. null,
  312. "What is Odysee?",
  313. [],
  314. "NNTNz25N0fwU",
  315. "eseme",
  316. false,
  317. false,
  318. 3,
  319. 1,
  320. null,
  321. false,
  322. "/static/v141/images/blank-profile.png",
  323. []
  324. ],
  325. [
  326. "69qwpTIezaJCb0AHunNBghQMPFcXWowWWiK0",
  327. null,
  328. "2023-03-24 00:24:10.178391+00:00",
  329. null,
  330. "I am really starting to get why this has to be a soft exposure! The normies are going to literally freak the FARK out! Thanks BW your dedication to getting this out is truly appreciated!",
  331. [],
  332. "vcWaw0oRGYPZ",
  333. "3nd5laveryQ",
  334. false,
  335. false,
  336. 6,
  337. 1,
  338. null,
  339. false,
  340. "https://static-3.bitchute.com/live/profile_images/vcWaw0oRGYPZ/nhl3wJhdEu8OYdvTaGOVXBy7_medium.jpg",
  341. []
  342. ],
  343. [
  344. "wr5uoJppgCBrHn21eIydrAEHQ6ZEhn7nn7tR",
  345. null,
  346. "2023-03-24 00:25:43.415416+00:00",
  347. null,
  348. "Thank you to both Christian21 and bluewater. I'm glad that Ryushin apologized, we all make mistakes, none of us is perfect.",
  349. [],
  350. "7EVG4eljblWD",
  351. "kg78bv2",
  352. false,
  353. false,
  354. 5,
  355. 1,
  356. null,
  357. false,
  358. "/static/v141/images/blank-profile.png",
  359. []
  360. ],
  361. [
  362. "2bNNBVb6ySQ0tNHkNoURaWavXPyL6tmE0dkP",
  363. "wdGCZbdx9aCwZ4fDGvAg0tk58wEKS1L481Te",
  364. "2023-03-24 00:27:15.508892+00:00",
  365. null,
  366. "It is an app like Rumble and BitChute that Bluewater is on.",
  367. [],
  368. "DAu6hPe36k5K",
  369. "PJStitcher",
  370. false,
  371. false,
  372. 2,
  373. 1,
  374. null,
  375. false,
  376. "/static/v141/images/blank-profile.png",
  377. []
  378. ],
  379. [
  380. "opFpX4MxSdZYMgXn7m0V8HFoahZ9TOF3XIwd",
  381. null,
  382. "2023-03-24 00:32:36.503395+00:00",
  383. null,
  384. "💜💜💜",
  385. [],
  386. "zC9exgm75dJG",
  387. "Janeybell",
  388. false,
  389. false,
  390. 1,
  391. 0,
  392. null,
  393. false,
  394. "/static/v141/images/blank-profile.png",
  395. []
  396. ],
  397. [
  398. "1XEXyQ5Gs8Wqi7JNCMIqpivvMcwfl5jEKZKV",
  399. null,
  400. "2023-03-24 00:34:58.928846+00:00",
  401. null,
  402. "Thank you for this Blue and Christian21!",
  403. [],
  404. "b1cOBqmTurdZ",
  405. "fuchsia",
  406. false,
  407. false,
  408. 0,
  409. 0,
  410. null,
  411. false,
  412. "https://static-3.bitchute.com/live/profile_images/b1cOBqmTurdZ/pfykURW9xNIULWdFaCNtQQoa_medium.jpg",
  413. []
  414. ],
  415. [
  416. "j4VhOHiDfCFzY78XWr7FyxjCZV3Fcxmt9ORd",
  417. "wdGCZbdx9aCwZ4fDGvAg0tk58wEKS1L481Te",
  418. "2023-03-24 00:36:22.977951+00:00",
  419. null,
  420. "https://odysee.com/@Bluewater:e",
  421. [],
  422. "S39g9uIZsV8L",
  423. "lupin in the green",
  424. false,
  425. false,
  426. 1,
  427. 0,
  428. null,
  429. false,
  430. "https://static-3.bitchute.com/live/profile_images/S39g9uIZsV8L/E61gvkIIRWiJQE2aNducAcuv_medium.jpg",
  431. []
  432. ]
  433. ],
  434. "callTime": "1679618290.1224425",
  435. "dynamicSeconds": 300,
  436. "normalizedCommentCount": 10,
  437. "isUniversalPin": false,
  438. "pinnedCommentId": null
  439. }
  440. """
  441. pass