-
Notifications
You must be signed in to change notification settings - Fork 0
feat(workspace): loop tonight's first section from the map #971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seonghobae
wants to merge
81
commits into
develop
Choose a base branch
from
feat/rehearsal-player-first-section-loop
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
81 commits
Select commit
Hold shift + click to select a range
0eeec7b
feat(workspace): loop tonight's first section from the map
seonghobae d329fbc
test(player): require explicit audio and one-shot start authority
seonghobae aa43a84
fix(player): bind starts to fresh audio-authorized requests
seonghobae 58cf249
test(workspace): bind role loop action to local audio
seonghobae de00811
fix(workspace): gate role loop by local audio authority
seonghobae 80265c2
test(workspace): reject duplicate section id selection aliasing
seonghobae 28e82d1
fix(workspace): select loop sections by renderer position
seonghobae 1fb2ad1
test(workspace): revoke loop authority fail closed
seonghobae 6675a83
fix(workspace): stop loop when local audio is revoked
seonghobae 9e8f605
test(workspace): reject malformed loop section entries
seonghobae 705b0f7
fix(workspace): validate loop sections before id lookup
seonghobae 65da84c
test(workspace): preserve paused count-in progress
seonghobae 5a839da
fix(workspace): resume paused count-in before looping
seonghobae eafc121
test(workspace): disable duplicate start during active loop timing
seonghobae 65c73c8
fix(workspace): disable start while loop timing is active
seonghobae 0476b6d
docs: use analysis-engine working directory for Python checks
seonghobae 78527c7
docs(workspace): describe finite non-negative transport guard
seonghobae ff20581
test(workspace): scope timeline range assertion
seonghobae affa8da
test(workspace): restore focused App integration coverage
seonghobae 1fc6317
fix(workspace): structure loop selector label fields
seonghobae 6a58bad
test(workspace): preserve live loop across metadata updates
seonghobae b100551
fix(workspace): preserve live loop on unrelated song updates
seonghobae 646f470
docs: align Python verification working directory
seonghobae 96a191a
test(i18n): name selected-section loop action honestly
seonghobae b4a5a0d
fix(i18n): name selected-section loop action honestly
seonghobae 3de2b62
fix(i18n): localize selected-section loop action
seonghobae 110ca67
test(workspace): follow selected-section loop copy
seonghobae ab660b6
test(i18n): keep rehearsal clock copy honest
seonghobae 139e820
fix(i18n): describe timer-only loop honestly
seonghobae 7617f0d
fix(i18n): localize rehearsal clock honestly
seonghobae c9fb0f6
test(workspace): follow rehearsal clock copy
seonghobae 0512f30
test(player): align loop assertions with shipped next-action copy
seonghobae f5f753d
test(player): verify revocation after the loop advances
seonghobae 7c1b497
test(player): advance the live-loop clock before revocation
seonghobae ad2d219
test(workspace): pin loop section descriptor authority
seonghobae 6b8abe5
fix(workspace): snapshot loop section authority
seonghobae 0c7b293
test(workspace): pin loop picker snapshot authority
seonghobae 3d5ac83
fix(workspace): snapshot song loop windows
seonghobae f420ffe
fix(workspace): render snapshotted loop windows
seonghobae 2a8a96b
docs(changelog): describe map-clock snapshot boundary
seonghobae d8e0c4f
docs(changelog): preserve historical release text
seonghobae 0a8d316
Merge branch 'develop' into feat/rehearsal-player-first-section-loop
seonghobae d2f9a2f
Merge remote-tracking branch 'origin/develop' into HEAD
seonghobae cfcfa1e
fix(desktop): bound rehearsal loop inputs
seonghobae 6de3f55
fix(a11y): announce rehearsal transport status
seonghobae 7a59047
fix(player): restart externally requested loops
seonghobae 2803109
feat(player): play real audio section loops
seonghobae c911b41
fix(player): harden cross-platform audio loop boundaries
seonghobae 5187e03
fix(player): cap long loop boundary timers
seonghobae 6a9c576
test(player): reproduce unusable audio authority start
seonghobae 2ff2615
fix(player): require playable media authority
seonghobae d53758b
test(player): use real media authority in loop tests
seonghobae 8ab7e7d
feat(player): add bounded rehearsal playback rates
seonghobae 9491543
fix(player): share playable audio authority
seonghobae 308c802
Merge real-audio loop authority fix
seonghobae e517a98
fix(player): align loop timing with playback rate
seonghobae 39927fb
feat(player): filter loops by rehearsal role
seonghobae 43698a6
fix(player): preserve role loop selection
seonghobae 2490ce2
feat(player): add keyboard section cue navigation
seonghobae 04396d9
fix(player): follow focused cue during arrow navigation
seonghobae 9c61a80
feat(player): allow manual cue boundary corrections
seonghobae 8a5160a
feat(player): add bounded cue seeking
seonghobae 81efbe3
fix(player): preserve cue editor focus
seonghobae c37e589
feat(player): add safe transport shortcuts
seonghobae dbd15a9
fix(player): ignore repeated transport shortcuts
seonghobae 4d13e56
feat(player): add safe transport shortcuts
seonghobae b8c17fa
fix(player): scope transport shortcuts safely
seonghobae c6fb104
feat(player): allow manual cue boundary corrections
seonghobae e279396
feat(player): filter loops by rehearsal role
seonghobae aa1c36f
fix(player): ignore modified escape shortcuts
seonghobae b8a4e1c
fix(player): preserve duplicate cue identity
seonghobae b96e454
fix(player): preserve cue selection across edits
seonghobae 82ec453
feat(player): play real audio section loops
seonghobae d92ad78
feat(player): add keyboard section cue navigation
seonghobae b8b4241
fix(player): preserve count-in timing across rate changes
seonghobae 826b1df
docs: align agent guidance with playback rate
seonghobae d99c935
test(player): cover repeated count-in rate changes
seonghobae 435cc02
feat(player): add bounded rehearsal playback rates
seonghobae 7c24f59
fix(player): preserve scroll-region Space behavior
seonghobae b7fd151
Merge latest rehearsal-player base
seonghobae e29739f
feat(player): add bounded cue seeking
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -317,6 +317,20 @@ fn lookup_bootstrap_source( | |
| .ok_or_else(|| "Analysis job source was not found. Choose local audio again.".to_string()) | ||
| } | ||
|
|
||
| /// Allow only the already-normalized source file to be served by the asset protocol. | ||
| /// | ||
| /// Security Notes: the path is produced by the native file dialog or by the | ||
| /// validated, app-owned YouTube cache path. The protocol starts with an empty | ||
| /// scope, so this does not expose a directory or accept a path from JavaScript. | ||
| fn allow_audio_source_for_playback<R: Runtime>( | ||
| app: &tauri::AppHandle<R>, | ||
| source: &LocalAudioSourcePayload, | ||
| ) -> Result<(), String> { | ||
| app.asset_protocol_scope() | ||
| .allow_file(&source.source_path) | ||
| .map_err(|_| "Could not prepare the selected audio for playback.".to_string()) | ||
| } | ||
|
Comment on lines
+325
to
+332
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| fn drain_analysis_status_updates( | ||
| state: &AppState, | ||
| app: &tauri::AppHandle<impl Runtime>, | ||
|
|
@@ -643,6 +657,7 @@ fn select_local_audio_source( | |
| .pick_file() | ||
| .ok_or_else(|| "Choose a WAV, MP3, FLAC, or M4A file to start analysis.".to_string())?; | ||
| let source = normalize_local_audio_source(&path)?; | ||
| allow_audio_source_for_playback(&app, &source)?; | ||
| let project_id = next_project_id(&state); | ||
| let project_root = app_owned_root(&app, "projects", &project_id)?; | ||
| let cache_root = app_owned_root(&app, "cache", &project_id)?; | ||
|
|
@@ -712,6 +727,7 @@ async fn import_youtube_url( | |
| if parsed.get("ok").and_then(|v| v.as_bool()) == Some(true) { | ||
| if let Some(metadata) = parsed.get("metadata") { | ||
| let source = youtube_source_from_metadata(metadata, &cache_root)?; | ||
| allow_audio_source_for_playback(&app, &source)?; | ||
|
|
||
| let summary = ProjectBootstrapSummaryPayload { | ||
| project_id, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
apps/desktop/src/features/workspace/RehearsalPlayer.audioAuthority.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import { render, screen } from "@testing-library/react"; | ||
| import { createDemoRehearsalSong } from "@bandscope/shared-types"; | ||
| import { afterEach, describe, expect, it, vi } from "vitest"; | ||
| import { RehearsalPlayer } from "./RehearsalPlayer"; | ||
|
|
||
| const originalTauriInternals = Object.getOwnPropertyDescriptor( | ||
| window, | ||
| "__TAURI_INTERNALS__", | ||
| ); | ||
|
|
||
| describe("RehearsalPlayer audio authority", () => { | ||
| afterEach(() => { | ||
| vi.restoreAllMocks(); | ||
| if (originalTauriInternals) { | ||
| Object.defineProperty(window, "__TAURI_INTERNALS__", originalTauriInternals); | ||
| } else { | ||
| delete (window as Window & { __TAURI_INTERNALS__?: unknown }) | ||
| .__TAURI_INTERNALS__; | ||
| } | ||
| }); | ||
|
|
||
| it("refuses to start when local-audio metadata has no playable asset URL", () => { | ||
| const song = createDemoRehearsalSong(); | ||
|
|
||
| render( | ||
| <RehearsalPlayer | ||
| song={song} | ||
| hasLocalAudio={true} | ||
| audioSourcePath="browser://selected-audio" | ||
| startNonce={1} | ||
| />, | ||
| ); | ||
|
|
||
| expect( | ||
| screen.getByRole("button", { name: /start the count-in/i }), | ||
| ).toBeDisabled(); | ||
| expect( | ||
| screen.getByTestId("rehearsal-loop-next-action").textContent, | ||
| ).not.toMatch(/count in 4 beats/i); | ||
| }); | ||
|
|
||
| it("reports native asset conversion failures instead of presenting missing-audio copy", () => { | ||
| Object.defineProperty(window, "__TAURI_INTERNALS__", { | ||
| configurable: true, | ||
| value: { | ||
| convertFileSrc: () => { | ||
| throw new Error("asset conversion failed"); | ||
| }, | ||
| }, | ||
| }); | ||
| const song = createDemoRehearsalSong(); | ||
|
|
||
| render( | ||
| <RehearsalPlayer | ||
| song={song} | ||
| hasLocalAudio={true} | ||
| audioSourcePath="/Users/test/Music/late-night-set.wav" | ||
| />, | ||
| ); | ||
|
|
||
| expect( | ||
| screen.getByRole("button", { name: /start the count-in/i }), | ||
| ).toBeDisabled(); | ||
| expect(screen.getByRole("alert").textContent).toMatch( | ||
| /could not play this local audio/i, | ||
| ); | ||
| }); | ||
| }); |
39 changes: 39 additions & 0 deletions
39
apps/desktop/src/features/workspace/RehearsalPlayer.descriptor-authority.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { render, screen } from "@testing-library/react"; | ||
| import { createDemoRehearsalSong } from "@bandscope/shared-types"; | ||
| import { describe, expect, it } from "vitest"; | ||
| import { RehearsalPlayer } from "./RehearsalPlayer"; | ||
|
|
||
| describe("RehearsalPlayer descriptor authority", () => { | ||
| it("renders the admitted section snapshot instead of Proxy get values", () => { | ||
| const song = createDemoRehearsalSong(); | ||
| const section = song.sections[0]!; | ||
| const expectedLabel = section.label; | ||
| const expectedStart = section.timeRange.start; | ||
| const expectedEnd = section.timeRange.end; | ||
| song.sections = [ | ||
| new Proxy(section, { | ||
| get(target, property, receiver) { | ||
| if (property === "id") { | ||
| return "proxy-injected-section"; | ||
| } | ||
| if (property === "label") { | ||
| return "outro"; | ||
| } | ||
| if (property === "timeRange") { | ||
| return { start: 90, end: 100 }; | ||
| } | ||
| return Reflect.get(target, property, receiver); | ||
| } | ||
| }) | ||
| ]; | ||
|
|
||
| render(<RehearsalPlayer song={song} hasLocalAudio={true} />); | ||
|
|
||
| expect( | ||
| screen.getByRole("button", { | ||
| name: new RegExp(`${expectedLabel}.*0:${String(expectedStart).padStart(2, "0")}.*0:${String(expectedEnd).padStart(2, "0")}`, "i") | ||
| }) | ||
| ).toBeTruthy(); | ||
| expect(screen.queryByRole("button", { name: /outro.*1:30.*1:40/i })).toBeNull(); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.