ぼざろクリーチャーシリーズ DB 兼 API(自分用)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

update_db.py 8.3 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import json
  2. import os
  3. import random
  4. import string
  5. import time
  6. from dataclasses import dataclass
  7. from datetime import datetime
  8. import mysql.connector
  9. import requests
  10. def main (
  11. ) -> None:
  12. conn = mysql.connector.connect (host = os.environ['MYSQL_HOST'],
  13. user = os.environ['MYSQL_USER'],
  14. password = os.environ['MYSQL_PASS'])
  15. video_dao = VideoDao (conn)
  16. api_data = search_nico_by_tags (['伊地知ニジカ', 'ぼざろクリーチャーシリーズ'])
  17. update_video_table (video_dao, api_data)
  18. # TODO: 書くこと
  19. def update_video_table (
  20. video_dao,
  21. api_data: list[dict],
  22. ) -> None:
  23. videos = video_dao.fetch_all ()
  24. # TODO: 書くこと
  25. def fetch_comments (
  26. video_id: str,
  27. ) -> list:
  28. headers = { 'X-Frontend-Id': '6',
  29. 'X-Frontend-Version': '0' }
  30. action_track_id = (
  31. ''.join (random.choice (string.ascii_letters + string.digits)
  32. for _ in range (10))
  33. + '_'
  34. + str (random.randrange (10 ** 12, 10 ** 13)))
  35. url = (f"https://www.nicovideo.jp/api/watch/v3_guest/{ video_id }"
  36. + f"?actionTrackId={ action_track_id }")
  37. res = requests.post (url, headers = headers, timeout = 60).json ()
  38. try:
  39. nv_comment = res['data']['comment']['nvComment']
  40. except KeyError:
  41. return []
  42. if nv_comment is None:
  43. return []
  44. headers = { 'X-Frontend-Id': '6',
  45. 'X-Frontend-Version': '0',
  46. 'Content-Type': 'application/json' }
  47. params = { 'params': nv_comment['params'],
  48. 'additionals': { },
  49. 'threadKey': nv_comment['threadKey'] }
  50. url = nv_comment['server'] + '/v1/threads'
  51. res = (requests.post (url, json.dumps (params),
  52. headers = headers,
  53. timeout = 60)
  54. .json ())
  55. try:
  56. return res['data']['threads'][1]['comments']
  57. except (IndexError, KeyError):
  58. return []
  59. def search_nico_by_tag (
  60. tag: str,
  61. ) -> list[dict]:
  62. return search_nico_by_tags ([tag])
  63. def search_nico_by_tags (
  64. tags: list[str],
  65. ) -> list[dict]:
  66. url = ('https://snapshot.search.nicovideo.jp'
  67. + '/api/v2/snapshot/video/contents/search')
  68. # TODO: 年月日の設定ができてゐなぃのと,100 件までしか取得できなぃので何とかすること
  69. query_filter = json.dumps ({ 'type': 'or',
  70. 'filters': [
  71. { 'type': 'range',
  72. 'field': 'startTime',
  73. 'from': f"{year}-{start}T00:00:00+09:00",
  74. 'to': f"{year}-{end}T23:59:59+09:00",
  75. 'include_lower': True }] })
  76. params: dict[str, int | str]
  77. params = { 'q': ' OR '.join (tags),
  78. 'targets': 'tagsExact',
  79. '_sort': '-viewCounter',
  80. 'fields': 'contentId,title,tags,viewCounter,startTime',
  81. '_limit': 100,
  82. 'jsonFilter': query_filter }
  83. res = requests.get (url, params = params, timeout = 60).json ()
  84. return res['data']
  85. class VideoDao:
  86. def __init__ (
  87. self,
  88. conn
  89. ):
  90. self.conn = conn
  91. def fetch (
  92. self,
  93. video_id: int,
  94. with_relation_tables: bool = True,
  95. ) -> VideoDto | None:
  96. with self.conn.cursor () as c:
  97. c.execute ("""
  98. SELECT
  99. id,
  100. code,
  101. title,
  102. description,
  103. uploaded_at,
  104. deleted_at
  105. FROM
  106. videos
  107. WHERE
  108. id = %s
  109. ORDER BY
  110. id""", video_id)
  111. row = c.fetchone ()
  112. if row is None:
  113. return None
  114. return self._create_dto_from_row (row, with_relation_tables)
  115. def fetch_all (
  116. self,
  117. with_relation_tables: bool = True,
  118. ) -> list[VideoDto]:
  119. with self.conn.cursor () as c:
  120. c.execute ("""
  121. SELECT
  122. id,
  123. code,
  124. title,
  125. description,
  126. uploaded_at,
  127. deleted_at
  128. FROM
  129. videos
  130. ORDER BY
  131. id""")
  132. videos: list[VideoDto] = []
  133. for row in c.fetchall ():
  134. videos.append (self._create_dto_from_row (row, with_relation_tables))
  135. return videos
  136. def _create_dto_from_row (
  137. self,
  138. row,
  139. with_relation_tables: bool,
  140. ) -> VideoDto:
  141. video = VideoDto (id_ = row['id'],
  142. code = row['code'],
  143. title = row['title'],
  144. description = row['description'],
  145. uploaded_at = row['uploaded_at'],
  146. deleted_at = row['deleted_at'],
  147. video_tags = None,
  148. comments = None,
  149. video_histories = None)
  150. if with_relation_tables:
  151. video.video_tags = VideoTagDao (self.conn).fetch_by_video_id (video.id_, False)
  152. for i in range (len (video.video_tags)):
  153. video.video_tags[i].video = video
  154. video.comments = CommentDao (self.conn).fetch_by_video_id (video.id_, False)
  155. for i in range (len (video.comments)):
  156. video.comments[i].video = video
  157. video.video_histories = VideoHistoryDao (self.conn).fetch_by_video_id (video.id_, False)
  158. for i in range (len (video.video_histories)):
  159. video.video_histories[i].video = video
  160. return video
  161. @dataclass (slots = True)
  162. class VideoDto:
  163. id_: int
  164. code: str
  165. title: str
  166. description: str
  167. uploaded_at: datetime
  168. deleted_at: datetime | None
  169. video_tags: list[VideoTagDto] | None
  170. comments: list[CommentDto] | None
  171. video_histories: list[VideoHistoryDto] | None
  172. class VideoTagDao:
  173. def __init__ (
  174. self,
  175. conn,
  176. ):
  177. self.conn = conn
  178. def fetch_by_video_id (
  179. self,
  180. video_id: int,
  181. with_relation_tables: bool = True,
  182. ) -> list[VideoTagDto]:
  183. with self.conn.cursor () as c:
  184. c.execute ("""
  185. SELECT
  186. id,
  187. video_id,
  188. tag_id,
  189. tagged_at,
  190. untagged_at
  191. FROM
  192. video_tags
  193. WHERE
  194. video_id = %s
  195. ORDER BY
  196. id""", video_id)
  197. video_tags: list[VideoTagDto] = []
  198. for row in c.fetchall ():
  199. video_tags.append (self._create_dto_from_row (row, with_relation_tables))
  200. return video_tags
  201. def _create_dto_from_row (
  202. self,
  203. row,
  204. with_relation_tables: bool,
  205. ) -> VideoTagDto:
  206. video_tag = VideoTagDto (id_ = row['id'],
  207. video_id = row['video_id'],
  208. tag_id = row['tag_id'],
  209. tagged_at = row['tagged_at'],
  210. untagged_at = row['untagged_at'],
  211. video = None,
  212. tag = None)
  213. if with_relation_tables:
  214. video_tag.video = VideoDao (self.conn).fetch (video_tag.video_id, True)
  215. video_tag.tag = TagDao (self.conn).fetch (video_tag.tag_id, True)
  216. return video_tag
  217. @dataclass (slots = True)
  218. class VideoTagDto:
  219. id_: int
  220. video_id: int
  221. tag_id: int
  222. tagged_at: datetime
  223. untagged_at: datetime
  224. video: VideoDto
  225. tag: TagDto
  226. if __name__ == '__main__':
  227. main ()