Commit graph

117 commits

Author SHA1 Message Date
Thorsten Bus 94170857b6 feat(export): render uploaded slide images as slide content, keep background/keyvisual as background media action
- New SlideMediaElementBuilder converts FOREGROUND (uploaded) image media
  actions into slide-content fill.media elements placed at the top of the
  slide content, so uploaded information/moderation/sermon images render as
  real content rather than a media-layer action.
- Service background and key-visual images remain BACKGROUND media actions on
  the bottom layer; song text and name-tag stay text content elements.
- Wire the builder into ProExportService and ProBundleExportService (after
  generate) and split PlaylistExportService::writeProFile into
  generate -> inject -> write via ProFileWriter so the playlist path applies
  the same rule.
- Parser library unchanged.
- Migrate export tests to the combined rule via a shared InspectsSlideFillMedia
  support trait and add SlideMediaElementTest.
2026-07-07 22:15:12 +02:00
Thorsten Bus fdabaadb89 fix(export): preserve probundle extension and attach slide macros
- store() persisted uploads under Laravel's MIME-guessed extension,
  turning a .probundle (a ZIP) into .zip; embedExportProFile then
  branched on the stored path, missed the bundle branch and embedded
  the raw ZIP as a broken .pro (ProPresenter showed an empty slide).
  Store with the real client extension and derive the bundle type from
  the original filename, treating .zip as a bundle for legacy uploads.
- Skip export .pro/.probundle files that parse to zero content slides,
  recording a German warning instead of injecting an empty slide.
- Attach position macros (first_slide/last_slide/all_slides) per slide
  for information/moderation/sermon parts in the agenda and legacy paths.
- Cover both with ExportProFileProbundleEmbedTest and PlaylistSermonMacroTest.
2026-07-06 11:19:56 +02:00
Thorsten Bus 27461b7c28 test: assert Abspann loop + dissolve on live parser
Bump the propresenter parser to dev-master (20fe32a), which brings the
background-layer and nametag-subtitle features onto the consumed
bussnet/propresenter7-php-api branch and resolves the
Slide::hasBackgroundMedia() regression. Strengthen AbspannExportTest to
assert the now-live 10s auto-advance loop (completion AFTER_TIME == 10,
target FIRST) and the Dissolve transition (renderId EC52A828-...).
2026-07-06 10:54:27 +02:00
Thorsten Bus 2a68d098a2 Merge branch 'feature/playlist-arrangement' into master
Honor per-service arrangement selection in ProPresenter export:
- .pro selected_arrangement (protobuf field 10) via existing parser API
- .proplaylist item arrangement_name (field 5), import-stable by name
- buildGroups emits union of all arrangements' sections

Conflict in PlaylistExportService.php (agenda song branch) resolved by
applying arrangement selection inside master's new else-block; legacy
path and ProExport/ProBundle changes auto-merged. All 63 export tests
pass against the hot-patched vendor.
2026-07-06 09:18:35 +02:00
Thorsten Bus ea4b868650 fix(test): make song-sections rollback guard test resilient to new migrations 2026-07-06 01:31:20 +02:00
Thorsten Bus 1df04c70e0 feat(export): keyword-matched custom export files per schedule item 2026-07-06 01:21:45 +02:00
Thorsten Bus e62ea5bf0a fix(export): align writeProFile test doubles with new options parameter
Update anonymous PlaylistExportService subclasses in export tests to match
the production writeProFile signature (added $options param) and to inject
the now-required MacroResolutionService constructor dependency.
2026-07-06 01:07:05 +02:00
Thorsten Bus 1c811a0550 feat(export): append looping Abspann presentation with keyvisual and info slides 2026-07-06 00:41:49 +02:00
Thorsten Bus e47299f90a feat(export): role+name nametag titles and combined keyvisual+nametag sermon intro 2026-07-05 23:20:35 +02:00
Thorsten Bus 4e548bbeed feat(export): honor per-service arrangement
ProPresenter export ignored service_songs.song_arrangement_id and
always used the default/is_default arrangement. Now the chosen
arrangement is pre-selected while ALL arrangements stay in the file,
so users can still switch in ProPresenter.

Import-stable, by NAME (avoids the fragile playlist arrangement UUID
that breaks on import):
- .pro: set selected_arrangement (protobuf field 10) to the chosen
  arrangement via Song::setSelectedArrangementUuid(getArrangementByName).
- .proplaylist: set the presentation item arrangement_name (field 5).

buildGroups now emits the UNION of sections across all arrangements
(selected first, in play order, with first/last-slide macro anchoring;
other arrangements' sections appended without position macros) so every
arrangement's group-name refs resolve instead of being silently dropped.

No parser package changes; installed public API only. Library/no-service
context keeps default-arrangement behaviour unchanged.
2026-07-05 23:16:59 +02:00
Thorsten Bus e849d43b74 feat(macros): add 'abspann' macro assignment part type
Add new part type 'abspann' (credits/Abspann presentation) to the macro
assignment enum, validation and UI.

- Relax part_type from enum to plain string on macro_assignments,
  service_macro_overrides and service_macro_assignments so 'abspann' (and
  any controller-validated value) is accepted across MySQL and SQLite.
- Add 'abspann' to Rule::in validation in MacroAssignmentController and
  ServiceMacroOverrideController.
- Include 'abspann' in the per-service macros_per_part list in
  ServiceController so the override UI exposes it.
- Add 'abspann' to the parts array in Settings/MacroAssignments.vue
  (by_label stays song-only; abspann offers all/first/last slide).
2026-07-05 23:05:57 +02:00
Thorsten Bus 7a29a21822 feat: confirm-state for service songs + service-edit UX fixes
- add explicit confirmed_at assignment state (red/amber/green) with confirm/unconfirm endpoints
- clone leader arrangement before opening dialog to avoid flicker
- agenda slide strip: show all previews, drag-reorder, number badges, auto-hide uploader
- fix info-slide expire-date saving via axios (was rendering raw JSON modal)
- point top-left logo to / instead of /dashboard
2026-06-21 11:53:55 +02:00
Thorsten Bus 42b8b5f428 fix(export): copyright/blank slides, bundle-relative media, probundle injection, song links
- populate COPYRIGHT (title/author/copyright/CCLI) + blank slides on every song; songHasContent ignores locked sections
- foreground info/moderation images now bundle-relative (fixes blank images)
- pre-added .probundle injection: Zip64-fix + verbatim .pro extraction (fixes empty bundle)
- nametag subtitle split (text + subtitle); smaller non-bold render
- skip songs with no content slides at export with German warning
- link service agenda songs to SongDB edit modal via #song-<id>
- allow CCLI import of metadata-only songs (no lyric sections)
- expose has_content_slides on service songs; show "Keine Inhaltsfolien"
2026-06-21 09:58:55 +02:00
Thorsten Bus d5f3990f3b fix: login redirect, nametag/worship resolution, macros, export headers & probundle
- Post-login (OAuth + dev-login) now redirects to the next upcoming
  service's edit page instead of /dashboard, mirroring the GET / route.
- NameTagResolver now reads the real ChurchTools `responsible` shape
  (persons[].person.title) and resolves moderator/preacher/worship-leader
  by responsible ROLE ([Moderation]/[Predigt]/[Lobpreis]). This fixes
  missing name slides and makes the worship-leader arrangement trigger
  (e.g. service 12 → "Benedikt Hardt" / "Jennifer Schneider").
- NameTagSlideBuilder no longer silently drops the name slide when the
  configured macro id points to a missing macro; it emits the slide
  without a macro instead.
- Song export: the "first slide" / "last slide" macro now applies only to
  the song's very first/last slide (global slide index across all
  sections), not the first slide of every section.
- Export "headlines" for content-less agenda items are now emitted as
  proper ProPresenter playlist HEADER items instead of text presentations.
- Prefix/postfix export files now also accept .probundle (unzipped: inner
  .pro + media embedded) in addition to .pro, both for upload validation
  and export injection.

Full suite green (587 passed).
2026-06-01 22:17:31 +02:00
Thorsten Bus e33418f716 feat: song pre/postfix, settings overhaul, export & schedule fixes
Resolves a batch of bugs and feature requests across songs, services,
settings and export:

Songs & sections
- Every song now carries permanent, empty, locked PREFIX (COPYRIGHT) and
  POSTFIX (BLANK) sections, deduplicated on import; locked sections cannot
  be edited or deleted via UI or API.
- Song edit modal: explicit Speichern/Schließen with dirty-tracking,
  editable section headline (combobox + custom values), and a fix for the
  419 CSRF errors after CCLI "Importieren & Bearbeiten" (token read fresh
  per request).
- CCLI bookmarklet "Importieren & Bearbeiten" now opens the edit dialog.

Service schedule & arrangements
- Fixed assigned songs showing no sections (slides loaded for all
  arrangements, not just the default).
- Added "Song entfernen / neu zuordnen" to reassign an assigned song.
- Worship-leader arrangement is created/selected lazily when the
  arrangement dialog opens (only when not user-overridden); the leader is
  resolved from the "Lobpreis" agenda item, and manual create/clone names
  are prefixed with the leader name.

Navigation
- "/" redirects to the next upcoming service's edit page (or the list).
- Service titles link to the edit page.

Settings
- Renamed "Makro-Import"/"Label-Import" menu items; fixed drag-and-drop
  imports (were downloading the dropped file); added label-import hint;
  made the panel scrollable.
- Nametag now uses a single MacroPicker; added song prefix/postfix label
  defaults (COPYRIGHT #24B34C / BLANK #000000); new "Export-Dateien" menu
  to upload prefix/postfix .pro files added to every export.

Export
- Filenames/playlist names are date-first ("YYYY-MM-DD <Title>").
- Keyvisual slide only for the first content-less item after real content;
  all other content-less items render as headlines.
- New "Vorschau herunterladen" for non-finalized services (filename and
  import name prefixed "Vorschau" with export timestamp).
- Uploaded prefix/postfix .pro files wrap every export.

Tests updated to the new behavior; full suite green (569 passed).
2026-06-01 08:56:20 +02:00
Thorsten Bus ff3484466b fix(songs): resolve seven song/service editing bugs
- CCLI import: group lyrics into 2-line slides (no blank line per line)
- Add-section: searchable label combobox with create-new option
- Service edit: show current global key-visual/background default live
- Assign dialog: prefill+open search, SongSelect link by CCLI nr/name
- "Auf SongSelect suchen" now also opens the CCLI import dialog
- SongDB: mark empty songs "Ohne Inhalt", default-on content filter
- Translation paste: strip section-mark lines so line mapping holds
2026-05-31 21:39:44 +02:00
Thorsten Bus ae42b48753 feat(songs): per-song sections + section editing; fix CCLI import bugs
Refactor lyric storage so each song owns its sections instead of sharing
global labels. Adds song_sections (per song+label) owning song_slides;
labels stay global ProPresenter group tags (name/color/macro). Arrangements
now reference sections, so editing/importing one song no longer corrupts
others that share a label name.

- New: song_sections table + migration with safe backfill; SongSection,
  SongArrangementSection models; SongSectionController (edit/add/delete
  sections, immediate persistence) wired into SongEditModal.
- Refactor writers/readers: CcliImport, ProImport, SongService,
  ArrangementController, SongController, ProExport, PDF, Translation
  (translation reset now section-scoped), CCLI pairing.
- CCLI import fixes: parse SongSelect copy-icon format (German "Vers"
  abbrev + trailing author), fill empty CTS-synced songs instead of
  blocking as duplicate, distinct label colors per section kind,
  import&edit/existing-song open the edit modal (no 404/405), teleport
  paste dialog above assign dialog, preview shows section content,
  correct SongSelect search URL, copy-icon instructions.
- Bookmarklet clicks #generalCopyLyricsButton and captures clipboard;
  serves correct host from request.
- Export: embed key-visual/background under fixed bundle-relative names.
- Tests updated for the section model; new section + isolation coverage.
2026-05-31 14:45:47 +02:00
Thorsten Bus e95abbc1e6 feat(export): sermon sequence + moderator injection 2026-05-31 06:30:25 +02:00
Thorsten Bus e2d6d813de test(e2e): nametag name override fields + namenseinblender settings 2026-05-31 05:03:55 +02:00
Thorsten Bus 4606bb26d6 fix(e2e): correct auth path + strict selector for Namenseinblender 2026-05-31 05:01:38 +02:00
Thorsten Bus c544f1db60 test: full playlist export assertions 2026-05-31 04:32:47 +02:00
Thorsten Bus 45221ced32 test(e2e): image upload + detail page 2026-05-31 04:20:54 +02:00
Thorsten Bus edceebb2f8 feat(service): finalize snapshot + sync protection 2026-05-31 02:33:32 +02:00
Thorsten Bus 929bda2018 feat(export): sermon sequence + moderator injection 2026-05-31 01:58:08 +02:00
Thorsten Bus bb877d16c6 feat(export): nametag slide builder 2026-05-31 00:48:46 +02:00
Thorsten Bus a19c967594 feat(export): keyvisual fallback slides 2026-05-31 00:43:59 +02:00
Thorsten Bus 196657b52b feat(export): background layer on song/sermon slides 2026-05-31 00:37:23 +02:00
Thorsten Bus d2193bb3b2 feat(service): moderator/preacher name resolution 2026-05-31 00:20:33 +02:00
Thorsten Bus b31f21959f feat(service): keyvisual/background upload + scope choice 2026-05-31 00:15:08 +02:00
Thorsten Bus 1ce30b76e3 feat(service): lazy image resolver 2026-05-31 00:08:50 +02:00
Thorsten Bus 38e79553eb feat(settings): namenseinblender macro + default image settings 2026-05-31 00:04:49 +02:00
Thorsten Bus 6061e4c4dd feat(service): add image columns and overrides
Enable storage-backed key visuals and background images plus service-specific moderator and preacher name overrides.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 00:01:24 +02:00
Thorsten Bus 73a523d0e1 feat(images): cover-fit conversion mode 2026-05-30 23:56:05 +02:00
Thorsten Bus 03fdfac3d3 test(e2e): add CCLI paste import, bookmarklet, and translation pairing e2e tests 2026-05-11 10:36:44 +02:00
Thorsten Bus 35d3298251 feat(ccli): add CcliPasteController endpoints
- POST /api/ccli/preview: parse-only endpoint (no DB writes)
- POST /api/songs/import-from-ccli-paste: 3 modes (create / pair-with-song / assign-to-service-song)
- GET /songs/import-from-ccli-paste: Inertia page with base64 bookmarklet prefill
- Routes guarded by auth:sanctum + throttle:30,1 (API); auth + web stack (web)
- Maps DuplicateCcliSongException to 409 with existing_song_id and edit_url
- Pest tests (10 cases, 63 assertions): preview, all 3 import modes, 409 dup, 422 errors, unauth, prefill happy/error, login redirect
2026-05-11 09:23:11 +02:00
Thorsten Bus cd0a72124d feat(ccli): serve CCLI bookmarklet JS 2026-05-10 19:34:30 +02:00
Thorsten Bus cd44d6289c feat(ccli): add CcliTranslationPairingService
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-10 19:02:10 +02:00
Thorsten Bus 091e00f255 feat(ccli): add CcliImportService for song upsert
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-10 18:56:38 +02:00
Thorsten Bus e4e5df912e fix(ccli): parse common CCLI metadata 2026-05-10 18:54:33 +02:00
Thorsten Bus 9412ca71c9 feat(ccli): implement CcliPasteParser parsing logic 2026-05-10 18:49:18 +02:00
Thorsten Bus 55a3ea3df8 feat(ccli): scaffold CcliPasteParser service 2026-05-10 18:42:21 +02:00
Thorsten Bus 85608f774d feat(settings): add default translation language setting 2026-05-10 18:38:14 +02:00
Thorsten Bus 73b7afcc2f feat(songs): track CCLI import metadata on songs table 2026-05-10 18:33:24 +02:00
Thorsten Bus 5c590eda9e feat(ccli): add section-label constants and language mapping 2026-05-10 18:28:18 +02:00
Thorsten Bus 02de6b03c0 test(ccli): add fixture corpus for CCLI paste parser
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-10 18:24:29 +02:00
Thorsten Bus 6ce5b6e018 feat(controllers): add macro/label import + global assignment + service override controllers and routes
- MacroImportController + LabelImportController: POST endpoints accepting uploaded .bin files,
  delegating to MacrosImportService / LabelsImportService and returning import stats / warnings as JSON.
  Generic German 422 error if parser rejects the file.
- MacroAssignmentController: index renders Settings Inertia page with assignments / macros / labels /
  collections / last-import metadata. store/update/destroy/reorder for global MacroAssignment rows.
- ServiceMacroOverrideController: store snapshots all matching global MacroAssignments into
  service-specific rows when a service opts to override; destroy removes both override and
  service-specific assignments. storeAssignment / updateAssignment / destroyAssignment manage the
  per-service rows directly.
- routes/web.php: 12 new named routes inside the auth middleware group; reorder route placed before
  {macroAssignment} parameter route to avoid capture conflict.
- Tests: 19 new Pest tests across 4 feature files (54 assertions). Full suite 376 passed.
2026-05-03 23:17:04 +02:00
Thorsten Bus cef247336e feat(export): use MacroResolutionService in ProExportService for flexible macro injection 2026-05-03 23:08:22 +02:00
Thorsten Bus 81b2a9caf6 feat(services): add LabelsImportService, MacrosImportService, MacroResolutionService 2026-05-03 23:03:32 +02:00
Thorsten Bus bdbf0c65e3 refactor(php): rename SongGroup references throughout controllers/services/tests
Replace all SongGroup/SongArrangementGroup model usages with Label/SongArrangementLabel
after the schema migration to global labels. Updates 12 app files and 11 test files:

- SongService: createDefaultGroups now finds-or-creates global Labels by name
- ArrangementController: validates label_id (labels are global, no song-ownership)
- ProImportService: imports groups as Labels (firstOrCreate by name); does not
  overwrite existing label colors per spec
- ProExportService/SongPdfController/TranslationService/etc: traverse via
  arrangements -> arrangementLabels -> label -> songSlides chain
- All test factories and assertions adapted to label-based schema
2026-05-03 22:55:02 +02:00
Thorsten Bus a1612dc3ef feat(support): add MacroColorConverter utility 2026-05-03 22:31:44 +02:00