Files
Roger d75738b90f feat(safari): multi-profile support (#581)
* feat(safari): multi-profile support
2026-04-21 15:50:36 +08:00

69 lines
1.9 KiB
Go

package safari
import (
"fmt"
"os"
"github.com/moond4rk/plist"
"github.com/moond4rk/hackbrowserdata/types"
)
type safariDownloads struct {
DownloadHistory []safariDownloadEntry `plist:"DownloadHistory"`
}
type safariDownloadEntry struct {
URL string `plist:"DownloadEntryURL"`
Path string `plist:"DownloadEntryPath"`
TotalBytes int64 `plist:"DownloadEntryProgressTotalToLoad"`
ProfileUUID string `plist:"DownloadEntryProfileUUIDStringKey"`
RemoveWhenDone bool `plist:"DownloadEntryRemoveWhenDoneKey"`
DownloadIdentifier string `plist:"DownloadEntryIdentifier"`
}
// extractDownloads reads Downloads.plist (shared across Safari profiles) and returns only the entries
// owned by ownerUUID — either "DefaultProfile" or a named profile's uppercase UUID. Entries written by
// older Safari (no ProfileUUID field) are attributed to the default profile.
func extractDownloads(path, ownerUUID string) ([]types.DownloadEntry, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("open downloads: %w", err)
}
defer f.Close()
var dl safariDownloads
if err := plist.NewDecoder(f).Decode(&dl); err != nil {
return nil, fmt.Errorf("decode downloads: %w", err)
}
var downloads []types.DownloadEntry
for _, d := range dl.DownloadHistory {
if !ownsDownload(d.ProfileUUID, ownerUUID) {
continue
}
downloads = append(downloads, types.DownloadEntry{
URL: d.URL,
TargetPath: d.Path,
TotalBytes: d.TotalBytes,
})
}
return downloads, nil
}
func countDownloads(path, ownerUUID string) (int, error) {
downloads, err := extractDownloads(path, ownerUUID)
if err != nil {
return 0, err
}
return len(downloads), nil
}
// ownsDownload treats empty ProfileUUID as DefaultProfile for backward compat with pre-profile Safari.
func ownsDownload(entryUUID, ownerUUID string) bool {
if entryUUID == "" {
entryUUID = defaultProfileSentinel
}
return entryUUID == ownerUUID
}