Google Antigravityすごい!

論より証拠ということで、Antigravityで作成したプログラムをGithubに公開した。

プログラムを自分で書いたり、テストデータを作成したりは一切していない。Agentへの指示のみでここまで出来た。

Novel Search System

A full-stack web application for managing and searching novels, built with modern technologies including Spring Boot and React.

Overview

This project is a comprehensive system designed to handle novel data, authors, and user authentication. It features a robust backend API and a responsive, internationalized frontend interface.

Tech Stack

Backend

  • Java: 25
  • Framework: Spring Boot 4
  • Database: MariaDB
  • Security: Spring Security
  • Tools: Maven, Lombok

Frontend

  • Framework: React 19 (Vite)
  • UI Library: Material UI (MUI) v7
  • State Management: TanStack Query (React Query) v5
  • Routing: React Router
  • Forms: React Hook Form + Zod
  • Internationalization: i18next
  • HTTP Client: Axios

Features

  • User Authentication: Secure login and registration system.
  • Novel Management: Create, read, update, and delete novels.
  • Author Management: Manage author profiles and link them to novels.
  • Internationalization (i18n): Full support for English and Japanese languages.
  • Responsive Design: Modern UI adapted for various screen sizes.

Getting Started

Prerequisites

  • Java JDK 25
  • Node.js (Latest LTS recommended)
  • MariaDB Server

Backend Setup

  1. Navigate to the backend directory: cd backend
  2. Configure the database connection in src/main/resources/application.properties if necessary (defaults to localhost:3306).
  3. Build and run the application: ./mvnw spring-boot:run The backend server will start on http://localhost:8080.

Frontend Setup

  1. Navigate to the frontend directory: cd frontend
  2. Install dependencies: npm install
  3. Start the development server: npm run dev The frontend application will be accessible at http://localhost:5173.

Logging

The application is configured to output logs to the logs/ directory:

  • app.log: General application logs.
  • error.log: Error-level logs.
  • debug.log: Debug-level logs for the application package.
  • sql.log: SQL statements and Hibernate logs.

License

MIT

Xのタイムラインから誹謗中傷や攻撃的な投稿を非表示にする

生成AIに「最強のミュートワードリスト」を作らせる(スマホ・PC共通)

「Xの標準機能(ミュート)」と「ChatGPTなどのAI」を組み合わせる、現実的で効果が高い方法です。

手順:

  1. AI(ChatGPTやGemini)に指示する: 誹謗中傷によく使われる言葉、不快な言い回し、暴言のパターンをAIに列挙させます。

    プロンプト例: 「X(Twitter)のタイムラインで、他人を攻撃したり誹謗中傷したりする投稿を見たくありません。そのような投稿によく含まれる単語、フレーズ、隠語を50個リストアップしてください。カンマ区切りで出力してください。」
  2. Xの設定にコピー&ペースト: 出力された単語リストを、Xの「設定とプライバシー」>「プライバシーと安全」>「ミュートとブロック」>「ミュートするキーワード」に登録します。
    • メリット: 無料で、スマホアプリにも適用され、確実に消えます。
    • デメリット: 定期的にリストを更新する必要があります。

    その他の設定項目

    対象を「フォローしていないアカウント」にすると良いでしょう。

    Dockerイメージを軽くする「マルチステージビルド」について

    前回の記事では、複数のコンテナを操る docker-compose.yml の書き方を紹介した。今回は、そのコンテナの中身を定義する Dockerfile について書く。

    今回作成したバックエンド(Java/Spring Boot)とフロントエンド(React/Node.js)のDockerfileには、「マルチステージビルド」と使っている。

    これを使うと、「イメージサイズが小さくなり、セキュリティも向上する」

    バックエンド編:Java (Spring Boot)

    Javaアプリを動かすには、ビルドツールの「Maven」や「Gradle」が必要だが、実行時にこれらは不要となる。そこで、ビルドする段階と、実行する段階を分ける。

    # --- Stage 1: ビルド環境 ---
    FROM maven:3.9-eclipse-temurin-25 AS build
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    # テストはCIでやる前提でスキップし、ビルド時間を短縮
    RUN mvn clean package -DskipTests
    
    # --- Stage 2: 実行環境 ---
    FROM eclipse-temurin:25-jre
    WORKDIR /app
    # ビルド環境(Stage 1)で作った成果物(jar)だけをコピー
    COPY --from=build /app/target/*.jar app.jar
    EXPOSE 8080
    ENTRYPOINT ["java", "-jar", "app.jar"]

    • AS build と COPY –from=build: 最初の FROM で「build」という名前をつけ、そこで mvn package して jarファイルを作る。2つ目の FROM が本番用イメージ。ここには Maven もソースコードも含まれない。あるのは 「JRE(Java実行環境)」と「jarファイル」だけ。 これにより、数百MB単位で容量を節約できる。
    • eclipse-temurin:25-jre: JDK(開発キット)ではなく、JRE(実行環境)を選んでいる。コンパイラなどが含まれないため、攻撃者に悪用されるリスクが減る。

    フロントエンド編:Node.js (React/Vueなど)

    フロントエンド開発では npm run dev サーバーを使うが、本番環境でNode.jsサーバーをそのまま動かすのはパフォーマンス的に最適ではない。 ビルドした静的ファイル(HTML/CSS/JS)を、高速なWebサーバーである Nginx に配信させる。

    # --- Stage 1: ビルド環境 ---
    FROM node:24-alpine AS build
    WORKDIR /app
    COPY package.json package-lock.json ./
    # npm install より高速で、lockファイルに忠実な ci を使用
    RUN npm ci
    COPY . .
    RUN npm run build
    
    # --- Stage 2: 配信環境 (Nginx) ---
    FROM nginx:alpine
    # ビルド成果物(dist)をNginxの公開ディレクトリに配置
    COPY --from=build /app/dist /usr/share/nginx/html
    # SPA用の設定ファイルをコピー
    COPY nginx.conf /etc/nginx/conf.d/default.conf
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]

    • Node.js はビルド時のみ使用: Reactなどのコードはブラウザで動くJavaScriptに変換(トランスパイル)する必要がある。これには Node.js が必要だが、変換が終われば Node.js 自体は不要となる。 最終的なイメージは nginx:alpine ベースになるため、サイズはわずか数十MBに収まる。
    • npm ci の採用: npm install ではなく npm ci を使っている。これは package-lock.json を厳密に守ってインストールを行うコマンドで、CI/CDやDockerビルドでの再現性を保証する。
    • カスタムNginx設定: ReactのようなSPA(シングルページアプリケーション)では、どのURLにアクセスしても index.html を返す設定が必要になるため、デフォルト設定を上書きしている。

    まとめ

    今回作成した2つのDockerfileに共通するのは、「作る道具(Maven/Node)」と「動かす場所(JRE/Nginx)」を明確に分けている点となる。

    Docker Composeで、Webアプリ開発環境を作る

    今回は、典型的な「フロントエンド(React等) + バックエンド(Spring/Node等) + データベース(MariaDB)」の構成を例に docker-compose.yml を書いた。

    今回の構成

    3つのコンテナ(Service)が専用のネットワーク内で連携し、データはボリュームによって永続化される構成。

    完成した docker-compose.yml

    以下が今回解説する設定ファイル。例として小説検索システム(Novel Search System)のようなアプリケーションを想定している。

    services:
      backend:
        build: ./backend
        container_name: novel-backend
        ports:
          - "8080:8080"
        environment:
          - DB_URL=jdbc:mariadb://novel-db:3306/${DB_NAME}
          # ... (省略)
        depends_on:
          novel-db:
            condition: service_healthy # ここが重要!
        networks:
          - novel-network
    
      frontend:
        build: ./frontend
        container_name: novel-frontend
        ports:
          - "80:80"
        depends_on:
          - backend
        networks:
          - novel-network
    
      novel-db:
        image: mariadb:12.1
        container_name: novel-db
        environment:
          - MARIADB_DATABASE=${DB_NAME}
          # ... (省略)
        healthcheck:
          test: ["CMD-SHELL", "mariadb-admin ping -h localhost -u root -p$$MARIADB_ROOT_PASSWORD || exit 1"]
          interval: 10s
          timeout: 5s
          retries: 10
        volumes:
          - db_data:/var/lib/mysql
          - ./backend/sql:/docker-entrypoint-initdb.d
        networks:
          - novel-network
    
    networks:
      novel-network:
        driver: bridge
    
    volumes:
      db_data:

    1.データベースの「起動待ち」を完璧にする healthcheck

    Docker Composeでよくあるトラブルが、「DBが完全に立ち上がる前にバックエンドが接続に行き、エラーで落ちる」という現象。

    単なる depends_on: – novel-db だけでは、「コンテナが作成された」ことしか確認できず、「DB接続の準備ができた」ことまでは保証されない。

    そこでHealthcheck(ヘルスチェック) を行う。

    novel-db:
        # ...
        healthcheck:
          test: ["CMD-SHELL", "mariadb-admin ping ..."]
          interval: 10s
          retries: 10

    ここでは mariadb-admin ping コマンドを定期的に実行し、DBが応答するかを監視している。

    backend側では以下のように記述する。

    backend:
        depends_on:
          novel-db:
            condition: service_healthy

    これにより、「DBのヘルスチェックがOKになるまで、バックエンドを起動させない」という制御が可能になる。

    2.データの永続化と初期データの投入

    データベースをコンテナ化する際、コンテナを削除するとデータも消えてしまう。これを防ぐのが volumes 。

    volumes:
          - db_data:/var/lib/mysql
          - ./backend/sql:/docker-entrypoint-initdb.d

    • db_data:/var/lib/mysql: ホスト側(Docker管理領域)にデータを保存する。コンテナを作り直しても、小説データやユーザーデータは消えない。
    • ./backend/sql:/docker-entrypoint-initdb.d: MariaDBやMySQLの公式イメージは、/docker-entrypoint-initdb.d に置かれた .sql ファイルを、初回起動時に自動実行してくれる。 テーブル作成や初期データの投入を自動化できるため、開発メンバー全員が docker compose up 一発で同じDB状態から開発をスタートできる。

    EclipseからVS Codeに開発環境を移した

    フロントエンドの開発をしたかったので、VS Codeを触ってみた。

    殆ど問題なく作業を進めることが出来たが、一部戸惑った点を書き残しておく。

    起動構成はJSON形式で書く

    {
        "configurations": [
            {
                "type": "java",
                "name": "Spring Boot App",
                "request": "launch",
                "mainClass": "com.example.novel.NovelApplication",
                "projectName": "",
                "env": {
                    "DEV_DB_URL": "jdbc:mariadb://url",
                    "DEV_DB_USERNAME": "id",
                    "DEV_DB_PASSWORD": "pass"
                }
            },
            {
                "type": "node",
                "name": "React App",
                "request": "launch",
                "runtimeExecutable": "npm",
                "runtimeArgs": [
                    "run-script",
                    "dev"
                ],
                "cwd": "${workspaceFolder}/frontend",
                "console": "integratedTerminal"
            }
        ]
    }

    1. ひとつ目の設定:Java (Spring Boot) アプリケーション用

    Javaのプログラム(Spring Bootなど)を起動するための設定。

    • "type": "java"
      • Java拡張機能を使ってデバッグ
    • "name": "Spring Boot App"
      • VS Codeの「実行とデバッグ」画面のプルダウンメニューに表示される名前
    • "request": "launch"
      • 新しくプログラムを起動するという意味(対義語は "attach" で、既に動いているプロセスに接続する場合に使う)
    • "mainClass": "com.example.novel.NovelApplication"
      • プログラムのエントリーポイント(public static void main メソッドがあるクラス)を指定
    • "projectName": ""
      • 通常、VS Codeが認識しているJavaプロジェクト名(フォルダ名など)が入る
      • 空欄 "" のままだと、複数のプロジェクトがある場合に起動できない可能性がある。1つだけなら問題なし
    • "env"
      • プログラム実行時にOSの環境変数として渡す値を設定している
      • アプリケーション側(application.properties など)で、DB接続情報をハードコードせずに、ここから読み込むようにしている
        • DEV_DB_URL: 接続先データベースのURL
        • DEV_DB_USERNAME: データベースのユーザーID
        • DEV_DB_PASSWORD: データベースのパスワード

    2. ふたつ目の設定:Node.js (NPM) アプリケーション用

    フロントエンド開発(React, Vueなど)の開発サーバーを起動するための設定。

    • "type": "node"
      • Node.js用のデバッグ
    • "name": "React App"
      • VS Codeの「実行とデバッグ」画面のプルダウンメニューに表示される名前
    • "request": "launch"
      • 新しくプログラムを起動するという意味
    • "runtimeExecutable": "npm"
      • 直接 node コマンドを実行するのではなく、npm コマンドを実行する
    • "runtimeArgs": [ "run-script", "dev" ]
      • npm コマンドに渡す引数
      • つまり、ターミナルで npm run-script dev (または単に npm run dev)と打つのと同じコマンドが実行される
    • "cwd": "${workspaceFolder}/frontend"
      • Current Working Directory(作業ディレクトリ)の指定
      • ${workspaceFolder} はVS Codeで開いているルートフォルダを指す
    • "console": "integratedTerminal"
      • ログの出力先を「デバッグコンソール」ではなく、VS Codeの**「統合ターミナル」**にする

    VS Codeの設定はJSON形式で書く

    {
        "java.configuration.updateBuildConfiguration": "automatic",
        "java.compile.nullAnalysis.mode": "automatic",
        "maven.terminal.customEnv": [
            {
                "environmentVariable": "DEV_DB_URL",
                "value": "jdbc:mariadb://url"
            },
            {
                "environmentVariable": "DEV_DB_USERNAME",
                "value": "id"
            },
            {
                "environmentVariable": "DEV_DB_PASSWORD",
                "value": "pass"
            }
        ]
    }

    1. Javaのビルド・解析設定

    VS CodeがJavaプロジェクトをどう扱うかという、エディタの挙動設定

    • "java.configuration.updateBuildConfiguration": "automatic"
      • 意味: pom.xml(や build.gradle)が書き換えられた際、自動的にプロジェクト構成を更新する
      • Eclipseで言うと: プロジェクトを右クリックして行う 「Maven > プロジェクトの更新 (Alt+F5)」を、保存のたびに全自動でやってくれる機能
      • メリット: 依存ライブラリを追加した際、いちいち手動更新しなくてもすぐにimportできるようになる
    • "java.compile.nullAnalysis.mode": "automatic"
      • 意味: Javaのコードに対するNull(ヌル)分析機能のモード
      • 動作: プロジェクト内にNull注釈(@Nullable@NonNull など)が含まれている場合、自動的にNullポインタ参照の警告を出してくれる
      • メリット: 「ここでNullになる可能性があります」という警告がエディタ上で出るようになり、NullPointerExceptionを未然に防ぎやすくなる

    2. Maven実行時の環境変数設定

    • "maven.terminal.customEnv": [ ... ]
      • 意味: VS CodeのMaven拡張機能を使ってコマンド(例: mvn spring-boot:runmvn test)を実行する際、一時的にセットする環境変数を定義

    Spring Security 7にアップグレードしたらxmlでの設定が使えなくなった

    XMLファイルでSpring Securityを設定するSecurityConfigは非推奨になっていたが、今回完全に削除されたのかクラスファイルが見つからなくなった。

    流石にJavaファイルで設定する方式に変更した。

    元のxmlファイルは下記の通り。
    ※ExtendedAuthenticationSuccessHandler、ExtendedAuthenticationFailureHandlerはログイン成功失敗時に独自処理を行うためのラッパークラス。

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
    
        <!-- Resources not processed by spring security filters -->
        <http pattern="/images/**" security="none" />
        <http pattern="/scripts/**" security="none" />
        <http pattern="/styles/**" security="none" />
    
        <http>
            <intercept-url pattern="/error" access="permitAll" />
            <intercept-url pattern="/login*/**" access="permitAll" />
            <intercept-url pattern="/signup*" access="permitAll" />
            <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
            <intercept-url pattern="/**" access="isAuthenticated()" />
    
            <form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler" />
            <remember-me user-service-ref="userDetails" key="aaa" />
            <logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="aaa" />
        </http>
    
        <authentication-manager alias="authenticationManager">
            <authentication-provider user-service-ref="userDetails">
                <password-encoder ref="passwordEncoder" />
            </authentication-provider>
        </authentication-manager>
    
        <beans:bean id="authenticationSuccessHandler" class="common.webapp.filter.ExtendedAuthenticationSuccessHandler">
            <beans:constructor-arg value="/top" />
        </beans:bean>
    
        <beans:bean id="authenticationFailureHandler" class="common.webapp.filter.ExtendedAuthenticationFailureHandler">
            <beans:property name="exceptionMappings">
                <beans:props>
                    <beans:prop key="org.springframework.security.authentication.DisabledException">/login/accountDisabled</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.LockedException">/login/accountLocked</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.AccountExpiredException">/login/accountExpired</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/login/credentialsExpired</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/login/badCredentials</beans:prop>
                </beans:props>
            </beans:property>
        </beans:bean>
    
        <beans:bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />
    
    </beans:beans>

    これに対して、Javaファイルは下記の通り。

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    
        private final UserDetailsService userDetailsService;
    
        public SecurityConfig(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) {
            http
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/images/**", "/scripts/**", "/styles/**",
                                     "/error", "/login*/**", 
                                     "/signup*").permitAll()
                    .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
                    .anyRequest().authenticated()
                )
                .formLogin(form -> form
                    .loginPage("/login")
                    .successHandler(authenticationSuccessHandler())
                    .failureHandler(authenticationFailureHandler())
                    .permitAll()
                )
                .rememberMe(remember -> remember
                    .userDetailsService(userDetailsService)
                    .key("aaa")
                )
                .logout(logout -> logout
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/login")
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID", "aaa")
                );
    
            return http.build();
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return PasswordEncoderFactories.createDelegatingPasswordEncoder();
        }
    
        @Bean
        public PasswordEncoder passwordTokenEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        AuthenticationSuccessHandler authenticationSuccessHandler() {
            return new ExtendedAuthenticationSuccessHandler("/top");
        }
    
        AuthenticationFailureHandler authenticationFailureHandler() {
            // 例外マッピングの設定
            Properties mappings = new Properties();
            mappings.put(DisabledException.class.getName(), "/login/accountDisabled");
            mappings.put(LockedException.class.getName(), "/login/accountLocked");
            mappings.put(AccountExpiredException.class.getName(), "/login/accountExpired");
            mappings.put(CredentialsExpiredException.class.getName(), "/login/credentialsExpired");
            mappings.put(BadCredentialsException.class.getName(), "/login/badCredentials");
    
            ExtendedAuthenticationFailureHandler handler = new ExtendedAuthenticationFailureHandler();
            handler.setExceptionMappings(mappings);
            return handler;
        }
    }

    Java 25のパフォーマンス改善点で気になったところ

    特に気になった点だけ調べてみた。

    ウォームアップ短縮(JEP 515)

    Java アプリケーションは JIT コンパイルによって実行速度を最適化するが、その「最適化」に到達するまでのウォームアップ時間は長年の課題だった。特に短命なサービスやバースト型のワークロードでは、起動直後のレスポンス低下が無視出来ない。

    JEP 515 では、過去の実行から収集したプロファイル情報を AOT キャッシュとして保存し、次回の起動時に利用できる仕組みが導入された。これにより、

    • 初期起動から JIT 最適化が効いた状態に近いパフォーマンスを発揮
    • マイクロサービスやサーバレス環境の「コールドスタート問題」を緩和
    • ウォームアップを待たずにスループットやレイテンシが安定

    といったメリットがある。

    トレーニング実行(プロファイル収集)

    まずはアプリを「本番に近い負荷テスト」、「代表的な入力データ」等で実行し、JIT がどのメソッドをよく使うかを記録する。

    java -XX:AOTCacheOutput=app-prof.aot -jar myapp.jar

    本番実行(プロファイル利用)

    保存したプロファイルを読み込み、本番環境で起動する。

    java -XX:AOTCache=app-prof.aot -jar myapp.jar

    Compact Object Headers(JEP 519)

    Java のオブジェクトはヒープ上で管理され、各オブジェクトには「ヘッダ情報」が付与されている。従来は 12 バイトが標準だったが、JEP 519 では ヘッダを 8 バイトに圧縮するオプションが提供された。

    これにより、

    • メモリ削減:多数の小さなオブジェクトを扱うアプリで特に有効。ヒープ効率が上がるため GC 回数も減少する。
    • キャッシュ効率の改善:メモリ占有が減ることで CPU キャッシュに収まるデータ量が増え、アクセスレイテンシが低下する。
    • スループット向上

    利用方法は、JVM 起動時に以下を指定する。

    java -XX:+UseCompactObjectHeaders -jar myapp.jar

    デフォルトでONになっていないということは、全ての環境で改善するわけではないことを表している。

    • COH ではオブジェクトヘッダに格納されていた情報を圧縮/再配置するため、hashCode/同期多用のアプリでは遅くなる可能性
    • ヒープダンプ解析ツールや JVMTI を使った低レベルの計測では、
      オブジェクトレイアウトが従来と変わるため、互換性のリスクあり
    • メモリ帯域が十分に広いサーバー、CPU キャッシュが大きい最新世代のCPUでは効果が限定的になる場合がある(逆に、組込み系やクラウドVMのようにメモリ制約がある環境で効果大)

    適用シナリオ

    これらの最適化はすべてのアプリケーションで均等に効くわけではない。効果が大きいシナリオは以下の通りとなる。

    ウォームアップ短縮(JEP 515)

    • サーバレス、マイクロサービス
    • 短命な CLI ツールやバッチ処理

    Compact Object Headers(JEP 519)

    • ヒープ上に数百万単位のオブジェクトを保持するシステム
    • 高トラフィックの Web サービス
    • データ処理や分析基盤

    まとめ

    Java 25 の JEP 515 と JEP 519 は、単なる言語仕様の追加ではなく、実行基盤のボトルネックを狙い撃ちした改善となっている。
    起動直後のパフォーマンスが課題であればウォームアップ短縮を、ヒープ使用量やGCコストに悩んでいるなら Compact Object Headers を試す価値がある。

    YOLOでYouTubeの映像から物体検出する

    YouTubeのライブ映像を取り込んで物体検出してみる。応用すると風景のライブ映像に野生動物が写ったらアラートを出す。録画するなど出来ると思う。

    前回の下記部分のカメラ映像読み込み部分をYouTubeに置き換える。

    # Load image source
    cap = cv2.VideoCapture(0)

    前回のパッケージに加えて「pip install yt-dlp」を実行しておく。

    実装

    モジュールの読み込み
    from yt_dlp import YoutubeDL
    YouTubeのライブ配信ページURLから、再生用の実URL(多くはHLSのm3u8)を取得する関数
    def get_best_live_url(youtube_url: str) -> str:
        ydl_opts = {
            'quiet': True,
            'no_warnings': True,
            'skip_download': True,
            # Filters in order of ease of live performance
            'format': 'best[protocol^=m3u8]/best[ext=mp4]/best'
        }
        with YoutubeDL(ydl_opts) as ydl:
            info = ydl.extract_info(youtube_url, download=False)
            # `url` contains a URL that can be played directly (assuming m3u8)
            stream_url = info.get('url')
            if not stream_url:
                for f in info.get('formats', []):
                    if 'm3u8' in (f.get('protocol') or ''):
                        stream_url = f.get('url')
                        break
            if not stream_url:
                raise RuntimeError('Unable to get live playback URL.')
            return stream_url
    YouTubeの動画、ライブ配信を開く関数
    def open_live_capture(stream_url: str) -> cv2.VideoCapture:
        cap = cv2.VideoCapture(stream_url)
        # Latency reduction (enabled builds only)
        try:
            cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
        except Exception:
            pass
        return cap
    前回コードの変更部分

    前回のコードの「cap = cv2.VideoCapture(0)」を下記に置き換える。「youtube_url」に取り込みたい動画やライブ映像のURLを入力する。

    youtube_url = 'https://www.youtube.com/watch?v=好きな動画のURL'
    
    stream_url = get_best_live_url(youtube_url)
    print('stream:', stream_url)
    cap = open_live_capture(stream_url)
    
    if not cap.isOpened():
        raise RuntimeError('Failed to open VideoCapture. Please use an FFmpeg-enabled build of OpenCV.')

    Android 16の新機能について

    自分のスマホにAndroid 16がそろそろ配信されそうなため、主にプライバシー、パフォーマンス、バッテリー最適化、ゲーム関連について調べてみた。

    プライバシーとセキュリティの強化について

    1. アプリの権限リクエストがより細分化・透明化

    写真・動画へのアクセス制限の強化

    これまで「すべての写真を許可/拒否」のような大まかな選択肢しかなかったストレージアクセスに対して、Android 16では:

    • 特定の写真のみ選択的に許可
    • アクセス履歴の表示
    • 一時的なアクセス(ワンタイムパーミッション)

    など、ユーザーがどのデータに対して、どのアプリに許可したかを明確に管理できるようになる。

    位置情報とマイク・カメラの利用制限

    • 「常時許可」は廃止され、アプリがアクティブな時だけ許可が基本に
    • バックグラウンドでのマイク・カメラの使用はユーザーに通知
    • 通知バーで現在利用中のセンサー(マイク、カメラ、GPS)がリアルタイムで表示

    2. Google Play プロテクトの進化(リアルタイムAIスキャン)

    Android 16では、Google Play プロテクトにAIによるリアルタイム挙動監視機能が追加される。

    • 新規インストール時にアプリのコードや動作傾向をクラウドAIが即座に解析
    • 不審な動作がある場合は即時警告・ブロック
    • 「このアプリは類似アプリと比べて異常に多くのデータを送信しています」など、行動ベースで通知

    これにより、インストール前だけでなく使用中のセキュリティも常時監視されるようになる。

    3. システムアップデートの分割と即時性向上(モジュラー化)

    Android 16では「Project Mainline」がさらに進化し、以下のような構成に:

    • セキュリティコンポーネントがOSと切り離され、Google Play経由で個別更新が可能
    • 端末メーカーやキャリアのアップデート配信を待たずに、月次パッチレベルで配布
    • 更新は自動かつバックグラウンドで完了

    これにより、ゼロデイ攻撃への即応性が格段に上がるとされる。

    4. アプリトラッキング・クロスアプリ識別子の制限

    Appleの「App Tracking Transparency」に近い機能がAndroid 16でも強化されている。

    • アプリごとの広告ID取得がデフォルトで無効化
    • アプリがユーザーを他のアプリやサービスを横断して追跡する行為に対し、事前に明確な許可を求めるUIが表示
    • 許可履歴やデータ共有先の表示がユーザー向けに視覚化

    これにより、「勝手に広告が最適化される」不快感を低減する。

    5. プライバシーダッシュボードの強化

    「設定」→「プライバシー」にあるダッシュボード画面が大幅に刷新された。

    • 各アプリがいつ、何のデータにアクセスしたかを時系列で表示
    • 「カレンダーへのアクセス:○月×日 14:22」など具体的な操作ログ付き
    • 不要と判断したアクセスは即座に取り消し可能

    パフォーマンスとバッテリー最適化について

    OSレベルでの最適化と、AIによる動的制御を組み合わせた省電力かつ高速なシステム設計が実現される。特に、バックグラウンド処理やメモリ割り当て、充電中の動作制御に重点が置かれている。

    1. 高効率スケジューラ:Dynamic Task Prioritization(DTP)

    Android 16のカーネルスケジューラは、タスク実行時にアプリの重要度や使用頻度を学習し、CPU・GPUリソースを動的に割り当てる機構が導入された。

    • アクティブアプリの処理を即時優先
    • バックグラウンドアプリは低負荷スレッドに回される
    • AIがユーザーの利用傾向を継続的に学習し、最適な実行タイミングを決定

    これにより、操作時のラグ減少や描画高速化が実現しつつ、リソースの無駄遣いも抑制される。

    2. バックグラウンド処理の制限強化

    Android 16では、Dozeモード/App Standby Bucket(ASB)/Foreground Serviceの動作制御が進化している。

    • アプリを「Active/Working set/Frequent/Rare/Restricted」に自動分類
    • 端末の未使用時間が長いほど、該当アプリのバックグラウンド実行権限が縮小
    • バッテリー消費の激しいアプリに対し、強制スリープ処理を自動で適用

    また、ユーザーは「バッテリー使用量ランキング」から該当アプリの活動詳細を確認し、手動制御も可能となる。

    3. AIによる電力最適化:Adaptive Battery v2

    Android 16では「Adaptive Battery」が第2世代にアップグレードされ、ディープラーニングによる予測制御が導入された。

    • アプリごとの使用傾向を解析し、不要なプロセスの事前抑制
    • 毎日の行動パターンに基づき、電力供給を時間帯ごとに最適配分
    • 充電パターンを学習してバッテリー劣化を抑える

    たとえば、就寝中の充電では、急速充電を避けて「夜間スロー充電モード」に自動切り替えされる。

    4. バッテリー保護機能の強化

    Android 16対応端末では、以下のようなハードウェア連携機能もサポートされる。

    • 温度・電圧監視機構の統合により、過熱や過充電をOSが自動制御
    • 一定の温度や電圧条件を超えると、充電速度を一時的に抑制
    • 「バッテリー寿命延長モード」では、満充電を80〜85%で制御するオプションも搭載

    これにより、バッテリー膨張や性能劣化のリスクを抑え、3〜5年の長期使用を前提とした保護設計が可能になる?

    5. 開発者向け電力監視ツールの拡充

    アプリ開発者向けに提供されるツールも進化した。

    • Android Studio Profilerでの電力消費分析が高精度化
    • 各フレームごとのCPU負荷、GPU描画負荷、ネットワーク使用率、バッテリー消費をリアルタイムで可視化
    • 開発段階で省電力設計を意識したUI設計が可能

    さらに、Battery Usage Stats APIの改良により、アプリ内部からも消費電力量を把握・最適化できる仕組みが追加される。

    ゲーム・マルチメディア機能の進化について

    Android 16では、モバイルデバイスを「ゲーム機」、「エンタメプレイヤー」として本格的に使うための土台が大幅に強化された。特にGPU制御、オーディオ出力、ディスプレイ対応が進化し、高性能端末のポテンシャルを最大限に引き出せる設計としている。

    1. グラフィックスと描画性能の最適化

    Vulkan APIのさらなる強化

    • Android 16ではVulkan 1.3 APIに対応(端末依存あり)
    • アセットの事前読み込み(Pipeline Caching)により読み込み時間を短縮
    • メッシュシェーダーや拡張バッファ制御によって、3D表現のリアリティ向上
    • GPUとCPUの同期処理の遅延(frame pacing)を大幅に削減

    これにより、FPSやオープンワールド系ゲームでも滑らかで安定した描画が可能になり、90fpsや120fps表示にも対応しやすくなる。

    ハードウェアレベルのレイテンシー改善

    • Graphics HAL(ハードウェア抽象化レイヤ)の改良により、タッチ入力→描画までの遅延を最小化

    高速表示が求められるゲームでも、瞬時の反応性が得られる。

    2. ゲームダッシュボードの標準搭載

    従来はSamsungやASUSなどの一部メーカーUIに限られていた「ゲームツール機能」が、Android 16では標準化された。

    • フレームレート(FPS)モニタリング
    • スクリーンレコード(録画)やライブ配信
    • 通知の一時ブロック
    • タッチ感度や視野角の即時調整
    • リソース節約モードへの切り替え

    これにより、ゲーム中のUX改善と集中力維持がしやすくなる。

    3. コントローラー対応と操作性の強化

    Android 16ではBluetooth HIDプロファイルが拡張され、DualSense(PS5)やXboxコントローラーの高精度入力に正式対応する。

    • アナログトリガー、触覚フィードバック、ジャイロセンサーなども一部ゲームでサポート
    • 外付けゲームパッドやUSB-C接続型ゲームデバイスとのレイテンシー最小化

    さらに、画面操作においてもスワイプ範囲のカスタマイズや、指の誤反応防止のタッチ制御アルゴリズムが強化された。

    4. マルチメディア:映像と音響の進化

    映像体験の進化

    • HDR10+、Dolby Vision、HLG などの最新HDR規格に幅広く対応
    • 輝度レンジ、色域、階調表現が向上し、映画・配信視聴での没入感アップ
    • ハードウェア対応端末では、自動トーンマッピング/暗部補正などのリアルタイム補正が可能

    オーディオ機能の進化

    • Spatial Audio(空間オーディオ):対応コンテンツで立体音響再生が可能に
    • 360度の音場感覚
    • 頭の動きに追従する「ヘッドトラッキング機能」にも対応(対応イヤホン必要)

    Bluetooth LE Audio対応:

    • 超低遅延通信
    • LC3コーデックによる高音質+省電力
    • 複数デバイス同時接続(ブロードキャスト)への拡張

    これにより、ゲーム・動画どちらにおいてもプロレベルのオーディオ体験が可能となる。

    5. XR(AR/VR)デバイスへの基盤整備

    Android 16では、今後の拡張現実(XR)プラットフォーム展開を見据えて、

    • Googleの新しいARフレームワーク「ARCore 4.0」に最適化
    • 空間マッピングや深度認識の精度向上
    • ヘッドセット連携に向けた低遅延ビデオ出力APIを統合

    Meta QuestやSnapdragon Spacesなどの外部XRエコシステムとの統合も視野に入れた設計となっており、将来的にはAndroid端末をXRハブとして活用する流れも予想される。

    Spring Data JPA 3.5でSpecification.whereが非推奨になった件

    /**
     * Simple static factory method to add some syntactic sugar around a {@link Specification}.
     *
     * @apiNote with 4.0, this method will no longer accept {@literal null} specifications.
     * @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
     * @param spec can be {@literal null}.
     * @return guaranteed to be not {@literal null}.
     * @since 2.0
     * @deprecated since 3.5, to be removed with 4.0 as we no longer want to support {@literal null} specifications.
     */
    @Deprecated(since = "3.5.0", forRemoval = true)
    static <T> Specification<T> where(@Nullable Specification<T> spec) {
        return spec == null ? (root, query, builder) -> null : spec;
    }

    非推奨になったけれども、まだ代替手段はないようだ。Nullを許容しなくなることを警告するためのものらしい。

    The where method merely caters for the broken nullability allowance and is replaced by where(PredicateSpecification) in 4.0.

    まだ確定ではないかもしれないが、mainでは下記の様に修正されている。

    /**
     * Simple static factory method to add some syntactic sugar translating {@link PredicateSpecification} to
     * {@link Specification}.
     *
     * @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
     * @param spec the {@link PredicateSpecification} to wrap.
     * @return guaranteed to be not {@literal null}.
     */
    static <T> Specification<T> where(PredicateSpecification<T> spec) {
    
        Assert.notNull(spec, "PredicateSpecification must not be null");
    
        return (root, update, criteriaBuilder) -> spec.toPredicate(root, criteriaBuilder);
    }

    データベース上に「Null」が登録されていることと、Java変数が「Null」であることは意味合いが違うから、そのまま「Null」を渡すことをNGとした感じだろうか。

    @Deprecatedを付けるのはまだ早かったのではないだろうか。(同じようにNullを許容しなくなる他のメソッドにはついていないし)