4, partial 5

This commit is contained in:
2026-02-04 01:00:57 -05:00
parent 7b5c256e07
commit d5ce8452e4
20 changed files with 2215 additions and 69 deletions

65
src/types/auth.ts Normal file
View File

@@ -0,0 +1,65 @@
/**
* Authentication types for PodTUI
* Authentication is optional and disabled by default
*/
/** User profile information */
export interface User {
id: string
email: string
name: string
createdAt: Date
lastLoginAt?: Date
syncEnabled: boolean
}
/** Authentication state */
export interface AuthState {
user: User | null
isAuthenticated: boolean
isLoading: boolean
error: AuthError | null
}
/** Authentication error */
export interface AuthError {
code: AuthErrorCode
message: string
}
/** Error codes for authentication */
export enum AuthErrorCode {
INVALID_CREDENTIALS = "INVALID_CREDENTIALS",
INVALID_CODE = "INVALID_CODE",
CODE_EXPIRED = "CODE_EXPIRED",
NETWORK_ERROR = "NETWORK_ERROR",
UNKNOWN_ERROR = "UNKNOWN_ERROR",
}
/** Login credentials */
export interface LoginCredentials {
email: string
password: string
}
/** Code validation request */
export interface CodeValidationRequest {
code: string
}
/** OAuth provider types */
export enum OAuthProvider {
GOOGLE = "google",
APPLE = "apple",
}
/** OAuth provider configuration */
export interface OAuthProviderConfig {
id: OAuthProvider
name: string
enabled: boolean
description: string
}
/** Auth screen types for navigation */
export type AuthScreen = "login" | "code" | "oauth" | "profile"

86
src/types/episode.ts Normal file
View File

@@ -0,0 +1,86 @@
/**
* Episode type definitions for PodTUI
*/
/** Episode playback status */
export enum EpisodeStatus {
NOT_STARTED = "not_started",
PLAYING = "playing",
PAUSED = "paused",
COMPLETED = "completed",
}
/** Core episode information */
export interface Episode {
/** Unique identifier */
id: string
/** Parent podcast ID */
podcastId: string
/** Episode title */
title: string
/** Episode description/show notes */
description: string
/** Audio file URL */
audioUrl: string
/** Duration in seconds */
duration: number
/** Publication date */
pubDate: Date
/** Episode number (if available) */
episodeNumber?: number
/** Season number (if available) */
seasonNumber?: number
/** Episode type (full, trailer, bonus) */
episodeType?: EpisodeType
/** Whether episode is explicit */
explicit?: boolean
/** Episode image URL (if different from podcast) */
imageUrl?: string
/** File size in bytes */
fileSize?: number
/** MIME type */
mimeType?: string
}
/** Episode type enumeration */
export enum EpisodeType {
FULL = "full",
TRAILER = "trailer",
BONUS = "bonus",
}
/** Episode playback progress */
export interface Progress {
/** Episode ID */
episodeId: string
/** Current position in seconds */
position: number
/** Total duration in seconds */
duration: number
/** Last played timestamp */
timestamp: Date
/** Playback speed (1.0 = normal) */
playbackSpeed?: number
}
/** Episode with playback state */
export interface EpisodeWithProgress extends Episode {
/** Current playback status */
status: EpisodeStatus
/** Playback progress */
progress?: Progress
}
/** Episode list item for display */
export interface EpisodeListItem {
/** Episode data */
episode: Episode
/** Podcast title (for display in feeds) */
podcastTitle: string
/** Podcast cover URL */
podcastCoverUrl?: string
/** Current status */
status: EpisodeStatus
/** Progress percentage (0-100) */
progressPercent: number
}

116
src/types/feed.ts Normal file
View File

@@ -0,0 +1,116 @@
/**
* Feed type definitions for PodTUI
*/
import type { Podcast } from "./podcast"
import type { Episode, EpisodeStatus } from "./episode"
/** Feed visibility */
export enum FeedVisibility {
PUBLIC = "public",
PRIVATE = "private",
}
/** Feed information */
export interface Feed {
/** Unique identifier */
id: string
/** Associated podcast */
podcast: Podcast
/** Episodes in this feed */
episodes: Episode[]
/** Whether feed is public or private */
visibility: FeedVisibility
/** Source ID that provided this feed */
sourceId: string
/** Last updated timestamp */
lastUpdated: Date
/** Custom feed name (user-defined) */
customName?: string
/** User notes about this feed */
notes?: string
/** Whether feed is pinned/favorited */
isPinned: boolean
/** Feed color for UI */
color?: string
}
/** Feed item for display in lists */
export interface FeedItem {
/** Episode data */
episode: Episode
/** Parent podcast */
podcast: Podcast
/** Feed ID */
feedId: string
/** Episode status */
status: EpisodeStatus
/** Progress percentage (0-100) */
progressPercent: number
/** Whether this item is new (unplayed) */
isNew: boolean
}
/** Feed filter options */
export interface FeedFilter {
/** Filter by visibility */
visibility?: FeedVisibility | "all"
/** Filter by source ID */
sourceId?: string
/** Filter by pinned status */
pinnedOnly?: boolean
/** Search query for filtering */
searchQuery?: string
/** Sort field */
sortBy?: FeedSortField
/** Sort direction */
sortDirection?: "asc" | "desc"
}
/** Feed sort fields */
export enum FeedSortField {
/** Sort by last updated */
UPDATED = "updated",
/** Sort by title */
TITLE = "title",
/** Sort by episode count */
EPISODE_COUNT = "episodeCount",
/** Sort by most recent episode */
LATEST_EPISODE = "latestEpisode",
}
/** Feed list display options */
export interface FeedListOptions {
/** Show episode count */
showEpisodeCount: boolean
/** Show last updated */
showLastUpdated: boolean
/** Show source indicator */
showSource: boolean
/** Compact mode */
compact: boolean
}
/** Default feed list options */
export const DEFAULT_FEED_LIST_OPTIONS: FeedListOptions = {
showEpisodeCount: true,
showLastUpdated: true,
showSource: false,
compact: false,
}
/** Feed statistics */
export interface FeedStats {
/** Total feed count */
totalFeeds: number
/** Public feed count */
publicFeeds: number
/** Private feed count */
privateFeeds: number
/** Total episode count across all feeds */
totalEpisodes: number
/** Unplayed episode count */
unplayedEpisodes: number
/** In-progress episode count */
inProgressEpisodes: number
}

40
src/types/podcast.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* Podcast type definitions for PodTUI
*/
/** Core podcast information */
export interface Podcast {
/** Unique identifier */
id: string
/** Podcast title */
title: string
/** Podcast description/summary */
description: string
/** Cover image URL */
coverUrl?: string
/** RSS feed URL */
feedUrl: string
/** Author/creator name */
author?: string
/** Podcast categories */
categories?: string[]
/** Language code (e.g., 'en', 'es') */
language?: string
/** Website URL */
websiteUrl?: string
/** Last updated timestamp */
lastUpdated: Date
/** Whether the podcast is currently subscribed */
isSubscribed: boolean
}
/** Podcast with episodes included */
export interface PodcastWithEpisodes extends Podcast {
/** List of episodes */
episodes: Episode[]
/** Total episode count */
totalEpisodes: number
}
/** Episode import - needed for PodcastWithEpisodes */
import type { Episode } from "./episode"

103
src/types/source.ts Normal file
View File

@@ -0,0 +1,103 @@
/**
* Podcast source type definitions for PodTUI
*/
/** Source type enumeration */
export enum SourceType {
/** RSS feed URL */
RSS = "rss",
/** API-based source (iTunes, Spotify, etc.) */
API = "api",
/** Custom/user-defined source */
CUSTOM = "custom",
}
/** Podcast source configuration */
export interface PodcastSource {
/** Unique identifier */
id: string
/** Source display name */
name: string
/** Source type */
type: SourceType
/** Base URL for the source */
baseUrl: string
/** API key (if required) */
apiKey?: string
/** Whether source is enabled */
enabled: boolean
/** Source icon/logo URL */
iconUrl?: string
/** Source description */
description?: string
/** Rate limit (requests per minute) */
rateLimit?: number
/** Last successful fetch */
lastFetch?: Date
}
/** Search query configuration */
export interface SearchQuery {
/** Search query text */
query: string
/** Source IDs to search (empty = all enabled sources) */
sourceIds: string[]
/** Optional filters */
filters?: SearchFilters
}
/** Search filters */
export interface SearchFilters {
/** Filter by language */
language?: string
/** Filter by category */
category?: string
/** Filter by explicit content */
explicit?: boolean
/** Sort by field */
sortBy?: SearchSortField
/** Sort direction */
sortDirection?: "asc" | "desc"
/** Results limit */
limit?: number
/** Results offset for pagination */
offset?: number
}
/** Search sort fields */
export enum SearchSortField {
RELEVANCE = "relevance",
DATE = "date",
TITLE = "title",
POPULARITY = "popularity",
}
/** Search result */
export interface SearchResult {
/** Source that returned this result */
sourceId: string
/** Podcast data */
podcast: import("./podcast").Podcast
/** Relevance score (0-1) */
score?: number
}
/** Default podcast sources */
export const DEFAULT_SOURCES: PodcastSource[] = [
{
id: "itunes",
name: "Apple Podcasts",
type: SourceType.API,
baseUrl: "https://itunes.apple.com/search",
enabled: true,
description: "Search the Apple Podcasts directory",
},
{
id: "rss",
name: "RSS Feed",
type: SourceType.RSS,
baseUrl: "",
enabled: true,
description: "Add podcasts via RSS feed URL",
},
]