Skip to content
itsjustadarshPublic

About

A sleek web application that transforms YouTube playlists into a clean, focused experience. Track your progress, take markdown notes per video, and navigate seamlessly with keyboard shortcuts.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Void List - YouTube Playlist Manager

A sleek web application that transforms YouTube playlists into a clean, focused experience. Track your progress, take markdown notes per video, and navigate seamlessly with keyboard shortcuts.

🌐 Live Demo: void-list.vercel.app

Features

  • Direct URL Input: Paste YouTube playlist URLs directly - no need to extract playlist IDs
  • Progress Tracking: Visual progress bars and completion tracking for each playlist
  • Per-Video Notes: Write and view markdown-formatted notes for each video with tabbed interface
  • Keyboard Shortcuts: Navigate efficiently with keyboard controls
  • Local Storage: All data is stored locally in your browser (no external database)

Tech Stack

  • Framework: Next.js 15.5.2 with App Router
  • Language: TypeScript
  • Markdown: ReactMarkdown for note rendering
  • Data: Local browser storage (no external dependencies)

Architecture Overview

Core Components

1. Home Page (src/app/page.tsx)

  • Main landing page with playlist input and management
  • Core Logic:
    • URL parsing with regex patterns to extract playlist IDs from various YouTube URL formats
    • React state management for playlists, search filtering, and loading states
    • Local storage integration through cache system
    • API calls to /api/playlist endpoint for fetching playlist data

2. Playlist Viewer (src/app/playlist/[id]/page.tsx)

  • Individual playlist interface with video player and notes
  • Core Logic:
    • Dynamic route parameter extraction using Next.js App Router
    • YouTube iframe URL construction with playlist and video IDs
    • Real-time note saving with debounced auto-save (500ms delay)
    • Keyboard event handling with focus detection to prevent conflicts
    • Progress state management and persistence to local storage

3. API Route (src/app/api/playlist/route.ts)

  • YouTube playlist scraping endpoint
  • Scraping Algorithm:
    1. Constructs YouTube playlist URL with optional continuation token
    2. Sends HTTP request with browser User-Agent to bypass bot detection
    3. Parses HTML response using multiple regex patterns for robustness
    4. Extracts video IDs using pattern: "videoId":"([a-zA-Z0-9_-]{11})"
    5. Extracts titles from JSON embedded in HTML using multiple fallback patterns
    6. Handles pagination by finding continuation tokens in HTML
    7. Implements 100ms delays between requests for rate limiting
    8. Deduplicates video IDs and titles across pages
    9. Returns structured JSON with all extracted data

4. Cache System (src/lib/cache.ts)

  • Local storage management for playlist data
  • Storage Implementation:
    interface PlaylistData {
      id: string;
      title: string;
      videoIds: string[];
      titles: string[];
      completedVideos: string[];
      currentVideo: string | null;
      lastAccessed: number;
      createdAt: number;
      videoNotes: Record<string, VideoNote>;
    }
  • Core Methods:
    • savePlaylist(): Serializes data to JSON and stores in localStorage
    • getPlaylist(): Deserializes JSON from localStorage with error handling
    • getRecentPlaylists(): Retrieves all playlists sorted by lastAccessed timestamp
    • updateVideoNote(): Updates specific video note and persists entire playlist
    • clearAll(): Removes all playlist data from localStorage

5. UI Components (src/components/)

  • PlaylistCard: Calculates progress percentage, formats dates, handles click events
  • Logo: Simple SVG component with configurable size props
  • Component Logic: Event handling, state management, and data formatting

Key Technical Implementations

URL Parsing Algorithm

const extractPlaylistId = (url: string): string | null => {
  const patterns = [
    /[?&]list=([a-zA-Z0-9_-]+)/,    // Standard YouTube URLs with list parameter
    /playlist\?list=([a-zA-Z0-9_-]+)/, // Direct playlist URLs
    /^([a-zA-Z0-9_-]+)$/              // Raw playlist ID input
  ];
  
  for (const pattern of patterns) {
    const match = url.match(pattern);
    if (match) return match[1];
  }
  return null;
}

Web Scraping Implementation

// Multiple regex patterns for robust data extraction
const videoIdRegex = /"videoId":"([a-zA-Z0-9_-]{11})"/g;
const titlePatterns = [
  /"title":{"runs":\[{"text":"([^"]+)"/g,
  /"title":"([^"]+)"/g,
  /"videoTitle":"([^"]+)"/g
];

// Pagination handling
const continuationRegex = /"continuationCommand":{"token":"([^"]+)"/;
const continuationMatch = continuationRegex.exec(html);
continuationToken = continuationMatch ? continuationMatch[1] : '';

Keyboard Event Handling

useEffect(() => {
  const handleKeyDown = (e: KeyboardEvent) => {
    const target = e.target as HTMLElement;
    
    // Prevent conflicts when typing in textarea
    if (target.tagName === 'TEXTAREA') return;
    
    if (e.code === 'Space') {
      e.preventDefault();
      toggleVideoCompleted();
    }
  };
  
  document.addEventListener('keydown', handleKeyDown);
  return () => document.removeEventListener('keydown', handleKeyDown);
}, [currentVideoId]);

Auto-Save Notes Implementation

const debouncedSave = useCallback(
  debounce((content: string) => {
    playlistCache.updateVideoNote(playlistId, currentVideoId, {
      content,
      lastModified: Date.now()
    });
  }, 500),
  [playlistId, currentVideoId]
);

Error Handling & Edge Cases

API Error Handling

try {
  const response = await fetch(playlistUrl, { headers: { 'User-Agent': '...' } });
  if (!response.ok) {
    return NextResponse.json(
      { error: 'Failed to fetch playlist page' },
      { status: response.status }
    );
  }
} catch (error) {
  console.error('Failed to scrape playlist:', error);
  return NextResponse.json(
    { error: 'Failed to scrape playlist' },
    { status: 500 }
  );
}

Data Validation

  • Playlist ID validation using regex patterns
  • Video ID format validation (11-character alphanumeric)
  • Title length validation (max 200 characters)
  • Array length synchronization between video IDs and titles
  • Safe JSON parsing with fallback handling for corrupted localStorage data

Data Flow Architecture

  1. Input Processing:

    • User input validated and parsed using regex patterns
    • Playlist ID extracted from various URL formats
    • Input sanitization and error handling
  2. API Request Cycle:

    • GET request to /api/playlist?playlistId=<id>
    • Server-side scraping with pagination loops
    • Rate limiting and continuation token handling
    • JSON response with structured data
  3. Data Transformation:

    • Raw HTML parsing with multiple regex patterns
    • Video ID and title extraction and deduplication
    • Array synchronization and padding for missing data
    • Playlist metadata extraction and cleaning
  4. State Management:

    • React useState hooks for component state
    • useEffect hooks for side effects and data fetching
    • useCallback hooks for memoized functions
    • Event handlers for user interactions
  5. Persistence Layer:

    • JSON serialization of playlist data
    • localStorage API for browser storage
    • Timestamp tracking for access patterns
    • Atomic updates for data consistency

Technical Limitations & Solutions

Cross-Origin Restrictions

  • YouTube iframe prevents programmatic video control
  • Solution: User-initiated controls only, no automated play/pause
  • Keyboard shortcuts work at document level, not iframe level

Rate Limiting Strategy

if (continuationToken && pageCount < maxPages) {
  await new Promise(resolve => setTimeout(resolve, 100));
}
  • 100ms delays between pagination requests
  • Maximum 20 pages limit to prevent infinite loops
  • Browser User-Agent headers to avoid bot detection

Data Consistency

  • Deduplication algorithms for video IDs across pages
  • Array length synchronization between IDs and titles
  • Fallback title generation for missing data
  • Safe JSON parsing with error recovery

Getting Started

Prerequisites

  • Node.js 18+
  • npm, yarn, pnpm, or bun

Installation

# Install dependencies
npm install

# Run development server
npm run dev

# Build for production
npm run build

# Start production server
npm start

Open http://localhost:3000 to view the application.

Usage

  1. Add Playlist: Paste any YouTube playlist URL into the input field
  2. Navigate: Click on playlist cards to start watching
  3. Take Notes: Use the Notes tab to write markdown-formatted notes for each video
  4. Track Progress: Videos are automatically marked as completed when you navigate to the next one
  5. Search: Use the search bar to filter through your saved playlists

Performance Considerations

  • Client-Side: React component memoization and debounced auto-save
  • Server-Side: Efficient regex patterns and minimal DOM parsing
  • Storage: JSON serialization optimized for localStorage limits
  • Network: Respectful rate limiting and error recovery mechanisms

Data Storage Implementation

⚠️ Local Storage Only: All data stored in browser localStorage as JSON

  • No external databases or cloud storage
  • Data persists across browser sessions
  • Clearing browser cache will delete all playlists and progress
  • Storage limit: ~5-10MB depending on browser (sufficient for hundreds of playlists)

Development Notes

  • Built with Next.js App Router (not Pages Router)
  • TypeScript strict mode enabled
  • No external dependencies for core functionality
  • Web scraping approach may need updates if YouTube changes their HTML structure
  • Local storage approach eliminates need for user accounts or databases

About

A sleek web application that transforms YouTube playlists into a clean, focused experience. Track your progress, take markdown notes per video, and navigate seamlessly with keyboard shortcuts.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages