SYMBOL INDEX (1162 symbols across 182 files) FILE: frontend/src/App.tsx function AppShell (line 32) | function AppShell() { function App (line 293) | function App() { FILE: frontend/src/components/AddFeedForm.tsx type AddFeedFormProps (line 7) | interface AddFeedFormProps { type AddMode (line 13) | type AddMode = 'url' | 'search'; constant PAGE_SIZE (line 15) | const PAGE_SIZE = 10; function AddFeedForm (line 17) | function AddFeedForm({ onSuccess, onUpgradePlan, planLimitReached }: Add... FILE: frontend/src/components/AudioPlayer.tsx function AudioPlayer (line 35) | function AudioPlayer() { FILE: frontend/src/components/DiagnosticsModal.tsx constant GITHUB_NEW_ISSUE_URL (line 5) | const GITHUB_NEW_ISSUE_URL = 'https://github.com/podly-pure-podcasts/pod... function DiagnosticsModal (line 22) | function DiagnosticsModal() { FILE: frontend/src/components/DownloadButton.tsx type DownloadButtonProps (line 10) | interface DownloadButtonProps { function DownloadButton (line 19) | function DownloadButton({ FILE: frontend/src/components/EpisodeProcessingStatus.tsx type EpisodeProcessingStatusProps (line 3) | interface EpisodeProcessingStatusProps { function EpisodeProcessingStatus (line 11) | function EpisodeProcessingStatus({ FILE: frontend/src/components/FeedDetail.tsx type FeedDetailProps (line 15) | interface FeedDetailProps { type SortOption (line 21) | type SortOption = 'newest' | 'oldest' | 'title'; type ProcessingEstimate (line 23) | interface ProcessingEstimate { constant EPISODES_PAGE_SIZE (line 30) | const EPISODES_PAGE_SIZE = 25; function FeedDetail (line 32) | function FeedDetail({ feed, onClose, onFeedDeleted }: FeedDetailProps) { FILE: frontend/src/components/FeedList.tsx type FeedListProps (line 5) | interface FeedListProps { function FeedList (line 12) | function FeedList({ feeds, onFeedDeleted: _onFeedDeleted, onFeedSelected... FILE: frontend/src/components/PlayButton.tsx type PlayButtonProps (line 4) | interface PlayButtonProps { function PlayButton (line 21) | function PlayButton({ episode, className = '' }: PlayButtonProps) { FILE: frontend/src/components/ProcessingStatsButton.tsx type ProcessingStatsButtonProps (line 5) | interface ProcessingStatsButtonProps { function ProcessingStatsButton (line 11) | function ProcessingStatsButton({ FILE: frontend/src/components/ReprocessButton.tsx type ReprocessButtonProps (line 5) | interface ReprocessButtonProps { function ReprocessButton (line 14) | function ReprocessButton({ FILE: frontend/src/components/config/ConfigContext.tsx type ConfigTabId (line 4) | type ConfigTabId = 'default' | 'advanced' | 'users' | 'discord'; type AdvancedSubtab (line 5) | type AdvancedSubtab = 'llm' | 'whisper' | 'processing' | 'output' | 'app'; type ConfigContextValue (line 7) | interface ConfigContextValue extends UseConfigStateReturn { function useConfigContext (line 18) | function useConfigContext(): ConfigContextValue { FILE: frontend/src/components/config/ConfigTabs.tsx constant TABS (line 12) | const TABS: { id: ConfigTabId; label: string; adminOnly?: boolean }[] = [ function ConfigTabs (line 19) | function ConfigTabs() { FILE: frontend/src/components/config/sections/AppSection.tsx function AppSection (line 4) | function AppSection() { FILE: frontend/src/components/config/sections/LLMSection.tsx constant LLM_MODEL_ALIASES (line 8) | const LLM_MODEL_ALIASES: string[] = [ function LLMSection (line 20) | function LLMSection() { function BaseUrlInfoBox (line 205) | function BaseUrlInfoBox() { FILE: frontend/src/components/config/sections/OutputSection.tsx function OutputSection (line 4) | function OutputSection() { FILE: frontend/src/components/config/sections/ProcessingSection.tsx function ProcessingSection (line 4) | function ProcessingSection() { FILE: frontend/src/components/config/sections/WhisperSection.tsx function WhisperSection (line 8) | function WhisperSection() { FILE: frontend/src/components/config/shared/ConnectionStatusCard.tsx type ConnectionStatusCardProps (line 1) | interface ConnectionStatusCardProps { function ConnectionStatusCard (line 9) | function ConnectionStatusCard({ FILE: frontend/src/components/config/shared/EnvOverrideWarningModal.tsx type EnvOverrideWarningModalProps (line 4) | interface EnvOverrideWarningModalProps { function EnvOverrideWarningModal (line 11) | function EnvOverrideWarningModal({ FILE: frontend/src/components/config/shared/EnvVarHint.tsx type EnvVarHintProps (line 3) | interface EnvVarHintProps { function EnvVarHint (line 7) | function EnvVarHint({ meta }: EnvVarHintProps) { FILE: frontend/src/components/config/shared/Field.tsx type FieldProps (line 5) | interface FieldProps { function Field (line 13) | function Field({ FILE: frontend/src/components/config/shared/SaveButton.tsx type SaveButtonProps (line 1) | interface SaveButtonProps { function SaveButton (line 7) | function SaveButton({ onSave, isPending, className = '' }: SaveButtonPro... FILE: frontend/src/components/config/shared/Section.tsx type SectionProps (line 3) | interface SectionProps { function Section (line 9) | function Section({ title, children, className = '' }: SectionProps) { FILE: frontend/src/components/config/shared/TestButton.tsx type TestButtonProps (line 1) | interface TestButtonProps { function TestButton (line 7) | function TestButton({ onClick, label, className = '' }: TestButtonProps) { FILE: frontend/src/components/config/shared/constants.ts constant ENV_FIELD_LABELS (line 1) | const ENV_FIELD_LABELS: Record = { FILE: frontend/src/components/config/tabs/AdvancedTab.tsx constant SUBTABS (line 10) | const SUBTABS: { id: AdvancedSubtab; label: string }[] = [ function AdvancedTab (line 18) | function AdvancedTab() { FILE: frontend/src/components/config/tabs/DefaultTab.tsx function DefaultTab (line 6) | function DefaultTab() { function GroqHelpBox (line 127) | function GroqHelpBox() { function GroqPricingBox (line 164) | function GroqPricingBox() { FILE: frontend/src/components/config/tabs/DiscordTab.tsx function DiscordTab (line 7) | function DiscordTab() { function StatusIndicator (line 216) | function StatusIndicator({ enabled }: { enabled: boolean }) { function SetupInstructions (line 231) | function SetupInstructions() { FILE: frontend/src/components/config/tabs/UserManagementTab.tsx function UserManagementTab (line 11) | function UserManagementTab() { type AccountSecurityProps (line 64) | interface AccountSecurityProps { function AccountSecuritySection (line 69) | function AccountSecuritySection({ changePassword, refreshUser }: Account... type UserLimitSectionProps (line 151) | interface UserLimitSectionProps { function UserLimitSection (line 160) | function UserLimitSection({ currentUsers, userLimit, onChangeLimit, onSa... type UserManagementProps (line 200) | interface UserManagementProps { function UserManagementSection (line 209) | function UserManagementSection({ currentUser, refreshUser, logout, manag... function getErrorMessage (line 521) | function getErrorMessage(error: unknown, fallback = 'Request failed.') { FILE: frontend/src/contexts/AudioPlayerContext.tsx type AudioPlayerState (line 5) | interface AudioPlayerState { type AudioPlayerContextType (line 15) | interface AudioPlayerContextType extends AudioPlayerState { type AudioPlayerAction (line 23) | type AudioPlayerAction = function audioPlayerReducer (line 42) | function audioPlayerReducer(state: AudioPlayerState, action: AudioPlayer... function AudioPlayerProvider (line 65) | function AudioPlayerProvider({ children }: { children: React.ReactNode }) { function useAudioPlayer (line 276) | function useAudioPlayer() { FILE: frontend/src/contexts/AuthContext.tsx type AuthStatus (line 6) | type AuthStatus = 'loading' | 'ready'; type AuthContextValue (line 8) | interface AuthContextValue { type InternalState (line 22) | interface InternalState { function AuthProvider (line 29) | function AuthProvider({ children }: { children: ReactNode }) { FILE: frontend/src/contexts/DiagnosticsContext.tsx type DiagnosticsContextValue (line 6) | type DiagnosticsContextValue = { function DiagnosticsProvider (line 30) | function DiagnosticsProvider({ children }: { children: ReactNode }) { FILE: frontend/src/hooks/useConfigState.ts constant DEFAULT_ENV_HINTS (line 14) | const DEFAULT_ENV_HINTS: Record = { type ConnectionStatus (line 52) | interface ConnectionStatus { type UseConfigStateReturn (line 58) | interface UseConfigStateReturn { function useConfigState (line 105) | function useConfigState(): UseConfigStateReturn { FILE: frontend/src/hooks/useEpisodeStatus.ts function useEpisodeStatus (line 5) | function useEpisodeStatus(episodeGuid: string, isWhitelisted: boolean, h... FILE: frontend/src/pages/BillingPage.tsx function BillingPage (line 8) | function BillingPage() { FILE: frontend/src/pages/ConfigPage.tsx function ConfigPage (line 3) | function ConfigPage() { FILE: frontend/src/pages/HomePage.tsx function HomePage (line 15) | function HomePage() { FILE: frontend/src/pages/JobsPage.tsx function getStatusColor (line 5) | function getStatusColor(status: string) { function StatusBadge (line 24) | function StatusBadge({ status }: { status: string }) { function ProgressBar (line 33) | function ProgressBar({ value }: { value: number }) { function RunStat (line 45) | function RunStat({ label, value }: { label: string; value: number }) { function formatDateTime (line 54) | function formatDateTime(value: string | null): string { function JobsPage (line 66) | function JobsPage() { FILE: frontend/src/pages/LandingPage.tsx function LandingPage (line 5) | function LandingPage() { FILE: frontend/src/pages/LoginPage.tsx function LoginPage (line 8) | function LoginPage() { FILE: frontend/src/services/api.ts constant API_BASE_URL (line 20) | const API_BASE_URL = ''; FILE: frontend/src/types/index.ts type Feed (line 1) | interface Feed { type Episode (line 15) | interface Episode { type PagedResult (line 30) | interface PagedResult { type Job (line 39) | interface Job { type JobManagerRun (line 56) | interface JobManagerRun { type JobManagerStatus (line 74) | interface JobManagerStatus { type CleanupPreview (line 78) | interface CleanupPreview { type CleanupRunResult (line 84) | interface CleanupRunResult { type LLMConfig (line 95) | interface LLMConfig { type WhisperConfig (line 111) | type WhisperConfig = type ProcessingConfigUI (line 133) | interface ProcessingConfigUI { type OutputConfigUI (line 137) | interface OutputConfigUI { type AppConfigUI (line 145) | interface AppConfigUI { type CombinedConfig (line 155) | interface CombinedConfig { type EnvOverrideEntry (line 163) | interface EnvOverrideEntry { type EnvOverrideMap (line 170) | type EnvOverrideMap = Record; type ConfigResponse (line 172) | interface ConfigResponse { type PodcastSearchResult (line 177) | interface PodcastSearchResult { type AuthUser (line 186) | interface AuthUser { type ManagedUser (line 195) | interface ManagedUser extends AuthUser { type DiscordStatus (line 201) | interface DiscordStatus { type BillingSummary (line 205) | interface BillingSummary { type LandingStatus (line 218) | interface LandingStatus { FILE: frontend/src/utils/clipboard.ts function copyToClipboard (line 3) | async function copyToClipboard(text: string, promptMessage: string = 'Co... FILE: frontend/src/utils/diagnostics.ts type DiagnosticsLevel (line 1) | type DiagnosticsLevel = 'debug' | 'info' | 'warn' | 'error'; type DiagnosticsEntry (line 3) | type DiagnosticsEntry = { type DiagnosticsState (line 10) | type DiagnosticsState = { type DiagnosticErrorPayload (line 15) | type DiagnosticErrorPayload = { constant STORAGE_KEY (line 22) | const STORAGE_KEY = 'podly.diagnostics.v1'; constant MAX_ENTRIES (line 23) | const MAX_ENTRIES = 200; constant MAX_ENTRY_MESSAGE_CHARS (line 24) | const MAX_ENTRY_MESSAGE_CHARS = 500; constant MAX_JSON_CHARS (line 25) | const MAX_JSON_CHARS = 120_000; constant SENSITIVE_KEY_RE (line 27) | const SENSITIVE_KEY_RE = /(authorization|cookie|set-cookie|token|access[... constant SENSITIVE_VALUE_REPLACEMENT (line 28) | const SENSITIVE_VALUE_REPLACEMENT = '[REDACTED]'; constant DIAGNOSTIC_UPDATED_EVENT (line 113) | const DIAGNOSTIC_UPDATED_EVENT = 'podly:diagnostic-updated'; constant DIAGNOSTIC_ERROR_EVENT (line 153) | const DIAGNOSTIC_ERROR_EVENT = 'podly:diagnostic-error'; FILE: frontend/src/utils/httpError.ts type ApiErrorData (line 3) | type ApiErrorData = { type HttpErrorInfo (line 9) | type HttpErrorInfo = { FILE: frontend/vite.config.ts constant BACKEND_TARGET (line 7) | const BACKEND_TARGET = 'http://localhost:5001' FILE: scripts/test_full_workflow.py function log (line 10) | def log(msg): function check_health (line 14) | def check_health(): function add_feed (line 27) | def add_feed(url): function get_feeds (line 43) | def get_feeds(): function get_posts (line 55) | def get_posts(feed_id): function whitelist_post (line 67) | def whitelist_post(guid): function check_status (line 89) | def check_status(guid): function wait_for_processing (line 96) | def wait_for_processing(guid, timeout=300): function main (line 123) | def main(): FILE: src/app/__init__.py function _env_bool (line 43) | def _env_bool(name: str, default: bool = False) -> bool: function _get_sqlite_busy_timeout_ms (line 50) | def _get_sqlite_busy_timeout_ms() -> int: function setup_dirs (line 55) | def setup_dirs() -> None: class SchedulerConfig (line 73) | class SchedulerConfig: function _set_sqlite_pragmas (line 85) | def _set_sqlite_pragmas(dbapi_connection: Any, connection_record: Any) -... function setup_scheduler (line 102) | def setup_scheduler(app: Flask) -> None: function create_app (line 109) | def create_app() -> Flask: function create_web_app (line 119) | def create_web_app() -> Flask: function create_writer_app (line 133) | def create_writer_app() -> Flask: function _create_configured_app (line 145) | def _create_configured_app( function _clear_scheduler_jobstore (line 192) | def _clear_scheduler_jobstore() -> None: function _validate_env_key_conflicts (line 234) | def _validate_env_key_conflicts() -> None: function _create_flask_app (line 260) | def _create_flask_app() -> Flask: function _load_auth_settings (line 265) | def _load_auth_settings() -> AuthSettings: function _apply_auth_settings (line 273) | def _apply_auth_settings(app: Flask, auth_settings: AuthSettings) -> None: function _configure_session (line 279) | def _configure_session(app: Flask, auth_settings: AuthSettings) -> None: function _configure_cors (line 303) | def _configure_cors(app: Flask) -> None: function _configure_scheduler (line 324) | def _configure_scheduler(app: Flask) -> None: function _configure_database (line 328) | def _configure_database(app: Flask) -> None: function _configure_external_loggers (line 350) | def _configure_external_loggers() -> None: function _configure_readonly_sessions (line 355) | def _configure_readonly_sessions(app: Flask) -> None: function _initialize_extensions (line 409) | def _initialize_extensions(app: Flask) -> None: function _register_routes_and_middleware (line 419) | def _register_routes_and_middleware(app: Flask) -> None: function _register_api_logging (line 426) | def _register_api_logging(app: Flask) -> None: function _run_app_startup (line 455) | def _run_app_startup(auth_settings: AuthSettings) -> None: function _hydrate_web_config (line 466) | def _hydrate_web_config() -> None: function _start_scheduler_and_jobs (line 473) | def _start_scheduler_and_jobs(app: Flask) -> None: FILE: src/app/auth/bootstrap.py function bootstrap_admin_user (line 17) | def bootstrap_admin_user(auth_settings: AuthSettings) -> None: FILE: src/app/auth/discord_service.py class DiscordAuthError (line 23) | class DiscordAuthError(Exception): class DiscordGuildRequirementError (line 27) | class DiscordGuildRequirementError(DiscordAuthError): class DiscordRegistrationDisabledError (line 31) | class DiscordRegistrationDisabledError(DiscordAuthError): class DiscordUser (line 36) | class DiscordUser: function generate_oauth_state (line 41) | def generate_oauth_state() -> str: function build_authorization_url (line 46) | def build_authorization_url( function exchange_code_for_token (line 66) | def exchange_code_for_token(settings: DiscordSettings, code: str) -> dic... function get_discord_user (line 85) | def get_discord_user(access_token: str) -> DiscordUser: function check_guild_membership (line 100) | def check_guild_membership(access_token: str, settings: DiscordSettings)... function find_or_create_user_from_discord (line 116) | def find_or_create_user_from_discord( FILE: src/app/auth/discord_settings.py class DiscordSettings (line 14) | class DiscordSettings: function load_discord_settings (line 23) | def load_discord_settings() -> DiscordSettings: function _load_from_database (line 72) | def _load_from_database() -> "DiscordSettingsModel | None": function reload_discord_settings (line 84) | def reload_discord_settings(app: "Flask") -> DiscordSettings: FILE: src/app/auth/feed_tokens.py function _hash_token (line 17) | def _hash_token(secret_value: str) -> str: class FeedTokenAuthResult (line 22) | class FeedTokenAuthResult: function _validate_token_access (line 28) | def _validate_token_access(token: FeedAccessToken, user: User, path: str... function create_feed_access_token (line 56) | def create_feed_access_token(user: User, feed: Feed | None) -> tuple[str... function authenticate_feed_token (line 68) | def authenticate_feed_token( function _verify_subscription (line 102) | def _verify_subscription(user: User, feed_id: int) -> bool: function _resolve_user_id_from_feed_path (line 120) | def _resolve_user_id_from_feed_path(path: str) -> Optional[int]: function _resolve_feed_id (line 130) | def _resolve_feed_id(path: str) -> Optional[int]: FILE: src/app/auth/guards.py function require_admin (line 14) | def require_admin( function is_auth_enabled (line 56) | def is_auth_enabled() -> bool: FILE: src/app/auth/middleware.py function init_auth_middleware (line 64) | def init_auth_middleware(app: Any) -> None: function _load_session_user (line 110) | def _load_session_user() -> AuthenticatedUser | None: function _is_token_protected_endpoint (line 127) | def _is_token_protected_endpoint(path: str) -> bool: function _authenticate_feed_token_from_query (line 131) | def _authenticate_feed_token_from_query() -> FeedTokenAuthResult | None: function _is_public_request (line 140) | def _is_public_request(path: str) -> bool: function _json_unauthorized (line 153) | def _json_unauthorized(message: str = "Authentication required.") -> Res... function _token_unauthorized (line 159) | def _token_unauthorized() -> Response: function _too_many_requests (line 164) | def _too_many_requests(retry_after: int) -> Response: FILE: src/app/auth/passwords.py function hash_password (line 6) | def hash_password(password: str, *, rounds: int = 12) -> str: function verify_password (line 13) | def verify_password(password: str, password_hash: str) -> bool: FILE: src/app/auth/rate_limiter.py class FailureState (line 9) | class FailureState: class FailureRateLimiter (line 15) | class FailureRateLimiter: method __init__ (line 18) | def __init__( method register_failure (line 29) | def register_failure(self, key: str) -> int: method register_success (line 51) | def register_success(self, key: str) -> None: method retry_after (line 55) | def retry_after(self, key: str) -> int | None: method _prune_stale (line 72) | def _prune_stale(self, now: datetime) -> None: FILE: src/app/auth/service.py class AuthServiceError (line 15) | class AuthServiceError(Exception): class InvalidCredentialsError (line 19) | class InvalidCredentialsError(AuthServiceError): class PasswordValidationError (line 23) | class PasswordValidationError(AuthServiceError): class DuplicateUserError (line 27) | class DuplicateUserError(AuthServiceError): class LastAdminRemovalError (line 31) | class LastAdminRemovalError(AuthServiceError): class UserLimitExceededError (line 35) | class UserLimitExceededError(AuthServiceError): class AuthenticatedUser (line 43) | class AuthenticatedUser: function _normalize_username (line 49) | def _normalize_username(username: str) -> str: function authenticate (line 53) | def authenticate(username: str, password: str) -> AuthenticatedUser | None: function list_users (line 62) | def list_users() -> Sequence[User]: function create_user (line 69) | def create_user(username: str, password: str, role: str = "user") -> User: function change_password (line 97) | def change_password(user: User, current_password: str, new_password: str... function update_password (line 104) | def update_password(user: User, new_password: str) -> None: function delete_user (line 115) | def delete_user(user: User) -> None: function set_role (line 124) | def set_role(user: User, role: str) -> None: function set_manual_feed_allowance (line 139) | def set_manual_feed_allowance(user: User, allowance: int | None) -> None: function update_user_last_active (line 150) | def update_user_last_active(user_id: int) -> None: function _count_admins (line 159) | def _count_admins() -> int: function _enforce_user_limit (line 163) | def _enforce_user_limit() -> None: FILE: src/app/auth/settings.py function _str_to_bool (line 7) | def _str_to_bool(value: str | None, default: bool = False) -> bool: class AuthSettings (line 15) | class AuthSettings: method admin_password_required (line 23) | def admin_password_required(self) -> bool: method without_password (line 26) | def without_password(self) -> "AuthSettings": function load_auth_settings (line 31) | def load_auth_settings() -> AuthSettings: FILE: src/app/background.py function add_background_job (line 11) | def add_background_job(minutes: int) -> None: function schedule_cleanup_job (line 26) | def schedule_cleanup_job(retention_days: Optional[int]) -> None: FILE: src/app/config_store.py function _is_empty (line 35) | def _is_empty(value: Any) -> bool: function _parse_int (line 39) | def _parse_int(val: Any) -> Optional[int]: function _parse_bool (line 46) | def _parse_bool(val: Any) -> Optional[bool]: function _set_if_empty (line 57) | def _set_if_empty(obj: Any, attr: str, new_val: Any) -> bool: function _set_if_default (line 66) | def _set_if_default(obj: Any, attr: str, new_val: Any, default_val: Any)... function _ensure_row (line 75) | def _ensure_row(model: type, defaults: Dict[str, Any]) -> Any: function ensure_defaults (line 105) | def ensure_defaults() -> None: function _apply_llm_env_overrides_to_db (line 167) | def _apply_llm_env_overrides_to_db(llm: Any) -> bool: function _apply_whisper_env_overrides_to_db (line 267) | def _apply_whisper_env_overrides_to_db(whisper: Any) -> bool: function _apply_env_overrides_to_db_first_boot (line 376) | def _apply_env_overrides_to_db_first_boot() -> None: function read_combined (line 405) | def read_combined() -> Dict[str, Any]: function _update_section_llm (line 479) | def _update_section_llm(data: Dict[str, Any]) -> None: function _update_section_whisper (line 509) | def _update_section_whisper(data: Dict[str, Any]) -> None: function _update_section_processing (line 561) | def _update_section_processing(data: Dict[str, Any]) -> None: function _update_section_output (line 577) | def _update_section_output(data: Dict[str, Any]) -> None: function _update_section_app (line 596) | def _update_section_app(data: Dict[str, Any]) -> Tuple[Optional[int], Op... function _maybe_reschedule_refresh_job (line 621) | def _maybe_reschedule_refresh_job( function _maybe_disable_cleanup_job (line 649) | def _maybe_disable_cleanup_job( function update_combined (line 666) | def update_combined(payload: Dict[str, Any]) -> Dict[str, Any]: function to_pydantic_config (line 688) | def to_pydantic_config() -> PydanticConfig: function hydrate_runtime_config_inplace (line 803) | def hydrate_runtime_config_inplace(db_config: Optional[PydanticConfig] =... function _log_initial_snapshot (line 825) | def _log_initial_snapshot(cfg: PydanticConfig) -> None: function _apply_top_level_env_overrides (line 836) | def _apply_top_level_env_overrides(cfg: PydanticConfig) -> None: function _apply_whisper_env_overrides (line 850) | def _apply_whisper_env_overrides(cfg: PydanticConfig) -> None: function _apply_llm_model_override (line 885) | def _apply_llm_model_override(cfg: PydanticConfig) -> None: function _configure_local_whisper (line 891) | def _configure_local_whisper(cfg: PydanticConfig) -> None: function _configure_remote_whisper (line 918) | def _configure_remote_whisper(cfg: PydanticConfig) -> None: function _configure_groq_whisper (line 983) | def _configure_groq_whisper(cfg: PydanticConfig) -> None: function _apply_whisper_type_override (line 1022) | def _apply_whisper_type_override(cfg: PydanticConfig) -> None: function _commit_runtime_config (line 1054) | def _commit_runtime_config(cfg: PydanticConfig) -> None: function _log_final_snapshot (line 1068) | def _log_final_snapshot() -> None: function ensure_defaults_and_hydrate (line 1077) | def ensure_defaults_and_hydrate() -> None: function _calculate_env_hash (line 1088) | def _calculate_env_hash() -> str: function _check_and_apply_env_changes (line 1131) | def _check_and_apply_env_changes() -> None: function _apply_llm_env_overrides (line 1164) | def _apply_llm_env_overrides(llm: LLMSettings) -> bool: function _apply_whisper_remote_overrides (line 1240) | def _apply_whisper_remote_overrides(whisper: WhisperSettings) -> bool: function _apply_whisper_groq_overrides (line 1274) | def _apply_whisper_groq_overrides(whisper: WhisperSettings) -> bool: function _apply_whisper_env_overrides_force (line 1291) | def _apply_whisper_env_overrides_force(whisper: WhisperSettings) -> bool: function _apply_env_overrides_to_db_force (line 1325) | def _apply_env_overrides_to_db_force() -> None: FILE: src/app/db_commit.py function safe_commit (line 7) | def safe_commit( FILE: src/app/db_guard.py function reset_session (line 15) | def reset_session( function db_guard (line 48) | def db_guard( FILE: src/app/feeds.py function is_feed_active_for_user (line 21) | def is_feed_active_for_user(feed_id: int, user: User) -> bool: function _should_auto_whitelist_new_posts (line 47) | def _should_auto_whitelist_new_posts(feed: Feed, post: Optional[Post] = ... function _get_base_url (line 82) | def _get_base_url() -> str: function fetch_feed (line 121) | def fetch_feed(url: str) -> feedparser.FeedParserDict: function refresh_feed (line 129) | def refresh_feed(feed: Feed) -> None: function add_or_refresh_feed (line 190) | def add_or_refresh_feed(url: str) -> Feed: function add_feed (line 204) | def add_feed(feed_data: feedparser.FeedParserDict) -> Feed: class ItunesRSSItem (line 267) | class ItunesRSSItem(PyRSS2Gen.RSSItem): # type: ignore[misc] method __init__ (line 268) | def __init__( method publish_extensions (line 289) | def publish_extensions(self, handler: Any) -> None: function feed_item (line 296) | def feed_item(post: Post, prepend_feed_title: bool = False) -> PyRSS2Gen... function generate_feed_xml (line 332) | def generate_feed_xml(feed: Feed) -> Any: function generate_aggregate_feed_xml (line 373) | def generate_aggregate_feed_xml(user: Optional[User]) -> Any: function get_user_aggregate_posts (line 416) | def get_user_aggregate_posts(user_id: int, limit_per_feed: int = 3) -> l... function _append_feed_token_params (line 445) | def _append_feed_token_params(url: str) -> str: function make_post (line 471) | def make_post(feed: Feed, entry: feedparser.FeedParserDict) -> Post: function _get_entry_field (line 519) | def _get_entry_field(entry: feedparser.FeedParserDict, field: str) -> Op... function _parse_datetime_string (line 524) | def _parse_datetime_string( function _parse_struct_time (line 536) | def _parse_struct_time(value: Optional[Any], field: str) -> Optional[dat... function _normalize_to_utc (line 550) | def _normalize_to_utc(dt: Optional[datetime.datetime]) -> Optional[datet... function _parse_release_date (line 558) | def _parse_release_date( function _format_pub_date (line 577) | def _format_pub_date(release_date: Optional[datetime.datetime]) -> Optio... function get_guid (line 589) | def get_guid(entry: feedparser.FeedParserDict) -> str: function get_duration (line 598) | def get_duration(entry: feedparser.FeedParserDict) -> Optional[int]: FILE: src/app/ipc.py class QueueManager (line 8) | class QueueManager(BaseManager): function _get_default_authkey (line 16) | def _get_default_authkey() -> bytes: function _ensure_process_authkey (line 24) | def _ensure_process_authkey(authkey: bytes) -> None: function get_queue (line 33) | def get_queue() -> Queue[Any]: function make_server_manager (line 37) | def make_server_manager( function make_client_manager (line 51) | def make_client_manager( FILE: src/app/job_manager.py class JobManager (line 10) | class JobManager: method __init__ (line 15) | def __init__( method job_id (line 34) | def job_id(self) -> Optional[str]: method _reload_job (line 37) | def _reload_job(self) -> Optional[ProcessingJob]: method get_active_job (line 45) | def get_active_job(self) -> Optional[ProcessingJob]: method ensure_job (line 51) | def ensure_job(self) -> ProcessingJob: method fail (line 80) | def fail(self, message: str, step: int = 0, progress: float = 0.0) -> ... method complete (line 87) | def complete(self, message: str = "Processing complete") -> Processing... method skip (line 95) | def skip( method _load_and_validate_post (line 111) | def _load_and_validate_post( method _mark_job_skipped (line 179) | def _mark_job_skipped(self, reason: str) -> Optional[ProcessingJob]: method start_processing (line 201) | def start_processing(self, priority: str) -> Dict[str, Any]: FILE: src/app/jobs_manager.py class JobsManager (line 23) | class JobsManager: method __init__ (line 34) | def __init__(self) -> None: method _set_run_id (line 65) | def _set_run_id(self, run_id: Optional[str]) -> None: method _get_run_id (line 69) | def _get_run_id(self) -> Optional[str]: method _wake_worker (line 73) | def _wake_worker(self) -> None: method _wait_for_work (line 76) | def _wait_for_work(self, timeout: float = 5.0) -> None: method start_post_processing (line 82) | def start_post_processing( method enqueue_pending_jobs (line 118) | def enqueue_pending_jobs( method _ensure_jobs_for_all_posts (line 155) | def _ensure_jobs_for_all_posts(self, run_id: Optional[str]) -> int: method get_post_status (line 175) | def get_post_status(self, post_guid: str) -> Dict[str, Any]: method get_job_status (line 236) | def get_job_status(self, job_id: str) -> Dict[str, Any]: method list_active_jobs (line 260) | def list_active_jobs(self, limit: int = 100) -> List[Dict[str, Any]]: method list_all_jobs_detailed (line 307) | def list_all_jobs_detailed(self, limit: int = 200) -> List[Dict[str, A... method cancel_job (line 353) | def cancel_job(self, job_id: str) -> Dict[str, Any]: method cancel_post_jobs (line 379) | def cancel_post_jobs(self, post_guid: str) -> Dict[str, Any]: method cleanup_stale_jobs (line 399) | def cleanup_stale_jobs(self, older_than: timedelta) -> int: method cleanup_stuck_pending_jobs (line 413) | def cleanup_stuck_pending_jobs(self, stuck_threshold_minutes: int = 10... method clear_all_jobs (line 441) | def clear_all_jobs(self) -> Dict[str, Any]: method start_refresh_all_feeds (line 459) | def start_refresh_all_feeds( method _cleanup_inconsistent_posts (line 479) | def _cleanup_inconsistent_posts(self) -> None: method _cleanup_and_process_new_posts (line 489) | def _cleanup_and_process_new_posts( method _dequeue_next_job (line 525) | def _dequeue_next_job(self) -> Optional[Tuple[str, str]]: method _worker_loop (line 552) | def _worker_loop(self) -> None: method _process_job (line 578) | def _process_job(self, job_id: str, post_guid: str) -> None: function get_jobs_manager (line 690) | def get_jobs_manager() -> JobsManager: function scheduled_refresh_all_feeds (line 696) | def scheduled_refresh_all_feeds() -> None: FILE: src/app/jobs_manager_run_service.py function _session_get (line 18) | def _session_get(session: Any, ident: str) -> Optional[JobsManagerRun]: function _build_context_payload (line 32) | def _build_context_payload( function get_or_create_singleton_run (line 43) | def get_or_create_singleton_run( function ensure_active_run (line 75) | def ensure_active_run( function get_active_run (line 82) | def get_active_run(session: Any) -> Optional[JobsManagerRun]: function recalculate_run_counts (line 87) | def recalculate_run_counts(session: Any) -> Optional[JobsManagerRun]: function serialize_run (line 160) | def serialize_run(run: JobsManagerRun) -> Dict[str, object]: function build_run_status_snapshot (line 191) | def build_run_status_snapshot(session: Any) -> Optional[Dict[str, object]]: FILE: src/app/logger.py class ExtraFormatter (line 6) | class ExtraFormatter(logging.Formatter): method format (line 39) | def format(self, record: logging.LogRecord) -> str: function setup_logger (line 53) | def setup_logger( FILE: src/app/models.py function generate_uuid (line 12) | def generate_uuid() -> str: function generate_job_id (line 17) | def generate_job_id() -> str: class Feed (line 23) | class Feed(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 44) | def __repr__(self) -> str: class FeedAccessToken (line 48) | class FeedAccessToken(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 66) | def __repr__(self) -> str: class Post (line 73) | class Post(db.Model): # type: ignore[name-defined, misc] method audio_len_bytes (line 103) | def audio_len_bytes(self) -> int: class TranscriptSegment (line 113) | class TranscriptSegment(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 135) | def __repr__(self) -> str: class User (line 139) | class User(db.Model): # type: ignore[name-defined, misc] method _normalize_username (line 170) | def _normalize_username(self, key: str, value: str) -> str: method set_password (line 174) | def set_password(self, password: str) -> None: method verify_password (line 177) | def verify_password(self, password: str) -> bool: method __repr__ (line 180) | def __repr__(self) -> str: class ModelCall (line 184) | class ModelCall(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 216) | def __repr__(self) -> str: class Identification (line 220) | class Identification(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 242) | def __repr__(self) -> str: class JobsManagerRun (line 250) | class JobsManagerRun(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 275) | def __repr__(self) -> str: class ProcessingJob (line 282) | class ProcessingJob(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 324) | def __repr__(self) -> str: class UserFeed (line 328) | class UserFeed(db.Model): # type: ignore[name-defined, misc] method __repr__ (line 343) | def __repr__(self) -> str: class LLMSettings (line 350) | class LLMSettings(db.Model): # type: ignore[name-defined, misc] class WhisperSettings (line 387) | class WhisperSettings(db.Model): # type: ignore[name-defined, misc] class ProcessingSettings (line 432) | class ProcessingSettings(db.Model): # type: ignore[name-defined, misc] class OutputSettings (line 453) | class OutputSettings(db.Model): # type: ignore[name-defined, misc] class AppSettings (line 476) | class AppSettings(db.Model): # type: ignore[name-defined, misc] class DiscordSettings (line 518) | class DiscordSettings(db.Model): # type: ignore[name-defined, misc] FILE: src/app/post_cleanup.py function _build_cleanup_query (line 23) | def _build_cleanup_query( function count_cleanup_candidates (line 46) | def count_cleanup_candidates( function cleanup_processed_posts (line 64) | def cleanup_processed_posts(retention_days: Optional[int]) -> int: function scheduled_cleanup_processed_posts (line 119) | def scheduled_cleanup_processed_posts() -> None: function _remove_associated_files (line 138) | def _remove_associated_files(post: Post) -> None: function _load_latest_completed_map (line 157) | def _load_latest_completed_map( function _processed_timestamp_before_cutoff (line 175) | def _processed_timestamp_before_cutoff( function _get_processed_file_timestamp (line 190) | def _get_processed_file_timestamp(post: Post) -> Optional[datetime]: FILE: src/app/posts.py function _collect_processed_paths (line 12) | def _collect_processed_paths(post: Post) -> List[Path]: function _dedupe_and_find_existing (line 52) | def _dedupe_and_find_existing(paths: List[Path]) -> tuple[List[Path], Op... function _remove_file_if_exists (line 71) | def _remove_file_if_exists(path: Optional[Path], file_type: str, post_id... function remove_associated_files (line 88) | def remove_associated_files(post: Post) -> None: function clear_post_processing_data (line 135) | def clear_post_processing_data(post: Post) -> None: class PostException (line 166) | class PostException(Exception): FILE: src/app/processor.py class ProcessorSingleton (line 5) | class ProcessorSingleton: method get_instance (line 11) | def get_instance(cls) -> PodcastProcessor: method reset_instance (line 18) | def reset_instance(cls) -> None: function get_processor (line 23) | def get_processor() -> PodcastProcessor: FILE: src/app/routes/__init__.py function register_routes (line 13) | def register_routes(app: Flask) -> None: FILE: src/app/routes/auth_routes.py function _auth_enabled (line 40) | def _auth_enabled() -> bool: function auth_status (line 46) | def auth_status() -> Response: function login (line 54) | def login() -> RouteResult: function logout (line 112) | def logout() -> RouteResult: function auth_me (line 125) | def auth_me() -> RouteResult: function change_password_route (line 154) | def change_password_route() -> RouteResult: function list_users_route (line 186) | def list_users_route() -> RouteResult: function create_user_route (line 221) | def create_user_route() -> RouteResult: function update_user_route (line 267) | def update_user_route(username: str) -> RouteResult: function delete_user_route (line 301) | def delete_user_route(username: str) -> RouteResult: function _require_authenticated_user (line 323) | def _require_authenticated_user() -> User | None: function _unauthorized_response (line 334) | def _unauthorized_response() -> RouteResult: FILE: src/app/routes/billing_routes.py function _get_stripe_client (line 18) | def _get_stripe_client() -> tuple[Optional[Any], Optional[str]]: function _product_id (line 30) | def _product_id() -> Optional[str]: function _min_subscription_amount_cents (line 34) | def _min_subscription_amount_cents() -> int: function _user_feed_usage (line 55) | def _user_feed_usage(user: User) -> dict[str, int]: function billing_summary (line 69) | def billing_summary() -> Any: function _build_return_urls (line 144) | def _build_return_urls() -> tuple[str, str]: function update_subscription (line 152) | def update_subscription() -> Any: # pylint: disable=too-many-statements function billing_portal_session (line 367) | def billing_portal_session() -> Any: function _update_user_from_subscription (line 400) | def _update_user_from_subscription(sub: Any) -> None: function stripe_webhook (line 428) | def stripe_webhook() -> Any: FILE: src/app/routes/config_routes.py function _mask_secret (line 24) | def _mask_secret(value: Any | None) -> str | None: function _sanitize_config_for_client (line 39) | def _sanitize_config_for_client(cfg: Dict[str, Any]) -> Dict[str, Any]: function api_get_config (line 61) | def api_get_config() -> flask.Response: function _hydrate_runtime_config (line 86) | def _hydrate_runtime_config(data: Dict[str, Any]) -> None: function _hydrate_llm_config (line 92) | def _hydrate_llm_config(data: Dict[str, Any]) -> None: function _hydrate_whisper_config (line 129) | def _hydrate_whisper_config(data: Dict[str, Any]) -> None: function _overlay_whisper_dict (line 142) | def _overlay_whisper_dict(target: Dict[str, Any], source: Dict[str, Any]... function _overlay_whisper_object (line 153) | def _overlay_whisper_object(target: Dict[str, Any], source: Any) -> None: function _overlay_remote_whisper_fields (line 164) | def _overlay_remote_whisper_fields(target: Dict[str, Any], source: Any) ... function _overlay_groq_whisper_fields (line 177) | def _overlay_groq_whisper_fields(target: Dict[str, Any], source: Any) ->... function _get_attr_or_value (line 186) | def _get_attr_or_value(source: Any, key: str, default: Any) -> Any: function _hydrate_app_config (line 192) | def _hydrate_app_config(data: Dict[str, Any]) -> None: function _first_env (line 215) | def _first_env(env_names: list[str]) -> tuple[str | None, str | None]: function _register_override (line 224) | def _register_override( function _register_llm_overrides (line 244) | def _register_llm_overrides(overrides: Dict[str, Any]) -> None: function _register_groq_shared_overrides (line 260) | def _register_groq_shared_overrides(overrides: Dict[str, Any]) -> None: function _register_remote_whisper_overrides (line 269) | def _register_remote_whisper_overrides(overrides: Dict[str, Any]) -> None: function _register_groq_whisper_overrides (line 304) | def _register_groq_whisper_overrides(overrides: Dict[str, Any]) -> None: function _register_local_whisper_overrides (line 324) | def _register_local_whisper_overrides(overrides: Dict[str, Any]) -> None: function _determine_whisper_type_for_metadata (line 333) | def _determine_whisper_type_for_metadata(data: Dict[str, Any]) -> str | ... function _build_env_override_metadata (line 354) | def _build_env_override_metadata(data: Dict[str, Any]) -> Dict[str, Any]: function api_put_config (line 379) | def api_put_config() -> flask.Response: function api_test_llm (line 428) | def api_test_llm() -> flask.Response: function _make_error_response (line 498) | def _make_error_response(error_msg: str, status_code: int = 400) -> flas... function _make_success_response (line 502) | def _make_success_response(message: str, **extra_data: Any) -> flask.Res... function _get_whisper_config_value (line 508) | def _get_whisper_config_value( function _get_env_whisper_api_key (line 523) | def _get_env_whisper_api_key(whisper_type: str) -> str | None: function _determine_whisper_type (line 533) | def _determine_whisper_type(whisper_cfg: Dict[str, Any]) -> str | None: function _test_local_whisper (line 547) | def _test_local_whisper(whisper_cfg: Dict[str, Any]) -> flask.Response: function _test_remote_whisper (line 575) | def _test_remote_whisper(whisper_cfg: Dict[str, Any]) -> flask.Response: function _test_groq_whisper (line 597) | def _test_groq_whisper(whisper_cfg: Dict[str, Any]) -> flask.Response: function api_test_whisper (line 615) | def api_test_whisper() -> flask.Response: function api_get_whisper_capabilities (line 643) | def api_get_whisper_capabilities() -> flask.Response: function api_configured_check (line 671) | def api_configured_check() -> flask.Response: FILE: src/app/routes/discord_routes.py function _get_discord_settings (line 42) | def _get_discord_settings() -> DiscordSettings | None: function _mask_secret (line 46) | def _mask_secret(value: str | None) -> str | None: function _has_env_override (line 55) | def _has_env_override(env_var: str) -> bool: function discord_status (line 61) | def discord_status() -> Response: function discord_config_get (line 72) | def discord_config_get() -> Response | tuple[Response, int]: function discord_config_put (line 127) | def discord_config_put() -> Response | tuple[Response, int]: function discord_login (line 195) | def discord_login() -> Response | tuple[Response, int]: function discord_callback (line 211) | def discord_callback() -> Response: FILE: src/app/routes/feed_routes.py function fix_url (line 58) | def fix_url(url: str) -> str: function _user_feed_count (line 65) | def _user_feed_count(user_id: int) -> int: function _get_latest_post (line 69) | def _get_latest_post(feed: Feed) -> Post | None: function _ensure_user_feed_membership (line 78) | def _ensure_user_feed_membership(feed: Feed, user_id: int | None) -> tup... function _whitelist_latest_for_first_member (line 92) | def _whitelist_latest_for_first_member( function _handle_developer_mode_feed (line 121) | def _handle_developer_mode_feed(url: str, user: Optional[User]) -> Respo... function _check_feed_allowance (line 159) | def _check_feed_allowance(user: User, url: str) -> Optional[ResponseRetu... function add_feed (line 193) | def add_feed() -> ResponseReturnValue: function create_feed_share_link (line 242) | def create_feed_share_link(feed_id: int) -> ResponseReturnValue: function search_feeds (line 287) | def search_feeds() -> ResponseReturnValue: function get_feed (line 370) | def get_feed(f_id: int) -> Response: function delete_feed (line 388) | def delete_feed(f_id: int) -> ResponseReturnValue: # pylint: disable=to... function refresh_feed_endpoint (line 443) | def refresh_feed_endpoint(f_id: int) -> ResponseReturnValue: function update_feed_settings_endpoint (line 473) | def update_feed_settings_endpoint(feed_id: int) -> ResponseReturnValue: function _refresh_feed_background (line 511) | def _refresh_feed_background(app: Flask, feed_id: int) -> None: function refresh_all_feeds_endpoint (line 528) | def refresh_all_feeds_endpoint() -> Response: function _enqueue_pending_jobs_async (line 544) | def _enqueue_pending_jobs_async(app: Flask) -> None: function _cleanup_feed_directories (line 552) | def _cleanup_feed_directories(feed: Feed) -> None: function get_feed_by_alt_or_url (line 607) | def get_feed_by_alt_or_url(something_or_rss: str) -> Response: function api_feeds (line 627) | def api_feeds() -> ResponseReturnValue: function api_join_feed (line 655) | def api_join_feed(feed_id: int) -> ResponseReturnValue: function api_exit_feed (line 703) | def api_exit_feed(feed_id: int) -> ResponseReturnValue: function api_leave_feed (line 724) | def api_leave_feed(feed_id: int) -> ResponseReturnValue: function get_user_aggregate_feed (line 742) | def get_user_aggregate_feed(user_id: int) -> Response: function get_aggregate_feed_redirect (line 772) | def get_aggregate_feed_redirect() -> ResponseReturnValue: function create_aggregate_feed_link (line 803) | def create_aggregate_feed_link() -> ResponseReturnValue: function _require_user_or_error (line 886) | def _require_user_or_error( function _serialize_feed (line 906) | def _serialize_feed( FILE: src/app/routes/jobs_routes.py function api_list_active_jobs (line 20) | def api_list_active_jobs() -> ResponseReturnValue: function api_list_all_jobs (line 30) | def api_list_all_jobs() -> ResponseReturnValue: function api_job_manager_status (line 40) | def api_job_manager_status() -> ResponseReturnValue: function api_cancel_job (line 46) | def api_cancel_job(job_id: str) -> ResponseReturnValue: function api_cleanup_preview (line 73) | def api_cleanup_preview() -> ResponseReturnValue: function api_run_cleanup (line 86) | def api_run_cleanup() -> ResponseReturnValue: FILE: src/app/routes/main_routes.py function index (line 22) | def index() -> flask.Response: function landing_status (line 35) | def landing_status() -> flask.Response: function catch_all (line 83) | def catch_all(path: str) -> flask.Response: function whitelist_all (line 105) | def whitelist_all(f_id: str, val: str) -> flask.Response: function set_whitelist (line 136) | def set_whitelist(p_guid: str, val: str) -> flask.Response: FILE: src/app/routes/post_routes.py function _is_latest_post (line 37) | def _is_latest_post(feed: Feed, post: Post) -> bool: function _increment_download_count (line 47) | def _increment_download_count(post: Post) -> None: function _ensure_whitelisted_for_download (line 57) | def _ensure_whitelisted_for_download( function _missing_processed_audio_response (line 86) | def _missing_processed_audio_response(post: Post, p_guid: str) -> flask.... function api_feed_posts (line 122) | def api_feed_posts(feed_id: int) -> flask.Response: function api_post_processing_estimate (line 199) | def api_post_processing_estimate(p_guid: str) -> ResponseReturnValue: function get_post_json (line 225) | def get_post_json(p_guid: str) -> flask.Response: function post_debug (line 295) | def post_debug(p_guid: str) -> flask.Response: function api_post_stats (line 346) | def api_post_stats(p_guid: str) -> flask.Response: function api_toggle_whitelist (line 499) | def api_toggle_whitelist(p_guid: str) -> ResponseReturnValue: function api_toggle_whitelist_all (line 567) | def api_toggle_whitelist_all(feed_id: int) -> ResponseReturnValue: function api_process_post (line 625) | def api_process_post(p_guid: str) -> ResponseReturnValue: function api_reprocess_post (line 707) | def api_reprocess_post(p_guid: str) -> ResponseReturnValue: function api_post_status (line 831) | def api_post_status(p_guid: str) -> ResponseReturnValue: function api_get_post_audio (line 843) | def api_get_post_audio(p_guid: str) -> ResponseReturnValue: function api_download_post (line 893) | def api_download_post(p_guid: str) -> flask.Response: function api_download_original_post (line 928) | def api_download_original_post(p_guid: str) -> flask.Response: function download_post_legacy (line 964) | def download_post_legacy(p_guid: str) -> flask.Response: function download_original_post_legacy (line 969) | def download_original_post_legacy(p_guid: str) -> flask.Response: FILE: src/app/routes/post_stats_utils.py function count_model_calls (line 6) | def count_model_calls( function parse_refined_windows (line 24) | def parse_refined_windows(raw_refined: Any) -> List[Tuple[float, float]]: function is_mixed_segment (line 50) | def is_mixed_segment( FILE: src/app/timeout_decorator.py class TimeoutException (line 8) | class TimeoutException(Exception): function timeout_decorator (line 12) | def timeout_decorator(timeout: int) -> Callable[[Callable[..., T]], Call... FILE: src/app/writer/actions/cleanup.py function cleanup_missing_audio_paths_action (line 18) | def cleanup_missing_audio_paths_action(params: Dict[str, Any]) -> int: function clear_post_processing_data_action (line 59) | def clear_post_processing_data_action(params: Dict[str, Any]) -> Dict[st... function cleanup_processed_post_action (line 109) | def cleanup_processed_post_action(params: Dict[str, Any]) -> Dict[str, A... FILE: src/app/writer/actions/feeds.py function refresh_feed_action (line 23) | def refresh_feed_action(params: Dict[str, Any]) -> Dict[str, Any]: function add_feed_action (line 67) | def add_feed_action(params: Dict[str, Any]) -> Dict[str, Any]: function update_feed_settings_action (line 109) | def update_feed_settings_action(params: Dict[str, Any]) -> Dict[str, Any]: function increment_download_count_action (line 127) | def increment_download_count_action(params: Dict[str, Any]) -> Dict[str,... function whitelist_post_action (line 140) | def whitelist_post_action(params: Dict[str, Any]) -> Dict[str, Any]: function ensure_user_feed_membership_action (line 151) | def ensure_user_feed_membership_action(params: Dict[str, Any]) -> Dict[s... function remove_user_feed_membership_action (line 170) | def remove_user_feed_membership_action(params: Dict[str, Any]) -> Dict[s... function whitelist_latest_post_for_feed_action (line 182) | def whitelist_latest_post_for_feed_action(params: Dict[str, Any]) -> Dic... function toggle_whitelist_all_for_feed_action (line 202) | def toggle_whitelist_all_for_feed_action(params: Dict[str, Any]) -> Dict... function create_dev_test_feed_action (line 215) | def create_dev_test_feed_action(params: Dict[str, Any]) -> Dict[str, Any]: function delete_feed_cascade_action (line 268) | def delete_feed_cascade_action(params: Dict[str, Any]) -> Dict[str, Any]: function _hash_token (line 343) | def _hash_token(secret_value: str) -> str: function create_feed_access_token_action (line 347) | def create_feed_access_token_action(params: Dict[str, Any]) -> Dict[str,... function touch_feed_access_token_action (line 389) | def touch_feed_access_token_action(params: Dict[str, Any]) -> Dict[str, ... FILE: src/app/writer/actions/jobs.py function dequeue_job_action (line 9) | def dequeue_job_action(params: Dict[str, Any]) -> Optional[Dict[str, Any]]: function cleanup_stale_jobs_action (line 38) | def cleanup_stale_jobs_action(params: Dict[str, Any]) -> Dict[str, Any]: function clear_all_jobs_action (line 51) | def clear_all_jobs_action(params: Dict[str, Any]) -> int: function create_job_action (line 59) | def create_job_action(params: Dict[str, Any]) -> Dict[str, Any]: function cancel_existing_jobs_action (line 78) | def cancel_existing_jobs_action(params: Dict[str, Any]) -> int: function update_job_status_action (line 101) | def update_job_status_action(params: Dict[str, Any]) -> Dict[str, Any]: function mark_cancelled_action (line 136) | def mark_cancelled_action(params: Dict[str, Any]) -> Dict[str, Any]: function reassign_pending_jobs_action (line 154) | def reassign_pending_jobs_action(params: Dict[str, Any]) -> int: FILE: src/app/writer/actions/processor.py function upsert_model_call_action (line 13) | def upsert_model_call_action(params: Dict[str, Any]) -> Dict[str, Any]: function upsert_whisper_model_call_action (line 75) | def upsert_whisper_model_call_action(params: Dict[str, Any]) -> Dict[str... function _normalize_segments_payload (line 137) | def _normalize_segments_payload( function replace_transcription_action (line 156) | def replace_transcription_action(params: Dict[str, Any]) -> Dict[str, Any]: function mark_model_call_failed_action (line 213) | def mark_model_call_failed_action(params: Dict[str, Any]) -> Dict[str, A... function insert_identifications_action (line 231) | def insert_identifications_action(params: Dict[str, Any]) -> Dict[str, A... function replace_identifications_action (line 258) | def replace_identifications_action(params: Dict[str, Any]) -> Dict[str, ... FILE: src/app/writer/actions/system.py function ensure_active_run_action (line 12) | def ensure_active_run_action(params: Dict[str, Any]) -> Dict[str, Any]: function update_discord_settings_action (line 34) | def update_discord_settings_action(params: Dict[str, Any]) -> Dict[str, ... function update_combined_config_action (line 55) | def update_combined_config_action(params: Dict[str, Any]) -> Dict[str, A... FILE: src/app/writer/actions/users.py function create_user_action (line 8) | def create_user_action(params: Dict[str, Any]) -> Dict[str, Any]: function update_user_password_action (line 29) | def update_user_password_action(params: Dict[str, Any]) -> Dict[str, Any]: function delete_user_action (line 46) | def delete_user_action(params: Dict[str, Any]) -> Dict[str, Any]: function set_user_role_action (line 69) | def set_user_role_action(params: Dict[str, Any]) -> Dict[str, Any]: function set_manual_feed_allowance_action (line 84) | def set_manual_feed_allowance_action(params: Dict[str, Any]) -> Dict[str... function upsert_discord_user_action (line 107) | def upsert_discord_user_action(params: Dict[str, Any]) -> Dict[str, Any]: function set_user_billing_fields_action (line 145) | def set_user_billing_fields_action(params: Dict[str, Any]) -> Dict[str, ... function set_user_billing_by_customer_id_action (line 167) | def set_user_billing_by_customer_id_action(params: Dict[str, Any]) -> Di... function update_user_last_active_action (line 187) | def update_user_last_active_action(params: Dict[str, Any]) -> Dict[str, ... FILE: src/app/writer/client.py class WriterClient (line 13) | class WriterClient: method __init__ (line 14) | def __init__(self) -> None: method connect (line 18) | def connect(self) -> None: method _should_use_local_fallback (line 23) | def _should_use_local_fallback(self) -> bool: method _local_execute (line 33) | def _local_execute(self, cmd: WriteCommand) -> WriteResult: method _local_execute_single (line 57) | def _local_execute_single( method _local_execute_transaction (line 64) | def _local_execute_transaction( method _local_execute_action (line 95) | def _local_execute_action(self, cmd: WriteCommand) -> WriteResult: method _local_execute_model (line 114) | def _local_execute_model( method submit (line 128) | def submit( method create (line 157) | def create( method update (line 165) | def update( method delete (line 174) | def delete(self, model: str, pk: Any, wait: bool = True) -> Optional[W... method action (line 183) | def action( FILE: src/app/writer/executor.py class CommandExecutor (line 15) | class CommandExecutor: method __init__ (line 16) | def __init__(self, app: Flask): method _register_default_actions (line 22) | def _register_default_actions(self) -> None: method _discover_models (line 141) | def _discover_models(self) -> Dict[str, Any]: method register_action (line 149) | def register_action(self, name: str, func: Callable[[Dict[str, Any]], ... method process_command (line 152) | def process_command(self, cmd: WriteCommand) -> WriteResult: method _execute_single_command (line 203) | def _execute_single_command(self, cmd: WriteCommand) -> WriteResult: method _handle_transaction (line 222) | def _handle_transaction(self, cmd: WriteCommand) -> WriteResult: method _handle_action (line 269) | def _handle_action(self, cmd: WriteCommand) -> WriteResult: FILE: src/app/writer/model_ops.py function execute_model_command (line 8) | def execute_model_command( FILE: src/app/writer/protocol.py class WriteCommandType (line 6) | class WriteCommandType(Enum): class WriteCommand (line 17) | class WriteCommand: class WriteResult (line 27) | class WriteResult: FILE: src/app/writer/service.py function run_writer_service (line 14) | def run_writer_service() -> None: FILE: src/main.py function main (line 8) | def main() -> None: FILE: src/migrations/env.py function get_engine (line 17) | def get_engine(): function get_engine_url (line 26) | def get_engine_url(): function get_metadata (line 46) | def get_metadata(): function run_migrations_offline (line 52) | def run_migrations_offline(): function run_migrations_online (line 71) | def run_migrations_online(): FILE: src/migrations/versions/0d954a44fa8e_feed_access.py function upgrade (line 19) | def upgrade(): function downgrade (line 49) | def downgrade(): FILE: src/migrations/versions/16311623dd58_env_hash.py function upgrade (line 19) | def upgrade(): function downgrade (line 29) | def downgrade(): FILE: src/migrations/versions/185d3448990e_stripe.py function upgrade (line 19) | def upgrade(): function downgrade (line 73) | def downgrade(): FILE: src/migrations/versions/18c2402c9202_cleanup_retention_days.py function upgrade (line 19) | def upgrade(): function downgrade (line 29) | def downgrade(): FILE: src/migrations/versions/2e25a15d11de_per_feed_auto_whitelist.py function upgrade (line 19) | def upgrade(): function downgrade (line 31) | def downgrade(): FILE: src/migrations/versions/31d767deb401_credits.py function upgrade (line 19) | def upgrade(): function downgrade (line 119) | def downgrade(): FILE: src/migrations/versions/35b12b2d9feb_landing_page.py function upgrade (line 19) | def upgrade(): function downgrade (line 34) | def downgrade(): FILE: src/migrations/versions/3c7f5f7640e4_add_counters_reset_timestamp.py function upgrade (line 21) | def upgrade() -> None: function downgrade (line 45) | def downgrade() -> None: FILE: src/migrations/versions/3d232f215842_migration.py function upgrade (line 19) | def upgrade(): function downgrade (line 34) | def downgrade(): FILE: src/migrations/versions/3eb0a3a0870b_discord.py function upgrade (line 19) | def upgrade(): function downgrade (line 35) | def downgrade(): FILE: src/migrations/versions/401071604e7b_config_tables.py function upgrade (line 19) | def upgrade(): function downgrade (line 264) | def downgrade(): FILE: src/migrations/versions/58b4eedd4c61_add_last_active_to_user.py function upgrade (line 19) | def upgrade(): function downgrade (line 27) | def downgrade(): FILE: src/migrations/versions/5bccc39c9685_zero_initial_allowance.py function upgrade (line 19) | def upgrade(): function downgrade (line 25) | def downgrade(): FILE: src/migrations/versions/608e0b27fcda_stronger_access_token.py function upgrade (line 19) | def upgrade(): function downgrade (line 29) | def downgrade(): FILE: src/migrations/versions/611dcb5d7f12_add_image_url_to_post_model_for_episode_.py function upgrade (line 19) | def upgrade(): function downgrade (line 27) | def downgrade(): FILE: src/migrations/versions/6e0e16299dcb_alternate_feed_id.py function upgrade (line 19) | def upgrade(): function downgrade (line 27) | def downgrade(): FILE: src/migrations/versions/73a6b9f9b643_allow_null_feed_id_for_aggregate_tokens.py function upgrade (line 19) | def upgrade(): function downgrade (line 27) | def downgrade(): FILE: src/migrations/versions/770771437280_episode_whitelist.py function upgrade (line 19) | def upgrade(): function downgrade (line 31) | def downgrade(): FILE: src/migrations/versions/7de4e57ec4bb_discord_settings.py function upgrade (line 19) | def upgrade(): function downgrade (line 36) | def downgrade(): FILE: src/migrations/versions/802a2365976d_gruanular_credits.py function upgrade (line 19) | def upgrade(): function downgrade (line 40) | def downgrade(): FILE: src/migrations/versions/82cfcc8e0326_refined_cuts.py function upgrade (line 19) | def upgrade(): function downgrade (line 32) | def downgrade(): FILE: src/migrations/versions/89d86978f407_limit_users.py function upgrade (line 19) | def upgrade(): function downgrade (line 27) | def downgrade(): FILE: src/migrations/versions/91ff431c832e_download_count.py function upgrade (line 19) | def upgrade(): function downgrade (line 49) | def downgrade(): FILE: src/migrations/versions/999b921ffc58_migration.py function upgrade (line 19) | def upgrade(): function downgrade (line 99) | def downgrade(): FILE: src/migrations/versions/a6f5df1a50ac_add_users_table.py function upgrade (line 20) | def upgrade() -> None: function downgrade (line 44) | def downgrade() -> None: FILE: src/migrations/versions/ab643af6472e_add_manual_feed_allowance_to_user.py function upgrade (line 19) | def upgrade(): function downgrade (line 37) | def downgrade(): FILE: src/migrations/versions/b038c2f99086_add_processingjob_table_for_async_.py function upgrade (line 19) | def upgrade(): function downgrade (line 48) | def downgrade(): FILE: src/migrations/versions/b92e47a03bb2_refactor_transcripts_to_db_tables_.py function upgrade (line 19) | def upgrade(): function downgrade (line 101) | def downgrade(): FILE: src/migrations/versions/bae70e584468_.py function upgrade (line 19) | def upgrade(): function downgrade (line 64) | def downgrade(): FILE: src/migrations/versions/c0f8893ce927_add_skipped_jobs_columns.py function upgrade (line 19) | def upgrade(): function downgrade (line 47) | def downgrade(): FILE: src/migrations/versions/ded4b70feadb_add_image_metadata_to_feed.py function upgrade (line 19) | def upgrade(): function downgrade (line 25) | def downgrade(): FILE: src/migrations/versions/e1325294473b_add_autoprocess_on_download.py function upgrade (line 19) | def upgrade(): function downgrade (line 34) | def downgrade(): FILE: src/migrations/versions/eb51923af483_multiple_supporters.py function _table_exists (line 22) | def _table_exists(table_name: str) -> bool: function _column_exists (line 29) | def _column_exists(table_name: str, column_name: str) -> bool: function upgrade (line 37) | def upgrade(): function downgrade (line 128) | def downgrade(): FILE: src/migrations/versions/f6d5fee57cc3_tz_fix.py function upgrade (line 21) | def upgrade(): function downgrade (line 71) | def downgrade(): FILE: src/migrations/versions/f7a4195e0953_add_enable_boundary_refinement_to_llm_.py function upgrade (line 19) | def upgrade(): function downgrade (line 34) | def downgrade(): FILE: src/migrations/versions/fa3a95ecd67d_audio_processing_paths.py function upgrade (line 19) | def upgrade(): function downgrade (line 30) | def downgrade(): FILE: src/podcast_processor/ad_classifier.py class ClassifyParams (line 41) | class ClassifyParams: method __init__ (line 42) | def __init__( class ClassifyException (line 57) | class ClassifyException(Exception): class AdClassifier (line 61) | class AdClassifier: method __init__ (line 64) | def __init__( method classify (line 126) | def classify( method _step (line 223) | def _step( method _process_chunk (line 291) | def _process_chunk( method _build_chunk_payload (line 338) | def _build_chunk_payload( method _combine_overlap_segments (line 411) | def _combine_overlap_segments( method _compute_next_overlap_segments (line 443) | def _compute_next_overlap_segments( method _apply_overlap_cap (line 494) | def _apply_overlap_cap( method _segments_covering_tail (line 533) | def _segments_covering_tail( method _validate_token_limit (line 555) | def _validate_token_limit(self, user_prompt_str: str, system_prompt: s... method _prepare_api_call (line 589) | def _prepare_api_call( method _generate_user_prompt (line 663) | def _generate_user_prompt( method _get_or_create_model_call (line 688) | def _get_or_create_model_call( method _should_call_llm (line 721) | def _should_call_llm(self, model_call: ModelCall) -> bool: method _perform_llm_call (line 725) | def _perform_llm_call(self, *, model_call: ModelCall, system_prompt: s... method _handle_test_mode_call (line 741) | def _handle_test_mode_call(self, model_call: ModelCall) -> None: method _process_successful_response (line 763) | def _process_successful_response( method _create_identifications (line 795) | def _create_identifications( method _adjust_confidence (line 880) | def _adjust_confidence( method _maybe_add_preroll_context (line 893) | def _maybe_add_preroll_context( method _find_matching_segment (line 940) | def _find_matching_segment( method _segment_has_ad_identification (line 956) | def _segment_has_ad_identification(self, transcript_segment_id: int) -... method _is_retryable_error (line 971) | def _is_retryable_error(self, error: Exception) -> bool: method _call_model (line 987) | def _call_model( method _handle_retryable_error (line 1112) | def _handle_retryable_error( method _handle_retry_exhausted (line 1155) | def _handle_retry_exhausted( method _get_segments_bulk (line 1182) | def _get_segments_bulk( method _get_existing_ids_bulk (line 1204) | def _get_existing_ids_bulk( method _create_identifications_bulk (line 1224) | def _create_identifications_bulk( method expand_neighbors_bulk (line 1241) | def expand_neighbors_bulk( method _should_expand_neighbor (line 1331) | def _should_expand_neighbor( method _neighbor_confidence (line 1347) | def _neighbor_confidence( method _refine_boundaries (line 1361) | def _refine_boundaries( method _group_into_blocks (line 1464) | def _group_into_blocks( method _create_block (line 1494) | def _create_block(self, identifications: List[Identification]) -> Dict... method _apply_refinement (line 1503) | def _apply_refinement( FILE: src/podcast_processor/ad_merger.py class AdGroup (line 9) | class AdGroup: class AdMerger (line 18) | class AdMerger: method __init__ (line 19) | def __init__(self) -> None: method merge (line 28) | def merge( method _group_by_proximity (line 51) | def _group_by_proximity( method _create_group (line 77) | def _create_group( method _extract_keywords (line 92) | def _extract_keywords(self, segments: List[TranscriptSegment]) -> List... method _refine_by_content (line 117) | def _refine_by_content( method _should_merge (line 159) | def _should_merge(self, group1: AdGroup, group2: AdGroup) -> bool: method _is_valid_group (line 181) | def _is_valid_group(self, group: AdGroup) -> bool: FILE: src/podcast_processor/audio.py function get_audio_duration_ms (line 13) | def get_audio_duration_ms(file_path: str) -> Optional[int]: function clip_segments_with_fade (line 31) | def clip_segments_with_fade( function _clip_segments_complex (line 61) | def _clip_segments_complex( function _clip_segments_simple (line 109) | def _clip_segments_simple( function trim_file (line 174) | def trim_file(in_path: Path, out_path: Path, start_ms: int, end_ms: int)... function split_audio (line 204) | def split_audio( FILE: src/podcast_processor/audio_processor.py class AudioProcessor (line 12) | class AudioProcessor: method __init__ (line 15) | def __init__( method get_ad_segments (line 35) | def get_ad_segments(self, post: Post) -> List[Tuple[float, float]]: method _apply_refined_boundaries (line 122) | def _apply_refined_boundaries(self, post: Post, ad_groups: Any) -> None: method _safe_get_post_row (line 141) | def _safe_get_post_row(self, post: Post) -> Optional[Post]: method _parse_refined_boundaries (line 148) | def _parse_refined_boundaries( method _refined_overlap_window_for_group (line 187) | def _refined_overlap_window_for_group( method merge_ad_segments (line 207) | def merge_ad_segments( method _get_last_segment_if_near_end (line 259) | def _get_last_segment_if_near_end( method _merge_close_segments (line 272) | def _merge_close_segments( method _filter_short_segments (line 288) | def _filter_short_segments( method _restore_last_segment_if_needed (line 296) | def _restore_last_segment_if_needed( method _extend_last_segment_to_end_if_needed (line 307) | def _extend_last_segment_to_end_if_needed( method process_audio (line 320) | def process_audio(self, post: Post, output_path: str) -> None: FILE: src/podcast_processor/boundary_refiner.py class BoundaryRefinement (line 28) | class BoundaryRefinement: class BoundaryRefiner (line 35) | class BoundaryRefiner: method __init__ (line 36) | def __init__(self, config: Config, logger: Optional[logging.Logger] = ... method _load_template (line 41) | def _load_template(self) -> Template: method refine (line 57) | def refine( method _update_model_call (line 262) | def _update_model_call( method _get_context (line 292) | def _get_context( method _heuristic_refine (line 308) | def _heuristic_refine( method _validate (line 359) | def _validate( FILE: src/podcast_processor/cue_detector.py class CueDetector (line 5) | class CueDetector: method __init__ (line 6) | def __init__(self) -> None: method has_cue (line 29) | def has_cue(self, text: str) -> bool: method analyze (line 37) | def analyze(self, text: str) -> Dict[str, bool]: method highlight_cues (line 47) | def highlight_cues(self, text: str) -> str: FILE: src/podcast_processor/llm_concurrency_limiter.py class LLMConcurrencyLimiter (line 16) | class LLMConcurrencyLimiter: method __init__ (line 19) | def __init__(self, max_concurrent_calls: int): method acquire (line 36) | def acquire(self, timeout: Optional[float] = None) -> bool: method release (line 60) | def release(self) -> None: method get_available_slots (line 69) | def get_available_slots(self) -> int: method get_active_calls (line 73) | def get_active_calls(self) -> int: function get_concurrency_limiter (line 82) | def get_concurrency_limiter(max_concurrent_calls: int = 3) -> LLMConcurr... class ConcurrencyContext (line 93) | class ConcurrencyContext: method __init__ (line 96) | def __init__(self, limiter: LLMConcurrencyLimiter, timeout: Optional[f... method __enter__ (line 108) | def __enter__(self) -> "ConcurrencyContext": method __exit__ (line 117) | def __exit__( FILE: src/podcast_processor/llm_error_classifier.py class LLMErrorClassifier (line 13) | class LLMErrorClassifier: method is_retryable_error (line 52) | def is_retryable_error(cls, error: Union[Exception, str]) -> bool: method get_error_category (line 81) | def get_error_category(cls, error: Union[Exception, str]) -> str: method get_suggested_backoff (line 111) | def get_suggested_backoff(cls, error: Union[Exception, str], attempt: ... method _matches_patterns (line 135) | def _matches_patterns(text: str, patterns: list[re.Pattern[str]]) -> b... FILE: src/podcast_processor/llm_model_call_utils.py function render_prompt_and_upsert_model_call (line 9) | def render_prompt_and_upsert_model_call( function try_upsert_model_call (line 43) | def try_upsert_model_call( function try_update_model_call (line 80) | def try_update_model_call( function extract_litellm_content (line 114) | def extract_litellm_content(response: Any) -> str: FILE: src/podcast_processor/model_output.py class AdSegmentPrediction (line 10) | class AdSegmentPrediction(BaseModel): class AdSegmentPredictionList (line 15) | class AdSegmentPredictionList(BaseModel): function _attempt_json_repair (line 28) | def _attempt_json_repair(json_str: str) -> str: function clean_and_parse_model_output (line 94) | def clean_and_parse_model_output(model_output: str) -> AdSegmentPredicti... FILE: src/podcast_processor/podcast_downloader.py class PodcastDownloader (line 21) | class PodcastDownloader: method __init__ (line 26) | def __init__( method download_episode (line 32) | def download_episode(self, post: Post, dest_path: str) -> Optional[str]: method get_and_make_download_path (line 88) | def get_and_make_download_path(self, post_title: str) -> Path: function sanitize_title (line 110) | def sanitize_title(title: str) -> str: function find_audio_link (line 115) | def find_audio_link(entry: Any) -> str: function _iter_enclosure_audio_urls (line 143) | def _iter_enclosure_audio_urls(entry: Any, audio_mime_types: Set[str]) -... function _iter_link_audio_urls (line 157) | def _iter_link_audio_urls( function download_episode (line 182) | def download_episode(post: Post, dest_path: str) -> Optional[str]: function get_and_make_download_path (line 186) | def get_and_make_download_path(post_title: str) -> Path: FILE: src/podcast_processor/podcast_processor.py function get_post_processed_audio_path (line 35) | def get_post_processed_audio_path(post: Post) -> Optional[ProcessingPaths]: function get_post_processed_audio_path_cached (line 53) | def get_post_processed_audio_path_cached( class PodcastProcessor (line 72) | class PodcastProcessor: method __init__ (line 81) | def __init__( method process (line 126) | def process( method _acquire_processing_lock (line 271) | def _acquire_processing_lock( method _perform_processing_steps (line 323) | def _perform_processing_steps( method _raise_if_cancelled (line 374) | def _raise_if_cancelled( method _classify_ad_segments (line 387) | def _classify_ad_segments( method _simulate_developer_processing (line 415) | def _simulate_developer_processing( method _handle_download_step (line 491) | def _handle_download_step( method make_dirs (line 570) | def make_dirs(self, processing_paths: ProcessingPaths) -> None: method get_system_prompt (line 577) | def get_system_prompt(self, system_prompt_path: str) -> str: method get_user_prompt_template (line 582) | def get_user_prompt_template(self, prompt_template_path: str) -> Templ... method remove_audio_files_and_reset_db (line 587) | def remove_audio_files_and_reset_db(self, post_id: Optional[int]) -> N... method _remove_unprocessed_audio (line 631) | def _remove_unprocessed_audio(self, post: Post) -> None: method _check_existing_processed_audio (line 652) | def _check_existing_processed_audio(self, post: Post) -> bool: class ProcessorException (line 709) | class ProcessorException(Exception): FILE: src/podcast_processor/processing_status_manager.py class ProcessingStatusManager (line 12) | class ProcessingStatusManager: method __init__ (line 18) | def __init__(self, db_session: Any, logger: Optional[logging.Logger] =... method generate_job_id (line 22) | def generate_job_id(self) -> str: method create_job (line 26) | def create_job( method cancel_existing_jobs (line 57) | def cancel_existing_jobs(self, post_guid: str, current_job_id: str) ->... method update_job_status (line 66) | def update_job_status( method mark_cancelled (line 117) | def mark_cancelled(self, job_id: str, error_message: Optional[str] = N... FILE: src/podcast_processor/prompt.py function transcript_excerpt_for_prompt (line 13) | def transcript_excerpt_for_prompt( function generate_system_prompt (line 29) | def generate_system_prompt() -> str: FILE: src/podcast_processor/token_rate_limiter.py class TokenRateLimiter (line 18) | class TokenRateLimiter: method __init__ (line 26) | def __init__(self, tokens_per_minute: int = 30000, window_minutes: int... method count_tokens (line 45) | def count_tokens(self, messages: List[Dict[str, str]], model: str) -> ... method _cleanup_old_usage (line 67) | def _cleanup_old_usage(self, current_time: float) -> None: method _get_current_usage (line 73) | def _get_current_usage(self, current_time: float) -> int: method check_rate_limit (line 78) | def check_rate_limit( method record_usage (line 120) | def record_usage(self, messages: List[Dict[str, str]], model: str) -> ... method wait_if_needed (line 137) | def wait_if_needed(self, messages: List[Dict[str, str]], model: str) -... method get_usage_stats (line 156) | def get_usage_stats(self) -> Dict[str, Union[int, float]]: function get_rate_limiter (line 176) | def get_rate_limiter(tokens_per_minute: int = 30000) -> TokenRateLimiter: function configure_rate_limiter_for_model (line 184) | def configure_rate_limiter_for_model(model: str) -> TokenRateLimiter: FILE: src/podcast_processor/transcribe.py class Segment (line 17) | class Segment(BaseModel): class Transcriber (line 23) | class Transcriber(ABC): method model_name (line 27) | def model_name(self) -> str: method transcribe (line 31) | def transcribe(self, audio_file_path: str) -> List[Segment]: class LocalTranscriptSegment (line 35) | class LocalTranscriptSegment(BaseModel): method to_segment (line 47) | def to_segment(self) -> Segment: class TestWhisperTranscriber (line 51) | class TestWhisperTranscriber(Transcriber): method __init__ (line 53) | def __init__(self, logger: logging.Logger): method model_name (line 57) | def model_name(self) -> str: method transcribe (line 60) | def transcribe(self, _: str) -> List[Segment]: class LocalWhisperTranscriber (line 68) | class LocalWhisperTranscriber(Transcriber): method __init__ (line 70) | def __init__(self, logger: logging.Logger, whisper_model: str): method model_name (line 75) | def model_name(self) -> str: method convert_to_pydantic (line 79) | def convert_to_pydantic( method local_seg_to_seg (line 85) | def local_seg_to_seg(local_segments: List[LocalTranscriptSegment]) -> ... method transcribe (line 88) | def transcribe(self, audio_file_path: str) -> List[Segment]: class OpenAIWhisperTranscriber (line 116) | class OpenAIWhisperTranscriber(Transcriber): method __init__ (line 118) | def __init__(self, logger: logging.Logger, config: RemoteWhisperConfig): method model_name (line 129) | def model_name(self) -> str: method transcribe (line 132) | def transcribe(self, audio_file_path: str) -> List[Segment]: method convert_segments (line 173) | def convert_segments(segments: List[TranscriptionSegment]) -> List[Seg... method add_offset_to_segments (line 184) | def add_offset_to_segments( method get_segments_for_chunk (line 194) | def get_segments_for_chunk(self, chunk_path: str) -> List[Transcriptio... class GroqTranscriptionSegment (line 220) | class GroqTranscriptionSegment(BaseModel): class GroqWhisperTranscriber (line 226) | class GroqWhisperTranscriber(Transcriber): method __init__ (line 228) | def __init__(self, logger: logging.Logger, config: GroqWhisperConfig): method model_name (line 237) | def model_name(self) -> str: method transcribe (line 240) | def transcribe(self, audio_file_path: str) -> List[Segment]: method convert_segments (line 279) | def convert_segments(segments: List[GroqTranscriptionSegment]) -> List... method add_offset_to_segments (line 290) | def add_offset_to_segments( method get_segments_for_chunk (line 300) | def get_segments_for_chunk(self, chunk_path: str) -> List[GroqTranscri... FILE: src/podcast_processor/transcription_manager.py class TranscriptionManager (line 24) | class TranscriptionManager: method __init__ (line 27) | def __init__( method _create_transcriber (line 45) | def _create_transcriber(self) -> Transcriber: method _check_existing_transcription (line 62) | def _check_existing_transcription( method _get_or_create_whisper_model_call (line 122) | def _get_or_create_whisper_model_call(self, post: Post) -> ModelCall: method transcribe (line 146) | def transcribe(self, post: Post) -> List[TranscriptSegment]: FILE: src/podcast_processor/word_boundary_refiner.py class WordBoundaryRefinement (line 32) | class WordBoundaryRefinement: class WordBoundaryRefiner (line 39) | class WordBoundaryRefiner: method __init__ (line 46) | def __init__(self, config: Config, logger: Optional[logging.Logger] = ... method _load_template (line 51) | def _load_template(self) -> Template: method refine (line 67) | def refine( method _fallback (line 206) | def _fallback(self, ad_start: float, ad_end: float) -> WordBoundaryRef... method _constrain_start (line 214) | def _constrain_start(self, estimated_start: float, orig_start: float) ... method _constrain_end (line 217) | def _constrain_end(self, estimated_end: float, orig_end: float) -> float: method _parse_json (line 221) | def _parse_json(self, content: str) -> Optional[Dict[str, Any]]: method _has_text (line 234) | def _has_text(value: Any) -> bool: method _extract_payload (line 242) | def _extract_payload(self, parsed: Dict[str, Any]) -> Dict[str, Any]: method _default_reason (line 260) | def _default_reason(reason: str, *, changed: bool) -> str: method _result_status (line 266) | def _result_status( method _refine_start (line 273) | def _refine_start( method _refine_end (line 320) | def _refine_end( method _get_context (line 350) | def _get_context( method _context_by_seq_window (line 369) | def _context_by_seq_window( method _context_by_time_overlap (line 404) | def _context_by_time_overlap( method _segment_overlaps (line 423) | def _segment_overlaps( method _estimate_phrase_times (line 436) | def _estimate_phrase_times( method _estimate_phrase_time (line 462) | def _estimate_phrase_time( method _find_phrase_match (line 537) | def _find_phrase_match( method _find_subsequence (line 566) | def _find_subsequence( method _estimate_word_time (line 584) | def _estimate_word_time( method _find_segment (line 620) | def _find_segment( method _split_words (line 635) | def _split_words(self, text: str) -> List[str]: method _normalize_token (line 642) | def _normalize_token(self, token: str) -> str: method _resolve_word_index (line 650) | def _resolve_word_index( method _update_model_call (line 677) | def _update_model_call( FILE: src/shared/config.py class ProcessingConfig (line 10) | class ProcessingConfig(BaseModel): method validate_overlap_limits (line 19) | def validate_overlap_limits(self) -> "ProcessingConfig": class OutputConfig (line 26) | class OutputConfig(BaseModel): method min_ad_segment_separation_seconds (line 33) | def min_ad_segment_separation_seconds(self) -> int: method min_ad_segment_separation_seconds (line 38) | def min_ad_segment_separation_seconds(self, value: int) -> None: class TestWhisperConfig (line 45) | class TestWhisperConfig(BaseModel): class RemoteWhisperConfig (line 49) | class RemoteWhisperConfig(BaseModel): class GroqWhisperConfig (line 59) | class GroqWhisperConfig(BaseModel): class LocalWhisperConfig (line 67) | class LocalWhisperConfig(BaseModel): class Config (line 72) | class Config(BaseModel): method redacted (line 153) | def redacted(self) -> Config: method validate_whisper_config (line 162) | def validate_whisper_config(self) -> "Config": FILE: src/shared/interfaces.py class Post (line 7) | class Post(Protocol): method whitelisted (line 16) | def whitelisted(self) -> bool: FILE: src/shared/llm_utils.py function model_uses_max_completion_tokens (line 20) | def model_uses_max_completion_tokens(model_name: str | None) -> bool: FILE: src/shared/processing_paths.py class ProcessingPaths (line 8) | class ProcessingPaths: function paths_from_unprocessed_path (line 12) | def paths_from_unprocessed_path( function get_job_unprocessed_path (line 31) | def get_job_unprocessed_path(post_guid: str, job_id: str, post_title: st... function get_instance_dir (line 44) | def get_instance_dir() -> Path: function get_base_podcast_data_dir (line 52) | def get_base_podcast_data_dir() -> Path: function get_in_root (line 59) | def get_in_root() -> Path: function get_srv_root (line 63) | def get_srv_root() -> Path: FILE: src/shared/test_utils.py function create_standard_test_config (line 8) | def create_standard_test_config( FILE: src/tests/conftest.py function app (line 46) | def app() -> Generator[Flask, None, None]: function test_config (line 59) | def test_config() -> Config: function test_logger (line 64) | def test_logger() -> logging.Logger: function mock_db_session (line 69) | def mock_db_session() -> MagicMock: function mock_transcription_manager (line 80) | def mock_transcription_manager() -> MagicMock: function mock_ad_classifier (line 94) | def mock_ad_classifier() -> MagicMock: function mock_audio_processor (line 101) | def mock_audio_processor() -> MagicMock: function mock_downloader (line 108) | def mock_downloader() -> MagicMock: function mock_status_manager (line 116) | def mock_status_manager() -> MagicMock: FILE: src/tests/test_ad_classifier.py function app (line 22) | def app() -> Generator[Flask, None, None]: function test_config (line 35) | def test_config() -> Config: function mock_db_session (line 40) | def mock_db_session() -> MagicMock: function test_classifier (line 51) | def test_classifier(test_config: Config) -> AdClassifier: function test_classifier_with_mocks (line 57) | def test_classifier_with_mocks( function test_call_model (line 72) | def test_call_model(test_config: Config, app: Flask) -> None: function test_call_model_retry_on_internal_error (line 115) | def test_call_model_retry_on_internal_error(test_config: Config, app: Fl... function test_process_chunk (line 169) | def test_process_chunk(test_config: Config, app: Flask) -> None: function test_compute_next_overlap_segments_includes_context (line 247) | def test_compute_next_overlap_segments_includes_context( function test_compute_next_overlap_segments_respects_cap (line 274) | def test_compute_next_overlap_segments_respects_cap( function test_compute_next_overlap_segments_baseline_overlap_without_ads (line 300) | def test_compute_next_overlap_segments_baseline_overlap_without_ads( function test_create_identifications_skips_existing_ad_label (line 323) | def test_create_identifications_skips_existing_ad_label( function test_build_chunk_payload_trims_for_token_limit (line 360) | def test_build_chunk_payload_trims_for_token_limit( FILE: src/tests/test_ad_classifier_rate_limiting_integration.py class TestAdClassifierRateLimiting (line 13) | class TestAdClassifierRateLimiting: method test_rate_limiter_initialization_enabled (line 16) | def test_rate_limiter_initialization_enabled(self): method test_rate_limiter_initialization_disabled (line 29) | def test_rate_limiter_initialization_disabled(self): method test_rate_limiter_custom_limit (line 38) | def test_rate_limiter_custom_limit(self): method test_is_retryable_error_rate_limit_errors (line 48) | def test_is_retryable_error_rate_limit_errors(self): method test_is_retryable_error_non_retryable (line 67) | def test_is_retryable_error_non_retryable(self): method test_call_model_with_rate_limiter (line 86) | def test_call_model_with_rate_limiter(self, mock_isinstance, mock_lite... method test_rate_limit_backoff_timing (line 142) | def test_rate_limit_backoff_timing(self, mock_sleep): method test_rate_limiter_model_specific_configs (line 162) | def test_rate_limiter_model_specific_configs(self): FILE: src/tests/test_aggregate_feed.py function test_get_user_aggregate_posts_auth_disabled (line 8) | def test_get_user_aggregate_posts_auth_disabled(app): function test_get_user_aggregate_posts_auth_enabled (line 47) | def test_get_user_aggregate_posts_auth_enabled(app): FILE: src/tests/test_audio_processor.py function test_processor (line 15) | def test_processor( function test_processor_with_mocks (line 24) | def test_processor_with_mocks( function test_get_ad_segments (line 44) | def test_get_ad_segments(app: Flask) -> None: function test_merge_ad_segments (line 82) | def test_merge_ad_segments( function test_merge_ad_segments_with_short_segments (line 106) | def test_merge_ad_segments_with_short_segments( function test_merge_ad_segments_end_extension (line 128) | def test_merge_ad_segments_end_extension( function test_process_audio (line 148) | def test_process_audio( FILE: src/tests/test_config_error_handling.py class TestConfigurationErrorHandling (line 15) | class TestConfigurationErrorHandling: method test_config_with_none_values (line 18) | def test_config_with_none_values(self) -> None: method test_zero_values (line 38) | def test_zero_values(self) -> None: method test_very_large_values (line 59) | def test_very_large_values(self) -> None: method test_boolean_field_validation (line 83) | def test_boolean_field_validation(self) -> None: class TestEnvKeyValidation (line 117) | class TestEnvKeyValidation: method test_llm_and_groq_conflict_raises (line 120) | def test_llm_and_groq_conflict_raises(self, monkeypatch: Any) -> None: method test_whisper_remote_allows_different_key (line 128) | def test_whisper_remote_allows_different_key(self, monkeypatch: Any) -... FILE: src/tests/test_feeds.py class MockPost (line 30) | class MockPost: method __init__ (line 33) | def __init__( method audio_len_bytes (line 59) | def audio_len_bytes(self): class MockFeed (line 63) | class MockFeed: method __init__ (line 66) | def __init__( function mock_feed_data (line 87) | def mock_feed_data(): function mock_db_session (line 129) | def mock_db_session(monkeypatch): function mock_post (line 137) | def mock_post(): function mock_feed (line 143) | def mock_feed(): function test_fetch_feed (line 149) | def test_fetch_feed(mock_parse, mock_feed_data): function test_refresh_feed (line 158) | def test_refresh_feed(mock_db_session): function test_should_auto_whitelist_new_posts_requires_members (line 183) | def test_should_auto_whitelist_new_posts_requires_members( function test_should_auto_whitelist_new_posts_true_with_members (line 195) | def test_should_auto_whitelist_new_posts_true_with_members(monkeypatch, ... function test_should_auto_whitelist_requires_members (line 206) | def test_should_auto_whitelist_requires_members( function test_should_auto_whitelist_with_members (line 219) | def test_should_auto_whitelist_with_members(monkeypatch, mock_feed, mock... function test_should_auto_whitelist_true_when_auth_disabled (line 230) | def test_should_auto_whitelist_true_when_auth_disabled(monkeypatch, mock... function test_should_auto_whitelist_true_when_no_users (line 239) | def test_should_auto_whitelist_true_when_no_users( function test_should_auto_whitelist_respects_feed_override_true (line 252) | def test_should_auto_whitelist_respects_feed_override_true(monkeypatch, ... function test_should_auto_whitelist_respects_feed_override_false (line 261) | def test_should_auto_whitelist_respects_feed_override_false(monkeypatch,... function test_refresh_feed_unwhitelists_without_members (line 274) | def test_refresh_feed_unwhitelists_without_members( function test_refresh_feed_whitelists_when_member_exists (line 301) | def test_refresh_feed_whitelists_when_member_exists( function test_add_or_refresh_feed_existing (line 326) | def test_add_or_refresh_feed_existing( function test_add_or_refresh_feed_new (line 347) | def test_add_or_refresh_feed_new( function test_add_feed (line 369) | def test_add_feed(mock_post_class, mock_writer_client, mock_feed_data, m... function test_feed_item (line 409) | def test_feed_item(mock_post, app): function test_feed_item_with_reverse_proxy (line 439) | def test_feed_item_with_reverse_proxy(mock_post, app): function test_feed_item_with_reverse_proxy_custom_port (line 472) | def test_feed_item_with_reverse_proxy_custom_port(mock_post, app): function test_get_base_url_without_reverse_proxy (line 505) | def test_get_base_url_without_reverse_proxy(): function test_get_base_url_with_reverse_proxy_default_port (line 514) | def test_get_base_url_with_reverse_proxy_default_port(): function test_get_base_url_with_reverse_proxy_custom_port (line 536) | def test_get_base_url_with_reverse_proxy_custom_port(): function test_get_base_url_localhost (line 561) | def test_get_base_url_localhost(): function test_generate_feed_xml_filters_processed_whitelisted (line 574) | def test_generate_feed_xml_filters_processed_whitelisted( function test_generate_feed_xml_includes_all_when_autoprocess_enabled (line 637) | def test_generate_feed_xml_includes_all_when_autoprocess_enabled( function test_make_post (line 706) | def test_make_post(mock_post_class, mock_feed): function test_get_guid_uses_id_if_valid_uuid (line 746) | def test_get_guid_uses_id_if_valid_uuid(mock_uuid5, mock_find_audio_link... function test_get_guid_generates_uuid_if_invalid_id (line 763) | def test_get_guid_generates_uuid_if_invalid_id( function test_get_duration_with_valid_duration (line 787) | def test_get_duration_with_valid_duration(): function test_get_duration_with_invalid_duration (line 796) | def test_get_duration_with_invalid_duration(): function test_get_duration_with_missing_duration (line 805) | def test_get_duration_with_missing_duration(): function test_get_base_url_no_request_context_fallback (line 814) | def test_get_base_url_no_request_context_fallback(): function test_get_base_url_with_http2_pseudo_headers (line 824) | def test_get_base_url_with_http2_pseudo_headers(): function test_get_base_url_with_strict_transport_security (line 849) | def test_get_base_url_with_strict_transport_security(): function test_get_base_url_fallback_http_without_sts (line 875) | def test_get_base_url_fallback_http_without_sts(): FILE: src/tests/test_filenames.py function test_filenames (line 8) | def test_filenames() -> None: FILE: src/tests/test_helpers.py function create_test_config (line 10) | def create_test_config(**overrides: Any) -> Config: FILE: src/tests/test_llm_concurrency_limiter.py class TestLLMConcurrencyLimiter (line 17) | class TestLLMConcurrencyLimiter: method test_initialization (line 20) | def test_initialization(self): method test_initialization_invalid_value (line 27) | def test_initialization_invalid_value(self): method test_acquire_and_release (line 39) | def test_acquire_and_release(self): method test_acquire_timeout (line 67) | def test_acquire_timeout(self): method test_context_manager (line 82) | def test_context_manager(self): method test_context_manager_timeout (line 95) | def test_context_manager_timeout(self): method test_thread_safety (line 109) | def test_thread_safety(self): class TestGlobalConcurrencyLimiter (line 149) | class TestGlobalConcurrencyLimiter: method test_get_concurrency_limiter_singleton (line 152) | def test_get_concurrency_limiter_singleton(self): method test_get_concurrency_limiter_different_limits (line 165) | def test_get_concurrency_limiter_different_limits(self): FILE: src/tests/test_llm_error_classifier.py class TestLLMErrorClassifier (line 10) | class TestLLMErrorClassifier: method test_rate_limit_errors (line 13) | def test_rate_limit_errors(self): method test_timeout_errors (line 27) | def test_timeout_errors(self): method test_server_errors (line 40) | def test_server_errors(self): method test_non_retryable_errors (line 53) | def test_non_retryable_errors(self): method test_auth_vs_client_errors (line 69) | def test_auth_vs_client_errors(self): method test_unknown_errors (line 89) | def test_unknown_errors(self): method test_suggested_backoff (line 101) | def test_suggested_backoff(self): method test_exception_objects (line 126) | def test_exception_objects(self): method test_case_insensitive_matching (line 140) | def test_case_insensitive_matching(self): FILE: src/tests/test_parse_model_output.py function test_clean_parse_output (line 11) | def test_clean_parse_output() -> None: function test_parse_multiple_segments_output (line 26) | def test_parse_multiple_segments_output() -> None: function test_clean_parse_output_malformed (line 43) | def test_clean_parse_output_malformed() -> None: function test_clean_parse_output_with_content_type (line 51) | def test_clean_parse_output_with_content_type() -> None: function test_clean_parse_output_truncated_missing_closing_brackets (line 63) | def test_clean_parse_output_truncated_missing_closing_brackets() -> None: function test_clean_parse_output_truncated_multiple_segments (line 72) | def test_clean_parse_output_truncated_multiple_segments() -> None: function test_clean_parse_output_truncated_with_content_type (line 84) | def test_clean_parse_output_truncated_with_content_type() -> None: FILE: src/tests/test_podcast_downloader.py function test_post (line 14) | def test_post(app): function downloader (line 39) | def downloader(tmp_path): function mock_entry (line 45) | def mock_entry(): function test_sanitize_title (line 60) | def test_sanitize_title(): function test_get_and_make_download_path (line 68) | def test_get_and_make_download_path(downloader): function test_find_audio_link_with_audio_link (line 79) | def test_find_audio_link_with_audio_link(mock_entry): function test_find_audio_link_without_audio_link (line 83) | def test_find_audio_link_without_audio_link(): function test_download_episode_already_exists (line 92) | def test_download_episode_already_exists(mock_get, test_post, downloader... function test_download_episode_new_file (line 110) | def test_download_episode_new_file(mock_get, test_post, downloader, app): function test_download_episode_download_failed (line 138) | def test_download_episode_download_failed(mock_get, test_post, downloade... function test_download_episode_invalid_url (line 165) | def test_download_episode_invalid_url( function test_download_episode_invalid_post_title (line 180) | def test_download_episode_invalid_post_title(mock_get, test_post, downlo... FILE: src/tests/test_podcast_processor_cleanup.py function test_remove_unprocessed_audio_deletes_file (line 14) | def test_remove_unprocessed_audio_deletes_file(app, tmp_path) -> None: FILE: src/tests/test_post_cleanup.py function _create_feed (line 18) | def _create_feed() -> Feed: function _create_post (line 31) | def _create_post(feed: Feed, guid: str, download_url: str) -> Post: function test_cleanup_removes_expired_posts (line 45) | def test_cleanup_removes_expired_posts(app, tmp_path) -> None: function test_cleanup_skips_when_retention_disabled (line 141) | def test_cleanup_skips_when_retention_disabled(app) -> None: function test_cleanup_includes_non_whitelisted_processed_posts (line 166) | def test_cleanup_includes_non_whitelisted_processed_posts(app, tmp_path)... function test_cleanup_skips_unprocessed_unwhitelisted_posts (line 205) | def test_cleanup_skips_unprocessed_unwhitelisted_posts(app) -> None: FILE: src/tests/test_post_routes.py function test_download_endpoints_increment_counter (line 13) | def test_download_endpoints_increment_counter(app, tmp_path): function test_download_triggers_processing_when_enabled (line 67) | def test_download_triggers_processing_when_enabled(app): function test_download_missing_audio_returns_404_when_disabled (line 111) | def test_download_missing_audio_returns_404_when_disabled(app): function test_download_auto_whitelists_post (line 144) | def test_download_auto_whitelists_post(app, tmp_path): function test_download_rejects_when_not_whitelisted_and_toggle_off (line 188) | def test_download_rejects_when_not_whitelisted_and_toggle_off(app): function test_toggle_whitelist_all_requires_admin (line 219) | def test_toggle_whitelist_all_requires_admin(app): function test_feed_posts_pagination_and_filtering (line 269) | def test_feed_posts_pagination_and_filtering(app): FILE: src/tests/test_posts.py class TestPostsFunctions (line 8) | class TestPostsFunctions: method test_remove_associated_files_files_dont_exist (line 16) | def test_remove_associated_files_files_dont_exist( FILE: src/tests/test_process_audio.py function test_get_duration_ms (line 14) | def test_get_duration_ms() -> None: function test_clip_segment_with_fade (line 18) | def test_clip_segment_with_fade() -> None: function test_clip_segment_with_fade_beginning (line 44) | def test_clip_segment_with_fade_beginning() -> None: function test_clip_segment_with_fade_end (line 70) | def test_clip_segment_with_fade_end() -> None: function test_split_audio (line 99) | def test_split_audio() -> None: FILE: src/tests/test_rate_limiting_config.py class TestRateLimitingConfig (line 10) | class TestRateLimitingConfig: method test_default_rate_limiting_config (line 13) | def test_default_rate_limiting_config(self) -> None: method test_custom_rate_limiting_config (line 37) | def test_custom_rate_limiting_config(self) -> None: method test_partial_rate_limiting_config (line 66) | def test_partial_rate_limiting_config(self) -> None: method test_config_field_descriptions (line 93) | def test_config_field_descriptions(self) -> None: FILE: src/tests/test_rate_limiting_edge_cases.py class TestRateLimitingEdgeCases (line 15) | class TestRateLimitingEdgeCases: method test_token_counting_edge_cases (line 18) | def test_token_counting_edge_cases(self) -> None: method test_rate_limiter_boundary_conditions (line 38) | def test_rate_limiter_boundary_conditions(self) -> None: method test_rate_limiter_time_window_edge (line 58) | def test_rate_limiter_time_window_edge(self) -> None: method test_config_validation_boundary_values (line 72) | def test_config_validation_boundary_values(self) -> None: method test_error_classification_comprehensive (line 86) | def test_error_classification_comprehensive(self) -> None: method test_backoff_progression (line 134) | def test_backoff_progression(self, mock_sleep: Any) -> None: method test_rate_limiter_with_very_short_window (line 206) | def test_rate_limiter_with_very_short_window(self) -> None: method test_model_configuration_case_sensitivity (line 220) | def test_model_configuration_case_sensitivity(self) -> None: method test_thread_safety_stress (line 252) | def test_thread_safety_stress(self) -> None: FILE: src/tests/test_session_auth.py function auth_app (line 18) | def auth_app() -> Flask: function test_login_sets_session_cookie_and_allows_authenticated_requests (line 78) | def test_login_sets_session_cookie_and_allows_authenticated_requests( function test_logout_clears_session (line 100) | def test_logout_clears_session(auth_app: Flask) -> None: function test_protected_route_without_session_returns_json_401 (line 112) | def test_protected_route_without_session_returns_json_401(auth_app: Flas... function test_feed_requires_token_when_no_session (line 120) | def test_feed_requires_token_when_no_session(auth_app: Flask) -> None: function test_share_link_generates_token_and_allows_query_access (line 128) | def test_share_link_generates_token_and_allows_query_access(auth_app: Fl... function test_share_link_returns_same_token_for_user_and_feed (line 176) | def test_share_link_returns_same_token_for_user_and_feed(auth_app: Flask... FILE: src/tests/test_token_limit_config.py function test_config_validation (line 8) | def test_config_validation() -> None: FILE: src/tests/test_token_rate_limiter.py class TestTokenRateLimiter (line 16) | class TestTokenRateLimiter: method test_initialization (line 19) | def test_initialization(self) -> None: method test_count_tokens (line 32) | def test_count_tokens(self) -> None: method test_token_counting_fallback (line 54) | def test_token_counting_fallback(self) -> None: method test_cleanup_old_usage (line 63) | def test_cleanup_old_usage(self) -> None: method test_get_current_usage (line 81) | def test_get_current_usage(self) -> None: method test_check_rate_limit_within_limits (line 95) | def test_check_rate_limit_within_limits(self) -> None: method test_check_rate_limit_exceeds_limits (line 105) | def test_check_rate_limit_exceeds_limits(self) -> None: method test_record_usage (line 126) | def test_record_usage(self) -> None: method test_wait_if_needed_no_wait (line 140) | def test_wait_if_needed_no_wait(self) -> None: method test_wait_if_needed_with_wait (line 158) | def test_wait_if_needed_with_wait(self) -> None: method test_get_usage_stats (line 179) | def test_get_usage_stats(self) -> None: method test_thread_safety (line 202) | def test_thread_safety(self) -> None: class TestGlobalRateLimiter (line 226) | class TestGlobalRateLimiter: method test_get_rate_limiter_singleton (line 229) | def test_get_rate_limiter_singleton(self) -> None: method test_get_rate_limiter_different_limits (line 237) | def test_get_rate_limiter_different_limits(self) -> None: method test_configure_rate_limiter_for_model_anthropic (line 246) | def test_configure_rate_limiter_for_model_anthropic(self) -> None: method test_configure_rate_limiter_for_model_openai (line 253) | def test_configure_rate_limiter_for_model_openai(self) -> None: method test_configure_rate_limiter_for_model_gemini (line 268) | def test_configure_rate_limiter_for_model_gemini(self) -> None: method test_configure_rate_limiter_for_model_unknown (line 280) | def test_configure_rate_limiter_for_model_unknown(self) -> None: method test_configure_rate_limiter_partial_match (line 285) | def test_configure_rate_limiter_partial_match(self) -> None: FILE: src/tests/test_transcribe.py function test_remote_transcribe (line 12) | def test_remote_transcribe() -> None: function test_local_transcribe (line 30) | def test_local_transcribe() -> None: function test_groq_transcribe (line 43) | def test_groq_transcribe(mocker: Any) -> None: function test_offset (line 82) | def test_offset() -> None: FILE: src/tests/test_transcription_manager.py class MockTranscriber (line 16) | class MockTranscriber(Transcriber): method __init__ (line 19) | def __init__(self, mock_response=None): method model_name (line 24) | def model_name(self) -> str: method transcribe (line 28) | def transcribe(self, audio_path): function app (line 36) | def app() -> Generator[Flask, None, None]: function test_config (line 49) | def test_config() -> Config: function test_logger (line 57) | def test_logger() -> logging.Logger: function mock_db_session (line 62) | def mock_db_session() -> MagicMock: function mock_transcriber (line 73) | def mock_transcriber() -> MockTranscriber: function test_manager (line 84) | def test_manager( function test_check_existing_transcription_success (line 108) | def test_check_existing_transcription_success( function test_check_existing_transcription_no_model_call (line 147) | def test_check_existing_transcription_no_model_call( function test_transcribe_new (line 162) | def test_transcribe_new( function test_transcribe_handles_error (line 203) | def test_transcribe_handles_error( function test_transcribe_reuses_placeholder_model_call (line 246) | def test_transcribe_reuses_placeholder_model_call( FILE: tests/test_cue_detector.py class TestCueDetector (line 8) | class TestCueDetector(unittest.TestCase): method setUp (line 9) | def setUp(self) -> None: method test_highlight_cues_url (line 12) | def test_highlight_cues_url(self) -> None: method test_highlight_cues_promo (line 18) | def test_highlight_cues_promo(self) -> None: method test_highlight_cues_cta (line 26) | def test_highlight_cues_cta(self) -> None: method test_highlight_cues_multiple (line 31) | def test_highlight_cues_multiple(self) -> None: method test_highlight_cues_no_cues (line 44) | def test_highlight_cues_no_cues(self) -> None: method test_integration_prompt (line 48) | def test_integration_prompt(self) -> None: