feat: Add player_configs feature and migrate user preferences

- Implemented player_configs table to store multiple player configurations per user.
- Migrated existing player settings from user_preferences to player_configs.
- Removed player-related columns from user_preferences.
- Added referral state fields to user for tracking referral rewards.
- Created migration scripts for database changes and data migration.
- Added test cases for app services and usage helpers.
- Introduced video job service interfaces and implementations.
This commit is contained in:
2026-03-24 16:08:36 +00:00
parent 91e5e3542b
commit e7fdd0e1ab
103 changed files with 9540 additions and 8446 deletions

View File

@@ -0,0 +1,65 @@
-- Migration: Create player_configs table
-- Created: 2026-03-19
-- Description: Creates the player_configs table for storing multiple player configurations per user
-- Create player_configs table
CREATE TABLE IF NOT EXISTS player_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
autoplay BOOLEAN NOT NULL DEFAULT FALSE,
loop BOOLEAN NOT NULL DEFAULT FALSE,
muted BOOLEAN NOT NULL DEFAULT FALSE,
show_controls BOOLEAN NOT NULL DEFAULT TRUE,
pip BOOLEAN NOT NULL DEFAULT TRUE,
airplay BOOLEAN NOT NULL DEFAULT TRUE,
chromecast BOOLEAN NOT NULL DEFAULT TRUE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
is_default BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL,
version BIGINT NOT NULL DEFAULT 1
);
-- Index for user_id to optimize queries by user
CREATE INDEX IF NOT EXISTS idx_player_configs_user_id ON player_configs(user_id);
-- Index for is_default to optimize default config lookups
CREATE INDEX IF NOT EXISTS idx_player_configs_is_default ON player_configs(is_default);
-- Composite index for user + is_default (common query pattern)
CREATE INDEX IF NOT EXISTS idx_player_configs_user_default ON player_configs(user_id, is_default);
-- Trigger function to update updated_at and version on row update
CREATE OR REPLACE FUNCTION update_player_configs_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
NEW.version = OLD.version + 1;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Trigger to auto-update updated_at and version
CREATE OR REPLACE TRIGGER trg_update_player_configs
BEFORE UPDATE ON player_configs
FOR EACH ROW
EXECUTE FUNCTION update_player_configs_updated_at();
-- Comments for documentation
COMMENT ON TABLE player_configs IS 'Stores multiple player configurations per user for video playback settings';
COMMENT ON COLUMN player_configs.id IS 'Unique identifier for the player config (UUID)';
COMMENT ON COLUMN player_configs.user_id IS 'Reference to the user who owns this config';
COMMENT ON COLUMN player_configs.name IS 'Human-readable name for the configuration';
COMMENT ON COLUMN player_configs.description IS 'Optional description of the configuration';
COMMENT ON COLUMN player_configs.autoplay IS 'Whether videos should autoplay';
COMMENT ON COLUMN player_configs.loop IS 'Whether videos should loop on end';
COMMENT ON COLUMN player_configs.muted IS 'Whether videos should start muted';
COMMENT ON COLUMN player_configs.show_controls IS 'Whether to show player controls';
COMMENT ON COLUMN player_configs.pip IS 'Whether Picture-in-Picture is enabled';
COMMENT ON COLUMN player_configs.airplay IS 'Whether AirPlay is enabled';
COMMENT ON COLUMN player_configs.chromecast IS 'Whether Chromecast is enabled';
COMMENT ON COLUMN player_configs.is_active IS 'Whether this config is active';
COMMENT ON COLUMN player_configs.is_default IS 'Whether this is the default config for new videos';
COMMENT ON COLUMN player_configs.version IS 'Optimistic locking version number';

View File

@@ -0,0 +1,134 @@
-- Migration: Migrate player settings from user_preferences to player_configs
-- Created: 2026-03-19
-- Description:
-- 1. Creates player_configs records from existing user_preferences player settings
-- 2. Removes player-related columns from user_preferences table
-- 3. Removes encrytion_m3u8 column (VIP feature not in use)
--
-- IMPORTANT: Run this AFTER 001_create_player_configs_table.sql
-- Usage: psql -h <host> -U postgres -d video_db -f 002_migrate_player_settings.sql
BEGIN;
-- ============================================================
-- STEP 1: Migrate existing player settings to player_configs
-- ============================================================
-- Insert player configs for all users that have user_preferences
-- Each user gets one default config with their existing settings
INSERT INTO player_configs (
id,
user_id,
name,
description,
autoplay,
loop,
muted,
show_controls,
pip,
airplay,
chromecast,
is_active,
is_default,
created_at,
updated_at,
version
)
SELECT
gen_random_uuid() AS id,
up.user_id,
'Default Config' AS name,
'Migrated from user_preferences' AS description,
COALESCE(up.autoplay, FALSE) AS autoplay,
COALESCE(up.loop, FALSE) AS loop,
COALESCE(up.muted, FALSE) AS muted,
COALESCE(up.show_controls, TRUE) AS show_controls,
COALESCE(up.pip, TRUE) AS pip,
COALESCE(up.airplay, TRUE) AS airplay,
COALESCE(up.chromecast, TRUE) AS chromecast,
TRUE AS is_active,
TRUE AS is_default,
COALESCE(up.created_at, CURRENT_TIMESTAMP) AS created_at,
CURRENT_TIMESTAMP AS updated_at,
COALESCE(up.version, 1) AS version
FROM user_preferences up
WHERE NOT EXISTS (
-- Skip if user already has a player config (from previous migration)
SELECT 1 FROM player_configs pc WHERE pc.user_id = up.user_id AND pc.is_default = TRUE
);
-- Also handle users that exist in "user" table but don't have user_preferences yet
-- They won't get a config now, but will get default values when they first access settings
-- ============================================================
-- STEP 2: Remove player-related columns from user_preferences
-- ============================================================
-- Drop player settings columns
ALTER TABLE user_preferences
DROP COLUMN IF EXISTS autoplay,
DROP COLUMN IF EXISTS loop,
DROP COLUMN IF EXISTS muted,
DROP COLUMN IF EXISTS show_controls,
DROP COLUMN IF EXISTS pip,
DROP COLUMN IF EXISTS airplay,
DROP COLUMN IF EXISTS chromecast;
-- Drop encrytion_m3u8 column (VIP feature not in use)
ALTER TABLE user_preferences
DROP COLUMN IF EXISTS encrytion_m3u8;
-- ============================================================
-- STEP 3: Add constraint to ensure one default config per user
-- ============================================================
-- Create a partial unique index to ensure only one is_default = TRUE per user
-- This prevents multiple default configs per user
CREATE UNIQUE INDEX IF NOT EXISTS idx_player_configs_one_default_per_user
ON player_configs(user_id)
WHERE is_default = TRUE;
-- ============================================================
-- STEP 4: Verify migration
-- ============================================================
-- Count migrated configs
DO $$
DECLARE
migrated_count INTEGER;
user_count INTEGER;
BEGIN
SELECT COUNT(*) INTO migrated_count FROM player_configs WHERE description = 'Migrated from user_preferences';
SELECT COUNT(*) INTO user_count FROM "user";
RAISE NOTICE '============================================';
RAISE NOTICE 'Migration completed successfully!';
RAISE NOTICE '============================================';
RAISE NOTICE 'Users in system: %', user_count;
RAISE NOTICE 'Player configs created: %', migrated_count;
RAISE NOTICE '============================================';
END $$;
COMMIT;
-- ============================================================
-- Verification queries (run after migration)
-- ============================================================
-- Verify player_configs structure
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'player_configs'
ORDER BY ordinal_position;
-- Verify user_preferences structure (should not have player columns anymore)
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'user_preferences'
ORDER BY ordinal_position;
-- Sample migrated data
SELECT pc.user_id, pc.name, pc.autoplay, pc.loop, pc.muted, pc.is_default
FROM player_configs pc
WHERE pc.description = 'Migrated from user_preferences'
LIMIT 5;

View File

@@ -0,0 +1,17 @@
-- Migration: Add user referral state
-- Created: 2026-03-23
-- Description: Adds minimal referral linkage and first-subscription reward tracking fields to user
BEGIN;
ALTER TABLE "user"
ADD COLUMN IF NOT EXISTS referred_by_user_id UUID REFERENCES "user"(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS referral_eligible BOOLEAN NOT NULL DEFAULT TRUE,
ADD COLUMN IF NOT EXISTS referral_reward_bps INTEGER,
ADD COLUMN IF NOT EXISTS referral_reward_granted_at TIMESTAMP WITH TIME ZONE,
ADD COLUMN IF NOT EXISTS referral_reward_payment_id UUID,
ADD COLUMN IF NOT EXISTS referral_reward_amount NUMERIC(65,30);
CREATE INDEX IF NOT EXISTS idx_user_referred_by_user_id ON "user"(referred_by_user_id);
COMMIT;