展示HN:VAM Seek – 2D视频导航网格,15KB,零服务器负载
Show HN: VAM Seek – 2D video navigation grid, 15KB, zero server load

原始链接: https://github.com/unhaya/vam-seek

## VAM Seek:一个视觉视频导航库 VAM Seek 是一个轻量级(约15KB JavaScript)的库,提供了一种新颖的视频导航方式。它不使用传统的进度条,而是呈现一个可视化的缩略图网格,以便即时访问场景,消除盲目拖动。它直接在浏览器中运行,优先考虑隐私并减少服务器负载。 主要特性包括使用 Canvas API 进行客户端帧提取(无需服务器端处理或 FFmpeg),一个内存高效的 LRU 缓存,可存储最多 200 帧,以及流畅的 60fps 标记动画。集成非常简单——只需一个脚本标签和几行 JavaScript 代码即可。 与依赖服务器处理和存储的传统缩略图系统不同,VAM Seek 将所有视频数据保存在本地。它支持 React、Vue 和原生 JavaScript,并且免费用于非商业用途。该库计算精确的时间戳,并提供 API 控制以进行定位、配置和清理。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 展示 HN: VAM Seek – 2D 视频导航网格,15KB,零服务器负载 (github.com/unhaya) 5 分,haasiy 发表于 1 小时前 | 隐藏 | 过去 | 收藏 | 讨论 大家好!我制作了 VAM Seek,因为我对 1D 进度条感到沮丧——你永远不知道会到达哪里,直到你到达那里。 VAM Seek 在你的视频旁边渲染一个 2D 缩略图网格。点击任意单元格即可跳转。所有帧提取都在客户端通过 canvas 进行——无需服务器处理,无需预生成缩略图。 - 15KB,零依赖 - 一行集成 - 适用于任何
相关文章

原文

License: Dual Size No Dependencies Browser

Try Live Demo

demo-trimmed.mp4

A lightweight 2D video seek grid library for video streaming sites.

Navigate videos visually with a thumbnail grid instead of a 1D seek bar. Client-side frame extraction with smooth marker animation.

🎯 Stop blind scrubbing. See every scene at once.

Traditional Seek Bar VAM Seek
1D timeline, trial-and-error 2D grid, instant visual navigation
Server-generated thumbnails Client-side canvas extraction
Heavy infrastructure Zero server load, ~15KB JS
Complex integration One-line setup

Keywords: video player, video seek, thumbnail grid, video navigation, HTML5 video, JavaScript library, video streaming, media player, video controls, video thumbnails, seek bar alternative, video UX

Quick Start (For External Sites)

<!-- 1. Add the script -->
<script src="https://cdn.jsdelivr.net/gh/unhaya/vam-seek/dist/vam-seek.js"></script>

<!-- 2. Connect to your video -->
<script>
  VAMSeek.init({
    video: document.getElementById('myVideo'),
    container: document.getElementById('seekGrid'),
    columns: 5,
    secondsPerCell: 15
  });
</script>

That's it. See docs/INTEGRATION.md for full documentation.

  • Client-side frame extraction - No server CPU usage
  • LRU cache - 200 frames cached in memory
  • Smooth marker animation - 60fps with requestAnimationFrame
  • VAM algorithm - Precise timestamp calculation
  • Framework support - React, Vue, vanilla JS examples included

Your video never leaves the browser.

Traditional thumbnail systems upload videos to a server, process with FFmpeg, store thumbnails, and serve via CDN. This costs money, takes time, and raises privacy concerns.

VAM Seek works differently:

Traditional VAM Seek
Video uploaded to server Video stays in browser
Server-side FFmpeg processing Client-side Canvas API
Thumbnails stored on disk Frames cached in memory
CDN bandwidth costs Zero server cost
Privacy risk Fully private

All frame extraction happens in the user's browser using the Canvas API. When the page closes, everything is gone. No data is ever sent to any server.

VAM_web/
├── dist/                       # Distributable files
│   └── vam-seek.js             # Standalone library (1 file, ~15KB)
│
├── docs/                       # Documentation
│   └── INTEGRATION.md          # API integration guide
│
├── examples/                   # Integration examples
│   ├── basic-integration.html  # Vanilla JS example
│   ├── react-integration.jsx   # React component & hook
│   └── vue-integration.vue     # Vue 3 component
│
├── backend/                    # FastAPI backend (for demo)
│   ├── main.py                 # Entry point, static file serving
│   ├── requirements.txt        # Python dependencies
│   ├── core/                   # Core logic
│   │   ├── grid_calc.py        # VAM grid calculation
│   │   └── video_utils.py      # FFmpeg video processing
│   ├── models/                 # Pydantic schemas
│   │   └── schemas.py          # Request/response models
│   ├── routers/                # API routers
│   │   ├── grid.py             # /api/grid/* endpoints
│   │   └── video.py            # /api/video/* endpoints
│   ├── uploads/                # Uploaded videos (gitignore)
│   └── thumbnails/             # Generated thumbnails (gitignore)
│
├── frontend/                   # Demo frontend
│   ├── index.html              # Main UI with embedded JS
│   └── assets/
│       └── marker.svg          # Grid marker icon
│
├── .gitignore
└── README.md

CDN (Recommended)

<script src="https://cdn.jsdelivr.net/gh/unhaya/vam-seek/dist/vam-seek.js"></script>

npm

const vam = VAMSeek.init({
  video: document.getElementById('video'),
  container: document.getElementById('grid'),
  columns: 5,
  secondsPerCell: 15,
  onSeek: (time, cell) => {
    console.log(`Seeked to ${time}s`);
  }
});

// API
vam.seekTo(120);           // Seek to 2:00
vam.moveToCell(2, 3);      // Move to column 2, row 3
vam.configure({ columns: 8 }); // Update settings
vam.destroy();             // Clean up
Option Type Default Description
video HTMLVideoElement required Target video element
container HTMLElement required Container for the grid
columns number 3 Grid columns (3-10)
secondsPerCell number 5 Seconds per cell
cacheSize number 200 LRU cache size
onSeek function null Seek callback
  • Python 3.9+
  • FFmpeg (in PATH)
cd backend
pip install -r requirements.txt
python main.py

Open http://localhost:8000

Method Path Description
GET / Serve frontend
GET /api/health Health check
POST /api/grid/config Calculate grid dimensions
POST /api/grid/position Calculate timestamp from position
POST /api/video/upload Upload video (demo only)

Frame Extraction (Client-side)

// 1. Create hidden video element
const video = document.createElement('video');
video.src = 'video.mp4';

// 2. Seek to timestamp
video.currentTime = 15.0;

// 3. Capture on seeked event
video.addEventListener('seeked', () => {
  const canvas = document.createElement('canvas');
  ctx.drawImage(video, 0, 0);
  const dataUrl = canvas.toDataURL('image/jpeg', 0.8);
});

// 4. Cache with LRU (max 200 frames)
frameCache.put(videoSrc, timestamp, dataUrl);
// X-continuous timestamp calculation
function calculateTimestamp(x, y, gridWidth, gridHeight, duration, secondsPerCell) {
  const rowIndex = Math.floor(y / gridHeight * rows);
  const colContinuous = x / gridWidth * columns;
  const cellIndex = rowIndex * columns + colContinuous;
  return Math.min(cellIndex * secondsPerCell, duration);
}
Key Action
Arrow Keys Move marker by cell
Space Play/Pause
Home First cell
End Last cell
  • Chrome 80+
  • Firefox 75+
  • Safari 14+
  • Edge 80+
  • Mobile browsers

Free for personal, educational, and research use. Commercial use requires a paid license. Contact: [email protected]

2025-01-10: Initial Release

  • FastAPI backend with modular architecture
  • Client-side frame extraction (video + canvas)
  • VAM-compliant marker movement (X-continuous mode)
  • LRU cache with fade-in animation
  • Scroll position fix
  • Same-origin serving for CORS

2025-01-10: Library Release

  • Standalone vam-seek.js for external integration
  • Integration documentation
  • React, Vue examples

Based on VAM Desktop application algorithms:

  • vam5.70/utils/video_utils.py - calculate_x_continuous_timestamp
  • vam5.70/gui/preview/core/grid_calculator.py - GridCalculator
联系我们 contact @ memedata.com