| Age | Commit message (Collapse) | Author |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
query
Previously ListEpisodes requested per-feed episodes with the full limit
from every feed, concatenated them, and then sliced the resulting slice
in memory. This silently skipped episodes from deeper pages because the
per-feed DB limit never returned them.
Fix: Add a new repository method ListEpisodesByFeedIDsWithStatus that
takes a slice of feed IDs and applies LIMIT/OFFSET globally in a single
SQL query (IN (...)). The service builds the feed ID list and delegates
pagination to the database instead of emulating it in memory.
Files changed:
- internal/repository/podcast.go: add ListEpisodesByFeedIDsWithStatus
- internal/repository/podcast_repo.go: extend PodcastRepo interface
- internal/repository/mock.go: add mock implementation
- internal/service/podcast.go: replace per-feed loop with new method
- internal/service/podcast_test.go: add tests for ListEpisodes
- internal/repository/podcast_test.go: add integration tests for new repo method
- internal/api/handlers_test.go: add mockPingStore method
|
|
- DB: add consecutive_failures and next_check_at to podcast_feeds
- model: add ConsecutiveFailures and NextCheckAt to PodcastFeed struct
- repository: update CRUD queries to handle new columns and
ListFeedsNeedingCheck to skip feeds with future next_check_at
- service: update CheckFeeds to use new query, update checkFeed to
reset counters on success and increment exponential backoff on failure
- tests: adapt mock signatures, add backoff integration test
|
|
|
|
Extract PodcastBrowser interface and podcastBrowseService implementation
to move podcast-specific grid augmentation out of browseService.BrowseSet.
- Remove PodcastRepo from BrowseServiceStore and MediaServiceStore.
- browseService now delegates podcast set augmentation to an injected
PodcastBrowser strategy (kept optional/nil-safe).
- Add NewMediaServiceWithPodcastBrowser to wire a real PodcastBrowser
(backed by PodcastRepo) in production while keeping the plain
NewMediaService for existing callers/tests.
- Update main.go to instantiate NewPodcastBrowseService and inject it.
- Update browse_test.go to test PodcastBrowser-augmented paths through
the new constructor signature.
This keeps BrowseSet focused on files and folders only and removes the
podcast dependency from the browse contract.
|
|
|
|
|
|
|
|
|
|
|
|
- Fix boolean scanning from SQLite INTEGER columns (intToBool helper)
- Fix BrowseSet LIMIT 0 returning zero episodes (use 1000 instead)
- Fix checkFeed double-fetch by parsing from resp.Body directly
+ Add ParseFeedReader for body reuse
- Fix file handle leak in DownloadEpisode (explicit Close before ImportMediaFile)
- Fix incomplete rollback in DownloadEpisode (remove file + delete media row)
- Fix 204 No Content handler writing 'null' body
- Fix DownloadCoverImage to accept *http.Client with timeout
- Deduplicate uniqueFilename into shared internal/service/filename.go
- Wire frontend renderPodcastEpisodes into renderBrowse from API data
- Refactor podcasts.js: remove extra API call, fix toggle toast, remove duplicate toast
- Add Config.PodcastCheckMinutes + PODCAST_CHECK_INTERVAL_MINUTES env var
- Start background CheckFeeds goroutine in main.go with ticker
- Update NewPodcastService to accept checkInterval parameter
- Log errors from CheckFeeds and episode creation instead of silently discarding
|
|
|
|
Refactored the following functions per CLAUDE.md (50+ line limit):
- internal/scanner/scanner.go: scanSet (192 → 56 lines)
Extracted: ensureSet, loadExistingMedia, gatherCoverImages,
thumbnailForVideo, thumbnailForImage, buildThumbnailPath,
processNewFile, updateAudioThumbnails
- internal/repository/migrate.go: Migrate (134 → 23 lines)
Extracted: enableForeignKeys, execSchema; split schema into
tablesSchema and indexesSchema constants
- internal/config.go: LoadConfig (85 → 28 lines)
Extracted: defaultConfig, loadNumericSettings, loadStringSettings,
loadLogLevel, loadSecureCookies; added validLogLevels variable
- cmd/mediaplayer/main.go: run / runWithSignal (114 → 26 / 26 lines)
Extracted: parseVersionFlag, buildLogger, wireDeps, runServer,
ensureSignalChannel, shutdownGracefully; introduced appDeps struct
- internal/api/server.go: routes (96 → 8 lines)
Extracted per route group: routesPublic, routesSharePublic,
routesStatic, routesHTML, routesAuth, routesSets, routesMedia,
routesNotes, routesProgress, routesShares, routesAdmin
Added helpers: requireSession, requireAdmin, publicMethod
- internal/service/browse.go: BrowseSet (85 → 26 lines)
Extracted: prefixForParent, classifyMediaPath, buildFolderMap,
folderHasCover, buildFolders
All public interfaces remain unchanged. Tests pass: go test ./... -race -cover
|
|
|
|
|
|
Extract accessHelper and split mediaService into:
- browseService (read-only browsing, streaming, thumbnails)
- writeService (upload, soft-delete, restore)
- shareService (share links)
- tagService (tagging)
- favService (favorites)
- noteService (notes)
Split adminService into:
- trashService (list deleted media)
- scanService (trigger rescan, scan progress)
- userAdminService (create/list/delete users)
- permissionAdminService (grant/revoke/list permissions)
Add repository sub-interfaces for each service.
Add negative tests for share sub-service.
|
|
|
|
- Protect adminService scan state (cancel func + progress pointer) with sync.Mutex.
- Allocate fresh ScanProgress per trigger and pass it to the scanner,
eliminating races on the previously shared progress struct.
- Cancel previous scan context before starting a new one.
- Add tests for cancellation, fresh progress per scan, concurrent triggers,
and empty progress when never started.
- Fix race-prone tests by polling Running==true before waiting for completion.
|
|
- Add Cache-Control: no-cache headers to thumbnail and cover endpoints
(GET /api/media/{id}/thumbnail, GET /api/sets/{id}/cover,
GET /s/{token}/thumbnail) so browsers revalidate instead of
serving stale cached images after regeneration.
- Add frontend cache-busting via ?t=Date.now() after folder cover
regeneration so the browser fetches the newly overwritten image.
- Replace toolbar filters with inline search syntax (min:, max:,
tag:, like:, type:, sort:, minsize:, maxsize:) for faster filtering.
- Remove dedicated toolbar and advanced filter panel; consolidate
all filtering into the search bar.
- Expand filter state to support filesize_min/filesize_max.
|
|
|
|
Introduce service.AuthService interface with Bootstrap and Login methods,
a concrete authService implementation, and a MockAuthService for testing.
Wire AuthService into api.Server and update cmd/mediaplayer/main.go to use it.
This removes direct store access from handleBootstrap and handleLogin,
fixing the DIP violation. Sentinel errors (ErrAlreadyBootstrapped,
ErrInvalidCredentials) are added to the service package so the API layer
can map them to the correct HTTP status codes without leaking DB details.
Files created:
- internal/service/auth.go
Files modified:
- internal/service/service.go
- internal/service/media.go
- internal/service/mock.go
- internal/repository/repository.go
- internal/repository/mock.go
- internal/api/server.go
- internal/api/handlers_auth.go
- internal/api/handlers_test.go
- internal/api/handlers_more_test.go
- cmd/mediaplayer/main.go
|
|
|
|
navigation
Backend:
- Add UpdateMediaThumbnail to MediaRepo for efficient thumbnail-only updates
- Update ScannerStore interface to include thumbnail update capability
- Update mock implementations across repository and API layers for interface changes
- Enhance scanner to detect image files in ancestor directories for audio covers
- Add background goroutine for async rescan to avoid HTTP timeout
Frontend:
- Fix hjkl cursor keys to navigate grid properly
- Implement ancestor directory cover image lookup for audiobooks
Infrastructure:
- Add start.sh helper script for quick server startup during development
|
|
|
|
|
|
filters, permission scoping)
|
|
Changes:
- MediaService.ListMedia now accepts userID and filters by allowed sets
for non-admins via AllowedSetIDs in repository.MediaFilter.
- Handlers pass userID into ListMedia; API returns 403 for forbidden.
- Added verifyModifyAccess and verifySetModifyAccess helpers so only
owners/admins can upload, soft-delete, restore, and regenerate
thumbnails/covers; viewers are blocked.
- GetMediaDetail, ToggleFavorite, AssignTag, RemoveTag, notes, and
shares now consistently verifyAccess before proceeding.
- Handlers handle ErrForbidden with 403 for soft-delete and restore.
- Added negative tests proving viewers cannot mutate and unauthorized
users cannot access/detail/tag/note/favorite/share inaccessible media.
|
|
- Update go.mod module path
- Replace all internal imports from github.com/paul/kiss-media-player to codeberg.org/snonux/play
- Run go mod tidy and gofmt -w .
|
|
Repository methods that perform single-row queries now return (nil,nil)
instead of leaking sql.ErrNoRows when a row is missing. This aligns with
service-layer expectations (e.g. GetMediaDetail, ValidateSession,
UpdateProgress, AssignTag, access checks) so normal missing data does
not break app flows.
Files changed:
- internal/repository/media.go, user.go, set.go, set_permission.go,
tag.go, note.go, playback_progress.go, playback_accumulator.go,
session.go, share.go
- internal/repository/sqlite_test.go (updated assertions)
- internal/repository/sqlite_no_rows_test.go (new focused repository tests)
- internal/service/no_rows_test.go (new focused service tests)
|
|
|
|
|
|
|
|
|
|
handlers, and bootstrap flow (m9)
|
|
implementations with :memory: table-driven tests (task l9)
|
|
- Initialize go.mod (github.com/paul/kiss-media-player)
- Add internal/version.go with const Version
- Add internal/config.go with env-based Config struct and validation
(PORT, MEDIA_ROOT, DB_PATH, MAX_UPLOAD_SIZE_MB, SESSION_TIMEOUT_HOURS,
GC_INTERVAL_MINUTES, SHARE_DEFAULT_EXPIRY_DAYS, LOG_LEVEL)
- Add table-driven config validation tests (defaults, overrides, invalid values)
- Add cmd/mediaplayer/main.go with -version flag
- Create directory scaffold: internal/{model,repository,scanner,probe,thumb,
clock,auth,service,api,setassign}, web/{css,js}, k8s
- Add .gitignore for binaries and data.db
- Refactor env parsing into envInt/envString helpers per go-best-practices
|