9.2 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-001 | 01 | execute | 1 |
|
true |
|
|
Purpose: Users often have several playlist files to import — forcing one-at-a-time selection is tedious. Output: Updated backend methods, regenerated Wails bindings, and updated frontend handler.
<execution_context> @/home/caleb/.config/Claude/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/Claude/get-shit-done/templates/summary.md </execution_context>
@backend/frontendutil/frontendutil.go @backend/playlist/playlist.go @frontend/src/components/playlist-view/playlist-view.ts @frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts @frontend/wailsjs/go/playlist/Service.d.tsFrom backend/frontendutil/frontendutil.go:
func (fe *FrontendUtil) PlaylistFilePicker() (string, error)
// Uses runtime.OpenFileDialog — single file selection
From backend/playlist/playlist.go:
func (s *Service) ImportPlaylist(filePath string) (Summary, error)
// Imports a single M3U/M3U8 file, creates DB entry, emits PlaylistCreated event
type Summary struct {
ID int64 `json:"ID"`
Name string `json:"Name"`
}
var errEmptyFilePath = errors.New("file path cannot be empty")
var errNoFilePaths = errors.New("no file paths provided")
var errUnsupportedFileType = errors.New("unsupported file type")
From Wails runtime API:
func OpenMultipleFilesDialog(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error)
From frontend bindings:
// Current:
export function PlaylistFilePicker(): Promise<string>;
export function ImportPlaylist(arg1: string): Promise<playlist.Summary>;
// After change (auto-generated):
// PlaylistFilePicker(): Promise<Array<string>>;
// ImportPlaylists(arg1: Array<string>): Promise<Array<playlist.Summary>>;
- In
backend/playlist/playlist.go, add a new exported methodImportPlayliststhat accepts a batch of file paths. Place it directly after the existingImportPlaylistmethod (after line 794):
// ImportPlaylists imports multiple playlists from external M3U/M3U8
// files. Each file is imported sequentially using ImportPlaylist.
// Errors from individual imports are collected; partial success is
// possible. Returns the summaries of successfully imported playlists
// and the first error encountered (if any).
func (s *Service) ImportPlaylists(
filePaths []string,
) ([]Summary, error) {
if len(filePaths) == 0 {
return nil, errNoFilePaths
}
summaries := make([]Summary, 0, len(filePaths))
var firstErr error
for _, fp := range filePaths {
summary, err := s.ImportPlaylist(fp)
if err != nil {
s.logger.Warn(
"Failed to import playlist file",
"path", fp,
"err", err,
)
if firstErr == nil {
firstErr = fmt.Errorf(
"import %q failed: %w", fp, err,
)
}
continue
}
summaries = append(summaries, summary)
}
return summaries, firstErr
}
Key design decisions:
- Sequential, NOT parallel — SQLite lock contention avoidance per research.
- Partial success — continues importing remaining files even if one fails.
- Returns first error + all successful summaries so the frontend can show what worked and what didn't.
- Reuses existing
ImportPlaylist— no logic duplication. errNoFilePathssentinel already exists (line 28).
Do NOT modify the existing ImportPlaylist method signature or behavior — it remains available for single-file import internally.
cd /mnt/vault/dev/golang/yellowjacket && go vet ./backend/frontendutil/... ./backend/playlist/...
- PlaylistFilePicker() returns ([]string, error) and uses OpenMultipleFilesDialog
- ImportPlaylists([]string) ([]Summary, error) exists and delegates to ImportPlaylist per file
- go vet passes for both packages
-
In
frontend/src/components/playlist-view/playlist-view.ts, update the imports (around line 15-17):- Change
ImportPlaylisttoImportPlaylistsin the import from@go/playlist/Service
- Change
-
Update
handleImportPlaylistmethod (starting at line 1874). Replace the entire method body:
private handleImportPlaylist = async () => {
try {
const filePaths =
await PlaylistFilePicker();
if (!filePaths || filePaths.length === 0) return;
this.importError = '';
await ImportPlaylists(filePaths);
} catch (err) {
console.error(
'Failed to import playlist:',
err,
);
this.importError =
err instanceof Error
? err.message
: String(err);
setTimeout(() => {
this.importError = '';
}, 6000);
}
};
Key changes:
PlaylistFilePicker()now returnsstring[]— check for empty array instead of falsy string- Call
ImportPlaylists(filePaths)instead ofImportPlaylist(filePath) - Error handling logic stays the same (toast with 6s auto-clear)
- No need to manually refresh — each imported playlist fires
PlaylistCreatedevent which triggers the existing reactive refresh viaPlaylistControllercd /mnt/vault/dev/golang/yellowjacket && wails generate module && cd frontend && npx tsc --noEmit- Wails bindings regenerated with new signatures
FrontendUtil.d.tsshowsPlaylistFilePicker(): Promise<Array<string>>Service.d.tsshowsImportPlaylists(arg1: Array<string>): Promise<Array<playlist.Summary>>- Frontend imports
ImportPlaylists(notImportPlaylist) handleImportPlaylisthandles array of file paths- TypeScript compiles with no errors
<success_criteria>
- Multi-file selection dialog opens when clicking Import
- Backend accepts and processes array of file paths sequentially
- Frontend correctly passes array to new ImportPlaylists binding
- All code compiles and type-checks cleanly </success_criteria>