| @@ -8,16 +8,19 @@ import wave | |||
| from datetime import datetime, timedelta | |||
| from enum import Enum, auto | |||
| from typing import Callable, TypedDict | |||
| from io import BytesIO | |||
| import cv2 | |||
| import emoji | |||
| import ephem | |||
| import numpy as np | |||
| import pygame | |||
| import pygame.gfxdraw | |||
| import pytchat | |||
| import requests | |||
| from cv2 import VideoCapture | |||
| from ephem import Moon, Observer, Sun | |||
| from pydub import AudioSegment | |||
| from pygame import Rect, Surface | |||
| from pygame.font import Font | |||
| from pygame.mixer import Sound | |||
| @@ -50,6 +53,7 @@ def main ( | |||
| x = CWindow.WIDTH * 3 / 4, | |||
| y = CWindow.HEIGHT - 120, | |||
| balloon = balloon) | |||
| Video (game, 'snack_time.mp4').play () | |||
| CurrentTime (game, SYSTEM_FONT) | |||
| try: | |||
| broadcast = Broadcast (os.environ['BROADCAST_CODE']) | |||
| @@ -639,16 +643,6 @@ class Jojoko (GameObject): | |||
| def redraw ( | |||
| self, | |||
| ) -> None: | |||
| if self.frame % (FPS * 3600) == 0: | |||
| self.surface = self._get_surface () | |||
| surface = pygame.transform.rotate (self.surface, -(90 + math.degrees (self.arg))) | |||
| surface.set_colorkey ((0, 255, 0)) | |||
| self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y))) | |||
| super ().redraw () | |||
| def update ( | |||
| self, | |||
| ) -> None: | |||
| self.moon.compute (self.game.sky.observer) | |||
| self.alt = self.moon.alt | |||
| @@ -657,7 +651,12 @@ class Jojoko (GameObject): | |||
| self.arg = self.new_arg | |||
| self.x = self.new_x | |||
| self.y = self.new_y | |||
| super ().update () | |||
| if self.frame % (FPS * 3600) == 0: | |||
| self.surface = self._get_surface () | |||
| surface = pygame.transform.rotate (self.surface, -(90 + math.degrees (self.arg))) | |||
| surface.set_colorkey ((0, 255, 0)) | |||
| self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y))) | |||
| super ().redraw () | |||
| @property | |||
| def phase ( | |||
| @@ -732,30 +731,6 @@ class CWindow: | |||
| HEIGHT = 768 | |||
| def get_surfaces_from_video ( | |||
| video_path: str, | |||
| ) -> list[Surface]: | |||
| cap = VideoCapture (video_path) | |||
| if not cap.isOpened (): | |||
| return [] | |||
| fps = cap.get (cv2.CAP_PROP_FPS) | |||
| surfaces: list[Surface] = [] | |||
| while cap.isOpened (): | |||
| (ret, frame) = cap.read () | |||
| if not ret: | |||
| break | |||
| frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB) | |||
| frame_surface = pygame.surfarray.make_surface (frame) | |||
| frame_surface = pygame.transform.rotate (frame_surface, -90) | |||
| surfaces.append (pygame.transform.flip (frame_surface, True, False)) | |||
| cap.release () | |||
| return surfaces | |||
| class Broadcast: | |||
| chat: PytchatCore | |||
| @@ -777,6 +752,126 @@ class Broadcast: | |||
| return random.choice (chats) | |||
| class NicoVideo (Video): | |||
| ... | |||
| class Video (GameObject): | |||
| fps: int | |||
| pausing: bool = False | |||
| sound: Sound | |||
| surfaces: list[Surface] | |||
| def __init__ ( | |||
| self, | |||
| game: Game, | |||
| path: str, | |||
| ): | |||
| super ().__init__ (game) | |||
| self.pausing = False | |||
| (self.surfaces, self.fps) = self._create_surfaces (path) | |||
| self.sound = self._create_sound (path) | |||
| self.stop () | |||
| def _create_sound ( | |||
| self, | |||
| path: str, | |||
| ) -> Sound: | |||
| bytes_io = BytesIO () | |||
| audio = AudioSegment.from_file (path, format = path.split ('.')[-1]) | |||
| audio.export (bytes_io, format = 'wav') | |||
| bytes_io.seek (0) | |||
| return pygame.mixer.Sound (bytes_io) | |||
| def _create_surfaces ( | |||
| self, | |||
| path: str, | |||
| ) -> tuple[list[Surface], int]: | |||
| cap = self._load (path) | |||
| surfaces: list[Surface] = [] | |||
| if cap is None: | |||
| return ([], FPS) | |||
| fps = int (cap.get (cv2.CAP_PROP_FPS)) | |||
| while cap.isOpened (): | |||
| frame = self._read_frame (cap) | |||
| if frame is None: | |||
| break | |||
| surfaces.append (self._convert_to_surface (frame)) | |||
| new_surfaces: list[Surface] = [] | |||
| for i in range (len (surfaces) * FPS // fps): | |||
| new_surfaces.append (surfaces[i * fps // FPS]) | |||
| return (new_surfaces, fps) | |||
| def _load ( | |||
| self, | |||
| path: str, | |||
| ) -> VideoCapture | None: | |||
| """ | |||
| OpenCV で動画を読込む. | |||
| """ | |||
| cap = VideoCapture (path) | |||
| if cap.isOpened (): | |||
| return cap | |||
| return None | |||
| def _read_frame ( | |||
| self, | |||
| cap: VideoCapture, | |||
| ) -> np.ndarray | None: | |||
| """ | |||
| 動画のフレームを読込む. | |||
| """ | |||
| ret: bool | |||
| frame: np.ndarray | |||
| (ret, frame) = cap.read () | |||
| if ret: | |||
| return frame | |||
| return None | |||
| def _convert_to_surface ( | |||
| self, | |||
| frame: np.ndarray, | |||
| ) -> Surface: | |||
| frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB) | |||
| frame_surface = pygame.surfarray.make_surface (frame) | |||
| frame_surface = pygame.transform.rotate (frame_surface, -90) | |||
| frame_surface = pygame.transform.flip (frame_surface, True, False) | |||
| return frame_surface | |||
| def play ( | |||
| self, | |||
| ) -> None: | |||
| self.enabled = True | |||
| self.pausing = False | |||
| self.sound.play () | |||
| def stop ( | |||
| self, | |||
| ) -> None: | |||
| self.enabled = False | |||
| self.frame = 0 | |||
| def pause ( | |||
| self, | |||
| ) -> None: | |||
| self.pausing = True | |||
| def redraw ( | |||
| self, | |||
| ) -> None: | |||
| self.game.screen.blit (self.surfaces[self.frame], (self.x, self.y)) | |||
| super ().redraw () | |||
| def update ( | |||
| self, | |||
| ) -> None: | |||
| if self.frame >= len (self.surfaces) - 1: | |||
| self.pause () | |||
| if self.pausing: | |||
| self.frame -= 1 | |||
| super ().update () | |||
| def fetch_bytes_from_url ( | |||
| url: str, | |||
| ) -> bytes | None: | |||