|
@@ -1,3 +1,7 @@ |
|
|
|
|
|
""" |
|
|
|
|
|
日次で実行し,ぼざクリ DB を最新に更新する. |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
from __future__ import annotations |
|
|
|
|
|
|
|
|
import json |
|
|
import json |
|
@@ -7,7 +11,7 @@ import string |
|
|
import time |
|
|
import time |
|
|
from dataclasses import dataclass |
|
|
from dataclasses import dataclass |
|
|
from datetime import datetime, timedelta |
|
|
from datetime import datetime, timedelta |
|
|
from typing import TypedDict, cast |
|
|
|
|
|
|
|
|
from typing import Any, TypedDict, cast |
|
|
|
|
|
|
|
|
import mysql.connector |
|
|
import mysql.connector |
|
|
import requests |
|
|
import requests |
|
@@ -16,15 +20,6 @@ DbNull = (None,) |
|
|
DbNullType = tuple[None] |
|
|
DbNullType = tuple[None] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VideoResult (TypedDict): |
|
|
|
|
|
contentId: str |
|
|
|
|
|
title: str |
|
|
|
|
|
tags: list[str] |
|
|
|
|
|
description: str |
|
|
|
|
|
viewCounter: int |
|
|
|
|
|
startTime: str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VideoSearchParam (TypedDict): |
|
|
class VideoSearchParam (TypedDict): |
|
|
q: str |
|
|
q: str |
|
|
targets: str |
|
|
targets: str |
|
@@ -34,8 +29,29 @@ class VideoSearchParam (TypedDict): |
|
|
jsonFilter: str |
|
|
jsonFilter: str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VideoResult (TypedDict): |
|
|
|
|
|
contentId: str |
|
|
|
|
|
title: str |
|
|
|
|
|
tags: list[str] |
|
|
|
|
|
description: str |
|
|
|
|
|
viewCounter: int |
|
|
|
|
|
startTime: str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CommentResult (TypedDict): |
|
|
class CommentResult (TypedDict): |
|
|
pass |
|
|
|
|
|
|
|
|
id: str |
|
|
|
|
|
no: int |
|
|
|
|
|
vposMs: int |
|
|
|
|
|
body: str |
|
|
|
|
|
commands: list[str] |
|
|
|
|
|
userId: str |
|
|
|
|
|
isPremium: bool |
|
|
|
|
|
score: int |
|
|
|
|
|
postedAt: str |
|
|
|
|
|
nicoruCount: int |
|
|
|
|
|
nicoruId: Any |
|
|
|
|
|
source: str |
|
|
|
|
|
isMyPost: bool |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main ( |
|
|
def main ( |
|
@@ -50,10 +66,13 @@ def main ( |
|
|
tag_dao = TagDao (conn) |
|
|
tag_dao = TagDao (conn) |
|
|
video_tag_dao = VideoTagDao (conn) |
|
|
video_tag_dao = VideoTagDao (conn) |
|
|
video_history_dao = VideoHistoryDao (conn) |
|
|
video_history_dao = VideoHistoryDao (conn) |
|
|
|
|
|
comment_dao = CommentDao (conn) |
|
|
|
|
|
user_dao = UserDao (conn) |
|
|
|
|
|
|
|
|
api_data = search_nico_by_tags (['伊地知ニジカ', 'ぼざろクリーチャーシリーズ']) |
|
|
api_data = search_nico_by_tags (['伊地知ニジカ', 'ぼざろクリーチャーシリーズ']) |
|
|
|
|
|
|
|
|
update_tables (video_dao, tag_dao, video_tag_dao, video_history_dao, api_data, now) |
|
|
|
|
|
|
|
|
update_tables (video_dao, tag_dao, video_tag_dao, video_history_dao, comment_dao, user_dao, |
|
|
|
|
|
api_data, now) |
|
|
|
|
|
|
|
|
# TODO: 書くこと |
|
|
# TODO: 書くこと |
|
|
|
|
|
|
|
@@ -63,9 +82,12 @@ def update_tables ( |
|
|
tag_dao: TagDao, |
|
|
tag_dao: TagDao, |
|
|
video_tag_dao: VideoTagDao, |
|
|
video_tag_dao: VideoTagDao, |
|
|
video_history_dao: VideoHistoryDao, |
|
|
video_history_dao: VideoHistoryDao, |
|
|
|
|
|
comment_dao: CommentDao, |
|
|
|
|
|
user_dao: UserDao, |
|
|
api_data: list[VideoResult], |
|
|
api_data: list[VideoResult], |
|
|
now: datetime, |
|
|
now: datetime, |
|
|
) -> None: |
|
|
) -> None: |
|
|
|
|
|
video_ids: list[int] = [] |
|
|
for datum in api_data: |
|
|
for datum in api_data: |
|
|
video = VideoDto (code = datum['contentId'], |
|
|
video = VideoDto (code = datum['contentId'], |
|
|
title = datum['title'], |
|
|
title = datum['title'], |
|
@@ -73,6 +95,7 @@ def update_tables ( |
|
|
uploaded_at = datetime.fromisoformat (datum['startTime'])) |
|
|
uploaded_at = datetime.fromisoformat (datum['startTime'])) |
|
|
video_dao.upsert (video, False) |
|
|
video_dao.upsert (video, False) |
|
|
if video.id_ is not None: |
|
|
if video.id_ is not None: |
|
|
|
|
|
video_ids.append (video.id_) |
|
|
video_history = VideoHistoryDto (video_id = video.id_, |
|
|
video_history = VideoHistoryDto (video_id = video.id_, |
|
|
fetched_at = now, |
|
|
fetched_at = now, |
|
|
views_count = datum['viewCounter']) |
|
|
views_count = datum['viewCounter']) |
|
@@ -99,17 +122,37 @@ def update_tables ( |
|
|
tag_id = tag.id_, |
|
|
tag_id = tag.id_, |
|
|
tagged_at = now) |
|
|
tagged_at = now) |
|
|
video_tag_dao.insert (video_tag, False) |
|
|
video_tag_dao.insert (video_tag, False) |
|
|
# TODO: コメント取得すること |
|
|
|
|
|
|
|
|
|
|
|
# TODO: 削除処理,存在しなぃ動画リストを取得した上で行ふこと |
|
|
|
|
|
# video_dao.delete (video_ids, now) |
|
|
|
|
|
|
|
|
|
|
|
# TODO: 書くこと |
|
|
|
|
|
|
|
|
for com in fetch_comments (video.code): |
|
|
|
|
|
user = user_dao.fetch_by_code (com['userId']) |
|
|
|
|
|
if user is None: |
|
|
|
|
|
user = UserDto (code = com['userId']) |
|
|
|
|
|
user_dao.insert (user) |
|
|
|
|
|
if video.id_ is not None and user.id_ is not None: |
|
|
|
|
|
comment = CommentDto (video_id = video.id_, |
|
|
|
|
|
comment_no = com['no'], |
|
|
|
|
|
user_id = user.id_, |
|
|
|
|
|
content = com['body'], |
|
|
|
|
|
posted_at = datetime.fromisoformat (com['postedAt']), |
|
|
|
|
|
nico_count = com['nicoruCount'], |
|
|
|
|
|
vpos_ms = com['vposMs']) |
|
|
|
|
|
comment_dao.upsert (comment, False) |
|
|
|
|
|
|
|
|
|
|
|
alive_video_ids = [d['contentId'] for d in api_data] |
|
|
|
|
|
|
|
|
|
|
|
lost_video_ids: list[int] = [] |
|
|
|
|
|
videos = video_dao.fetch_alive () |
|
|
|
|
|
for video in videos: |
|
|
|
|
|
if video.id_ is not None and video.id_ not in alive_video_ids: |
|
|
|
|
|
lost_video_ids.append (video.id_) |
|
|
|
|
|
|
|
|
|
|
|
video_dao.delete (lost_video_ids, now) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch_comments ( |
|
|
def fetch_comments ( |
|
|
video_id: str, |
|
|
|
|
|
) -> list: |
|
|
|
|
|
|
|
|
video_code: str, |
|
|
|
|
|
) -> list[CommentResult]: |
|
|
|
|
|
time.sleep (1.2) |
|
|
|
|
|
|
|
|
headers = { 'X-Frontend-Id': '6', |
|
|
headers = { 'X-Frontend-Id': '6', |
|
|
'X-Frontend-Version': '0' } |
|
|
'X-Frontend-Version': '0' } |
|
|
|
|
|
|
|
@@ -119,7 +162,7 @@ def fetch_comments ( |
|
|
+ '_' |
|
|
+ '_' |
|
|
+ str (random.randrange (10 ** 12, 10 ** 13))) |
|
|
+ str (random.randrange (10 ** 12, 10 ** 13))) |
|
|
|
|
|
|
|
|
url = (f"https://www.nicovideo.jp/api/watch/v3_guest/{ video_id }" |
|
|
|
|
|
|
|
|
url = (f"https://www.nicovideo.jp/api/watch/v3_guest/{ video_code }" |
|
|
+ f"?actionTrackId={ action_track_id }") |
|
|
+ f"?actionTrackId={ action_track_id }") |
|
|
|
|
|
|
|
|
res = requests.post (url, headers = headers, timeout = 60).json () |
|
|
res = requests.post (url, headers = headers, timeout = 60).json () |
|
@@ -253,6 +296,27 @@ class VideoDao: |
|
|
videos.append (self._create_dto_from_row (row, with_relation_tables)) |
|
|
videos.append (self._create_dto_from_row (row, with_relation_tables)) |
|
|
return videos |
|
|
return videos |
|
|
|
|
|
|
|
|
|
|
|
def fetch_alive ( |
|
|
|
|
|
self, |
|
|
|
|
|
) -> list[VideoDto]: |
|
|
|
|
|
with self.conn.cursor () as c: |
|
|
|
|
|
c.execute (""" |
|
|
|
|
|
SELECT |
|
|
|
|
|
id, |
|
|
|
|
|
code, |
|
|
|
|
|
title, |
|
|
|
|
|
description, |
|
|
|
|
|
uploaded_at, |
|
|
|
|
|
deleted_at |
|
|
|
|
|
FROM |
|
|
|
|
|
videos |
|
|
|
|
|
WHERE |
|
|
|
|
|
deleted_at IS NULL""") |
|
|
|
|
|
videos: list[VideoDto] = [] |
|
|
|
|
|
for row in c.fetchall (): |
|
|
|
|
|
videos.append (self._create_dto_from_row (row, False)) |
|
|
|
|
|
return videos |
|
|
|
|
|
|
|
|
def upsert ( |
|
|
def upsert ( |
|
|
self, |
|
|
self, |
|
|
video: VideoDto, |
|
|
video: VideoDto, |
|
@@ -836,6 +900,58 @@ class CommentDto: |
|
|
nico_count: int = 0 |
|
|
nico_count: int = 0 |
|
|
vpos_ms: int | DbNullType = DbNull |
|
|
vpos_ms: int | DbNullType = DbNull |
|
|
video: VideoDto | None = None |
|
|
video: VideoDto | None = None |
|
|
|
|
|
user: UserDto | None = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserDao: |
|
|
|
|
|
def __init__ ( |
|
|
|
|
|
self, |
|
|
|
|
|
conn, |
|
|
|
|
|
): |
|
|
|
|
|
self.conn = conn |
|
|
|
|
|
|
|
|
|
|
|
def fetch_by_code ( |
|
|
|
|
|
self, |
|
|
|
|
|
user_code: str |
|
|
|
|
|
) -> UserDto | None: |
|
|
|
|
|
with self.conn.cursor () as c: |
|
|
|
|
|
c.execute (""" |
|
|
|
|
|
SELECT |
|
|
|
|
|
id, |
|
|
|
|
|
code |
|
|
|
|
|
FROM |
|
|
|
|
|
users |
|
|
|
|
|
WHERE |
|
|
|
|
|
code = %s""", user_code) |
|
|
|
|
|
row = c.fetchone () |
|
|
|
|
|
if row is None: |
|
|
|
|
|
return None |
|
|
|
|
|
return self._create_dto_from_row (row) |
|
|
|
|
|
|
|
|
|
|
|
def insert ( |
|
|
|
|
|
self, |
|
|
|
|
|
user: UserDto, |
|
|
|
|
|
) -> None: |
|
|
|
|
|
with self.conn.cursor () as c: |
|
|
|
|
|
c.execute (""" |
|
|
|
|
|
INSERT INTO |
|
|
|
|
|
users(code) |
|
|
|
|
|
VALUES |
|
|
|
|
|
(%s)""", user.code) |
|
|
|
|
|
user.id_ = c.lastrowid |
|
|
|
|
|
|
|
|
|
|
|
def _create_dto_from_row ( |
|
|
|
|
|
self, |
|
|
|
|
|
row, |
|
|
|
|
|
) -> UserDto: |
|
|
|
|
|
return UserDto (id_ = row['id'], |
|
|
|
|
|
code = row['code']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass (slots = True) |
|
|
|
|
|
class UserDto: |
|
|
|
|
|
code: str |
|
|
|
|
|
id_: int | None = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
if __name__ == '__main__': |
|
|