• Analyzes position data to determine if a track change was a skip

    Evaluates track progress information to determine if a track change should be classified as a skip, and if so, what type of skip it was. This algorithm considers factors like:

    • Progress percentage relative to track duration
    • User-defined skip threshold preferences
    • Very short plays (preview skips)
    • Near-completion skips (almost finished tracks)
    • Normal track completions

    Each classification includes a confidence rating indicating the algorithm's certainty in its determination.

    Parameters

    • previousState: PlaybackState

      Previous playback state before track change

    • skipThreshold: number

      User-defined skip threshold as decimal (0.0-1.0)

    Returns { isSkip: boolean; skipType: SkipType; confidence: number; reason: string }

    Object containing skip detection results:

    • isSkip: Whether the track change is classified as a skip
    • skipType: Classification of the skip type
    • confidence: Confidence level in the determination (0.0-1.0)
    • reason: Human-readable explanation of the determination
    // Analyze if the previous track was skipped
    const skipResult = analyzePositionBasedSkip(previousState, 0.7);

    if (skipResult.isSkip) {
    console.log(`Track skipped (${skipResult.skipType}): ${skipResult.reason}`);
    } else {
    console.log('Track completed normally');
    }
    export function analyzePositionBasedSkip(
    previousState: PlaybackState,
    skipThreshold: number,
    ): {
    isSkip: boolean;
    skipType: SkipType;
    confidence: number;
    reason: string;
    } {
    // Default result
    const result = {
    isSkip: false,
    skipType: SkipType.NONE,
    confidence: 0,
    reason: "No skip detected",
    };

    // Need basic information to analyze
    if (
    !previousState ||
    !previousState.currentTrackDuration ||
    !previousState.lastProgress
    ) {
    return result;
    }

    const duration = previousState.currentTrackDuration;
    const progress = previousState.lastProgress;
    const progressPercent = progress / duration;

    // 1. Quick Skip Detection (played very briefly)
    if (progress < VERY_SHORT_PLAY_MS) {
    return {
    isSkip: true,
    skipType: SkipType.QUICK_PREVIEW,
    confidence: 0.9,
    reason: `Track played for only ${progress}ms, likely a quick preview skip`,
    };
    }

    // 2. Standard Skip Detection (below threshold)
    if (progressPercent < skipThreshold) {
    // Calculate confidence based on how far from threshold
    const confidenceValue = 1 - progressPercent / skipThreshold;

    return {
    isSkip: true,
    skipType: SkipType.STANDARD,
    confidence: Math.min(0.95, confidenceValue),
    reason: `Track skipped at ${(progressPercent * 100).toFixed(1)}%, below threshold of ${skipThreshold * 100}%`,
    };
    }

    // 3. Near-Completion (close to end but not quite finished)
    if (progressPercent > 0.9 && progressPercent < 0.98) {
    // Only count as a skip if it's below the user's threshold
    if (progressPercent < skipThreshold) {
    return {
    isSkip: true,
    skipType: SkipType.NEAR_COMPLETION,
    confidence: 0.7,
    reason: `Track nearly finished (${(progressPercent * 100).toFixed(1)}%) but skipped before end`,
    };
    } else {
    // Track has passed user's threshold, so don't count as a skip
    return {
    isSkip: false,
    skipType: SkipType.NONE,
    confidence: 0.9,
    reason: `Track played beyond threshold (${(progressPercent * 100).toFixed(1)}% > ${skipThreshold * 100}%), considered completed`,
    };
    }
    }

    // 4. Normal completion or very close to end
    if (progressPercent >= 0.98) {
    return {
    isSkip: false,
    skipType: SkipType.NONE,
    confidence: 0.95,
    reason: `Track completed (${(progressPercent * 100).toFixed(1)}%)`,
    };
    }

    // If we get here, the track wasn't skipped by our thresholds
    return result;
    }