internal

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 13, 2025 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package internal provides internal utilities for managing podcast episodes on macOS.

Package internal provides core functionality for podcast synchronization, including USB drive detection, podcast matching, and file transfer progress tracking.

Index

Constants

View Source
const AppleEpochOffset = 978307200

AppleEpochOffset is the difference between Apple's epoch (2001-01-01) and Unix epoch (1970-01-01)

Variables

This section is empty.

Functions

func AddID3Tags added in v0.3.0

func AddID3Tags(filePath string, episode PodcastEpisode) error

AddID3Tags adds metadata from the Apple Podcasts database to an audio file. This is best-effort; errors are returned but should not fail the sync operation.

The function implements several safeguards to prevent duplicate files: 1. Cleans up any existing temp files before starting 2. Sets ID3v2.3 for maximum compatibility with older players 3. Verifies no temp files remain after save 4. Retries once on failure with cleanup

func CleanupID3TempFiles added in v0.3.1

func CleanupID3TempFiles(filePath string) error

CleanupID3TempFiles removes any orphaned temporary files created by the id3v2 library. The library creates temporary files named "{originalFile}-id3v2" during the save process. If the atomic rename fails (common on USB drives with FAT32), these temp files remain.

func FormatBytes

func FormatBytes(bytes int64) string

FormatBytes returns a human-readable representation of a byte count

func USBDrivesEqual

func USBDrivesEqual(a, b []USBDrive) bool

USBDrivesEqual compare two slices of USB drive names for equality

func VerifyNoTempFiles added in v0.3.1

func VerifyNoTempFiles(filePath string) error

VerifyNoTempFiles checks if any temporary ID3 files exist and returns an error if found.

Types

type Debug

type Debug struct {
	DTitle       string
	DDescription string
}

func (Debug) Description

func (d Debug) Description() string

func (Debug) FilterValue

func (d Debug) FilterValue() string

func (Debug) Title

func (d Debug) Title() string

type DirectoryTemplate

type DirectoryTemplate struct {
	ShowNameFormat string
	EpisodeFormat  string
	DateFormat     string
	SanitizeNames  bool
	CreateIndex    bool
}

type DriveManager

type DriveManager struct {
	// contains filtered or unexported fields
}

func NewDriveManager

func NewDriveManager(volumesPath string, template DirectoryTemplate) *DriveManager

NewDriveManager creates a new DriveManager instance

func (*DriveManager) DetectDrives

func (dm *DriveManager) DetectDrives() ([]USBDrive, error)

DetectDrives finds all mounted USB drives except Macintosh HD

type FileOp

type FileOp struct {
	Progress TransferProgress
	Complete bool
	Error    error
}

FileOp represents a file operation update sent through channels.

type PodcastEpisode

type PodcastEpisode struct {
	ZTitle    string
	ShowName  string
	FilePath  string
	Published time.Time
	Selected  bool
	FileSize  int64
	OnDrive   bool
	Duration  time.Duration
	Progress  float64
}

func LoadLocalPodcasts

func LoadLocalPodcasts(episodes []PodcastEpisode) ([]PodcastEpisode, error)

LoadLocalPodcasts fills in the file size and checksum for each episode. Continues processing all episodes even if some fail, setting FileSize to 0 for failed episodes. Returns episodes with file sizes populated where possible, and nil error.

func LoadMacPodcasts

func LoadMacPodcasts() ([]PodcastEpisode, error)

LoadMacPodcasts queries every podcast episodes from the local Apple Podcasts database

func (PodcastEpisode) Description

func (p PodcastEpisode) Description() string

func (PodcastEpisode) FilterValue

func (p PodcastEpisode) FilterValue() string

func (PodcastEpisode) Title

func (p PodcastEpisode) Title() string

type PodcastMatcher

type PodcastMatcher struct {
	// contains filtered or unexported fields
}

func NewPodcastMatcher

func NewPodcastMatcher(podcastsBySize map[int64][]*PodcastEpisode) *PodcastMatcher

NewPodcastMatcher creates a new PodcastMatcher instance

func (*PodcastMatcher) Match

func (pm *PodcastMatcher) Match(podcast *PodcastEpisode) error

Match attempts to match a podcast with its local counterpart using a cascading strategy: 1. Path-based matching (fastest, works for tagged files) 2. Size-based matching (existing approach) 3. Duration-based tiebreaking (fast) 4. Checksum matching (slowest, final fallback)

type PodcastScanner

type PodcastScanner struct {
	// contains filtered or unexported fields
}

func NewPodcastScanner

func NewPodcastScanner(template DirectoryTemplate) *PodcastScanner

NewPodcastScanner creates a new PodcastScanner instance

func (*PodcastScanner) ScanDrive

func (ps *PodcastScanner) ScanDrive(drive USBDrive, podcastsBySize map[int64][]*PodcastEpisode) ([]PodcastEpisode, error)

ScanDrive scans a drive for podcasts and returns matched episodes

type PodcastSync

type PodcastSync struct {
	// contains filtered or unexported fields
}

func NewPodcastSync

func NewPodcastSync() *PodcastSync

NewPodcastSync creates a new PodcastSync instance

func (*PodcastSync) DeleteSelected

func (ps *PodcastSync) DeleteSelected(episodes []PodcastEpisode) FileOp

DeleteSelected removes selected episodes from the drive

func (*PodcastSync) StartSync

func (ps *PodcastSync) StartSync(episodes []PodcastEpisode, drive USBDrive, ch chan<- FileOp) *TransferManager

StartSync begins the podcast synchronization process

type ProgressWriter

type ProgressWriter struct {
	// contains filtered or unexported fields
}

ProgressWriter handles asynchronous progress updates and speed calculations. It runs a background goroutine that periodically sends progress updates through a channel. Thread-safe: uses atomic operations for byte counting and mutexes for progress updates. IMPORTANT: Callers MUST call Stop() to clean up the background goroutine.

func NewProgressWriter

func NewProgressWriter(total int64, progress *TransferProgress, ch chan<- FileOp) *ProgressWriter

NewProgressWriter creates a new ProgressWriter that sends periodic updates through ch. Starts a background goroutine for asynchronous updates. The caller must call Stop() to clean up resources.

func (*ProgressWriter) IsStopped

func (pw *ProgressWriter) IsStopped() bool

IsStopped returns whether the progress writer has been stopped.

func (*ProgressWriter) Stop

func (pw *ProgressWriter) Stop()

Stop gracefully shuts down the progress writer's background goroutine. Sends a final update before stopping. Safe to call multiple times.

func (*ProgressWriter) Write

func (pw *ProgressWriter) Write(p []byte) (int, error)

Write is a no-op implementation for interface compatibility. TransferManager handles all byte counting using atomic operations.

type TransferManager added in v0.2.0

type TransferManager struct {
	// contains filtered or unexported fields
}

TransferManager coordinates file transfer progress tracking across multiple files. It maintains accurate byte counts and delegates UI updates to ProgressWriter. Safe for concurrent use - all public methods are protected by mutex or atomic operations.

func NewTransferManager added in v0.2.0

func NewTransferManager(totalBytes int64, totalFiles int, ch chan<- FileOp) *TransferManager

NewTransferManager creates a new TransferManager for tracking file transfer progress. It automatically starts a background ProgressWriter for UI updates. totalBytes: total bytes to transfer across all files totalFiles: total number of files to transfer ch: channel for sending progress updates (caller owns, TransferManager will not close it)

func (*TransferManager) CompleteFile added in v0.2.0

func (tm *TransferManager) CompleteFile(fileSize int64)

CompleteFile marks a file transfer as complete and updates base offset.

func (*TransferManager) IsStopped added in v0.2.0

func (tm *TransferManager) IsStopped() bool

IsStopped returns whether the transfer manager has been stopped.

func (*TransferManager) StartFile added in v0.2.0

func (tm *TransferManager) StartFile(filename string)

StartFile marks the beginning of a new file transfer. Resets current file progress and updates the progress display.

func (*TransferManager) Stop added in v0.2.0

func (tm *TransferManager) Stop()

Stop gracefully shuts down the progress writer. Blocks until all background goroutines have exited. Safe to call multiple times.

func (*TransferManager) Write added in v0.2.0

func (tm *TransferManager) Write(p []byte) (int, error)

Write implements io.Writer for tracking bytes transferred during file copy. This method is called by io.Copy and similar functions.

type TransferProgress

type TransferProgress struct {
	CurrentFile      string
	CurrentProgress  float64
	BytesTransferred int64
	TotalBytes       int64
	Speed            float64 // bytes per second
	StartTime        time.Time
	FilesDone        int
	TotalFiles       int
}

TransferProgress represents the current state of a file transfer operation. All fields are safe to read, but writes should be coordinated through TransferManager.

type USBDrive

type USBDrive struct {
	Name      string
	MountPath string
	Folder    string
}

func (USBDrive) Description

func (d USBDrive) Description() string

func (USBDrive) FilterValue

func (d USBDrive) FilterValue() string

func (USBDrive) Title

func (d USBDrive) Title() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL