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
- func AddID3Tags(filePath string, episode PodcastEpisode) error
- func CleanupID3TempFiles(filePath string) error
- func FormatBytes(bytes int64) string
- func USBDrivesEqual(a, b []USBDrive) bool
- func VerifyNoTempFiles(filePath string) error
- type Debug
- type DirectoryTemplate
- type DriveManager
- type FileOp
- type PodcastEpisode
- type PodcastMatcher
- type PodcastScanner
- type PodcastSync
- type ProgressWriter
- type TransferManager
- type TransferProgress
- type USBDrive
Constants ¶
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
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 ¶
FormatBytes returns a human-readable representation of a byte count
func USBDrivesEqual ¶
USBDrivesEqual compare two slices of USB drive names for equality
func VerifyNoTempFiles ¶ added in v0.3.1
VerifyNoTempFiles checks if any temporary ID3 files exist and returns an error if found.
Types ¶
type DirectoryTemplate ¶
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.
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.
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.