Skip to content

anton1615/VOCALOID-Search

Repository files navigation

VOCALOID Search

English | 日本語 | 中文

A self-hosted Niconico VOCALOID video search engine with a modern Spotify-like interface.

Security Notice

Before exposing this service outside a local or otherwise trusted environment, set a unique JWT secret and restrict allowed hosts/origins to the exact values you need. Do not deploy the example defaults as-is on an internet-facing system. If you plan to self-host this for other users, review authentication, reverse-proxy, and frontend/backend exposure settings first.

Inspired by ニコニコ超検索 – a popular Niconico video search service.

Screenshots

Search Interface Video Player

Features

  • Modern Interface: Spotify-inspired UI with playlist support and continuous playback via embedded player
  • Infinite Scroll: Modern infinite scrolling design instead of fixed pagination
  • Dark/Light Mode: Toggle between dark and light themes
  • Multi-language Support: Switch between interface languages
  • Compact Mode: Resize window to a compact PIP-like mode for multitasking
  • Self-Hosted: Full control over your data and deployment
  • Custom Formula Sorting & Filtering: Weight videos by view count, mylist count, comment count, and like count using your own formula
  • Watch History: Track and filter videos you've already watched
  • Customizable Scraper: Define your own query conditions to curate the video database
  • Android Support (Prototype): PWA/TWA packaging for mobile use

About This Project

This project is a result of vibe coding – building something useful while exploring modern web technologies and the NixOS ecosystem. It's functional but has rough edges.

Tech Stack

Layer Technology
Backend Python 3.11+, FastAPI, Uvicorn
Frontend TypeScript, React, Vite, Tailwind CSS
Database SQLite with FTS5 full-text search
Data Source Niconico Snapshot API v2 + GetThumbInfo API (user info)
Package Management uv (Python), npm (Node.js)
Deployment NixOS Flakes, systemd services

Key Technical Details

  • Full-text Search: SQLite FTS5 enables fast keyword and tag searches with AND/OR/NOT operators
  • JWT Authentication: Stateless user authentication for multi-user support
  • Embedded Player: Uses official Niconico embed player for seamless playback
  • Infinite Scroll: Dynamic loading for both search results and watch history
  • Formula-based Scoring: Custom weighted scoring system for flexible sorting and filtering

Deployment

Note: This project has only been tested on NixOS with Flakes. Generic Linux builds are provided but untested.

NixOS Flake (Recommended)

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
    vocaloid-search.url = "github:anton1615/VOCALOID-Search";
  };

  outputs = { self, nixpkgs, vocaloid-search, ... }: {
    nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
      modules = [
        vocaloid-search.nixosModules.default
        {
          services.vocaloid-search = {
            enable = true;
            port = 8765;
            jwtSecret = "your-secret-key";
            allowedHosts = [ "example.com" ];
          };
        }
      ];
    };
  };
}

NixOS Module Options

Option Type Default Description
enable bool false Enable the service
port port 8765 Backend API port
frontendPort port 5173 Frontend dev server port
jwtSecret string change-me-in-production JWT secret key
dataDir path /var/lib/vocaloid-search Data directory
allowedHosts list of string [] Vite allowed hosts

NixOS Services

Service Type Description
vocaloid-search.service Simple FastAPI backend
vocaloid-frontend.service Simple Vite dev server
vocaloid-scraper.service Oneshot Sync videos from Niconico API
vocaloid-scraper.timer Timer Daily sync at 06:00

Generic Linux

Prerequisites

  • Python 3.11+
  • Node.js 18+
  • uv Python package manager

Quick Start

git clone https://github.com/anton1615/VOCALOID-Search.git
cd VOCALOID-Search

# Development mode (backend + frontend)
./scripts/start.sh dev

# Backend only
./scripts/start.sh backend

# Frontend only
./scripts/start.sh frontend

# Run scraper
./scripts/start.sh scraper

Install as Systemd Service

export VOCALOID_DATA_DIR=/var/lib/vocaloid-search
export VOCALOID_JWT_SECRET=your-secret-key

./scripts/start.sh install-systemd

systemctl --user enable --now vocaloid-search.service
systemctl --user enable --now vocaloid-frontend.service

Scraper Configuration

The scraper reads configuration from $VOCALOID_DATA_DIR/scraper-config.json. If the file doesn't exist, default values are used.

Configuration File

{
  "query": "VOCALOID",
  "max_age_days": 365,
  "targets": "tags",
  "category_filter": "MUSIC"
}

Options

Option Type Default Description
query string VOCALOID Search query for Niconico Snapshot API
max_age_days int or null 365 Only fetch videos newer than this many days. null = unlimited
targets string tags Search targets (see below)
category_filter string or null MUSIC Niconico category filter

Target Options

Value Description
tags Keyword search, matches tags containing the query
tagsExact Exact match, tag must equal the query exactly
title Search in video titles
description Search in video descriptions
Combined tags,title or tags,title,description

Category Options

Value Niconico Genre
MUSIC 音楽・サウンド (Music & Sound)
GAME ゲーム (Game)
ANIME アニメ (Anime)
ENTERTAINMENT エンターテイメント (Entertainment)
DANCE ダンス (Dance)
OTHER その他 (Other)

Example: Different Database Contents

UTAU songs only:

{
  "query": "UTAU",
  "targets": "tags",
  "category_filter": "MUSIC",
  "max_age_days": null
}

CeVIO and Synthesizer V:

{
  "query": "CeVIO OR Synthesizer_V OR SynthV",
  "targets": "tags",
  "category_filter": "MUSIC"
}

All entertainment (not just music):

{
  "query": "VOCALOID",
  "targets": "tags",
  "category_filter": "ENTERTAINMENT"
}

Environment Variables

Variable Default Description
VOCALOID_DATA_DIR /var/lib/vocaloid-search (NixOS) / ./data (other) Data directory (database + config)
VOCALOID_JWT_SECRET change-me-in-production JWT secret (must set in production)
VOCALOID_PORT 8765 Backend API port
VITE_ALLOWED_HOSTS (empty) Vite allowed hosts, comma-separated

Development

Backend

uv sync
uv run uvicorn vocaloid_search.main:app --reload
uv run python -m vocaloid_search.scraper --once --init-db

Frontend

cd frontend
npm install
npm run dev
npm run build

Known Issues & Limitations

Current Issues

  1. Android interface not polished: The PWA/TWA support is a prototype and hasn't been thoroughly tested
  2. Background playback limitation: Continuous playback doesn't work when the browser tab is in the background due to browser resource management
  3. Player reset on mode switch: Switching between maximized view and narrow window (PIP mode) resets the video player
  4. Region-locked videos: Videos with regional restrictions interrupt continuous playback and cannot be marked as watched
  5. Rudimentary login management: User authentication is minimal
  6. SQLite limitations: Not suitable for high-traffic deployments
  7. Ghost history entries: Videos in watch history that no longer exist in the video database become invisible in the history page
  8. Performance on low-resource hardware: On resource-constrained systems, count queries can take too long and cause the frontend to hang on "searching". Workarounds: (1) use vmtouch to lock the SQLite database in RAM, (2) limit the number of videos in scraper-config.json by narrowing the query or setting max_age_days

Future Plans

  1. Built-in playlist system: Implement "watch later" and custom playlists similar to Niconico's あとで見る feature, but without Niconico's playlist limits
  2. Multiple daily scraper updates: Workaround for Niconico Snapshot API limitations to enable more frequent database updates

License

MIT