음성 녹음기 컴포넌트

감사합니다. 말씀하신 내용을 이해한 것 같습니다. 직접 오디오 녹음은 불가능하네요.

현재 CSP 문제로 인해 해당 컴포넌트가 작동하지 않습니다.

content security policy script src 사이트 설정에 https://cdn.jsdelivr.net/를 추가해도 문제가 해결되지 않습니다. 녹음 버튼은 작동하지만, 파일 생성 시 또 다른 JS 오류가 발생합니다:

Refused to create a worker from 'blob:https://discourse.canapin.dev/a2d7c36c-2919-48a4-9ca6-8da59a2b020e' because it violates the following Content Security Policy directive: "worker-src 'self' https://discourse.canapin.dev/assets/ https://discourse.canapin.dev/brotli_asset/ https://discourse.canapin.dev/javascripts/ https://discourse.canapin.dev/plugins/".

음성 기록기가 제 사이트에서 (대부분) 정상 작동하고 있습니다. 제 작업 과정을 보여주는 아래 스크린샷을 참고하세요…

녹음 직후에는 아무것도 클릭할 수 없습니다(하지만 파일이 실제로 녹음되어 게시글에 삽입되었음을 빨간색 밑줄로 확인할 수 있습니다)…

그 후 브라우저의 뒤로 버튼을 클릭하면(업로드 버튼이 반응하지 않기 때문에) 작성기 화면으로 돌아오게 되며, 여기서 파일이 삽입된 것을 볼 수 있고 미리보기 영역에서 재생도 정상적으로 작동합니다 …

그리고 + 주제 만들기 버튼을 클릭한 후, 게시글이 정상적으로 표시됩니다…

그러나 녹음 중지 버튼을 클릭한 직후 콘솔에 이 오류가 발생했습니다…

또한 업로드 버튼을 클릭하면 두 번째 오류가 발생합니다…

마지막으로 취소 버튼을 클릭하면 세 번째 오류가 발생합니다…

이러한 오류에도 불구하고 오디오 클립은 게시글에 정상적으로 표시되고 재생됩니다.

이 Theme Component를 사용하려고 하면서 발생하는 오류를 이해하려고 하고 있습니다.

모달이 닫히지 않는 것을 제외하면, 나머지는 잘 작동하는 것 같습니다.

(모달에서 벗어나기 위해 페이지를 새로고침하면, 파일이 기록되고 올바르게 업로드된 상태로 컴포저에 다시 도달하며, 재생은 가능하지만, 포스트를 저장한 후에야 미리보기에서 확인됩니다.)

지금까지의 가장 중요한 단서…

이것이 가능한 해결책일까요…

1) 모달이 열릴 때 (컴포저에서 마이크 버튼을 클릭한 후)…

2) 업로드 전에 녹음 내용을 확인하기 위해 모달에서 재생 버튼을 클릭할 때…

3) 모달에서 업로드 버튼을 클릭할 때…

Chrome 인스펙터에서 복사한 전체 로그는 다음과 같습니다…

deprecate-shim.js:33 DEPRECATION: Function prototype extensions have been deprecated, please migrate from function(){}.on('foo') to on('foo', function() {}). [deprecation id: function-prototype-extensions.on] See https://deprecations.emberjs.com/v3.x#toc_function-prototype-extensions-on for more details.
(anonymous) @ deprecate-shim.js:33
e @ deprecate-shim.js:10
(anonymous) @ main.js:43
e @ deprecate-shim.js:10
(anonymous) @ main.js:78
e @ deprecate-shim.js:10
require.deprecate @ deprecate-shim.js:17
value @ function.js:139
(anonymous) @ autoplay-media.js:50
e.withPluginApi @ plugin-api.js:2247
initialize @ autoplay-media.js:47
i.initialize @ app.js:173
(anonymous) @ index.js:126
e.each @ dag-map.js:192
e.walk @ dag-map.js:121
e.each @ dag-map.js:66
e.topsort @ dag-map.js:72
_runInitializer @ index.js:138
runInstanceInitializers @ index.js:124
_bootSync @ instance.js:101
didBecomeReady @ application.js:656
p.invoke @ queue.ts:201
p.flush @ queue.ts:98
h.flush @ deferred-action-queues.ts:75
$._end @ index.ts:616
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._ensureInstance @ index.ts:791
$.schedule @ index.ts:384
_ @ index.js:351
waitForDOMReady @ application.js:409
init @ application.js:323
r @ index.js:388
_ @ core_object.js:122
create @ core_object.js:626
(anonymous) @ start-app.js:4
(anonymous) @ discourse-boot.js:20
(anonymous) @ discourse-boot.js:1
deprecated.js:64 Deprecation notice: DButton no longer supports @action as a string. Please refactor to use an closure action instead. [deprecation id: discourse.d-button-action-string]
e.default @ deprecated.js:64
y @ d-button.js:91
createComponent @ base-component-manager.js:31
createComponent @ ember-component-manager.js:50
create @ manager.js:419
create @ glimmer-component-with-deprecated-parent-view.js:11
(anonymous) @ runtime.js:2679
evaluate @ runtime.js:1052
evaluateSyscall @ runtime.js:4263
evaluateInner @ runtime.js:4234
evaluateOuter @ runtime.js:4227
next @ runtime.js:5058
_execute @ runtime.js:5045
execute @ runtime.js:5038
handleException @ runtime.js:4372
handleException @ runtime.js:4580
throw @ runtime.js:4319
evaluate @ runtime.js:2091
_execute @ runtime.js:4306
execute @ runtime.js:4291
rerender @ runtime.js:4606
render @ index.js:6751
(anonymous) @ index.js:7013
Mt @ runtime.js:4139
_renderRoots @ index.js:6996
_renderRootsTransaction @ index.js:7039
_revalidate @ index.js:7072
p.invoke @ queue.ts:201
p.flush @ queue.ts:98
h.flush @ deferred-action-queues.ts:75
$._end @ index.ts:616
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._end @ index.ts:623
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._end @ index.ts:623
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._end @ index.ts:623
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._end @ index.ts:623
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._ensureInstance @ index.ts:791
$.ensureInstance @ index.ts:579
scheduleRevalidate @ index.js:5390
dirtyTag @ validator.js:411
R @ validator.js:486
F @ index.js:497
z @ index.js:533
_set @ index.js:1848
set @ index.js:1790
r @ index.js:930
De @ index.js:1390
Te @ index.js:1385
set @ observable.js:175
_setModel @ composer.js:1000
await in _setModel (async)
open @ composer.js:980
await in open (async)
editPost @ topic.js:611
$._join @ index.ts:646
$.join @ index.ts:362
p @ index.js:157
(anonymous) @ index.js:708
a @ index.js:128
(anonymous) @ index.js:707
(anonymous) @ index.js:666
_sendComponentAction @ widget.js:275
(anonymous) @ widget.js:319
rerenderResult @ widget.js:291
sendWidgetAction @ widget.js:314
click @ button.js:122
(anonymous) @ hooks.js:236
(anonymous) @ hooks.js:202
rerenderResult @ widget.js:291
R @ hooks.js:202
(anonymous) @ hooks.js:236
dispatch @ jquery.js:5135
g.handle @ jquery.js:4939
deprecated.js:64 Deprecation notice: DButton no longer supports @action as a string. Please refactor to use an closure action instead. [deprecation id: discourse.d-button-action-string]
e.default @ deprecated.js:64
_triggerAction @ d-button.js:164
click @ d-button.js:144
deprecated.js:64 [THEME 11 'Voice Recorder'] Deprecation notice: Defining modals using a controller is deprecated. Use the component-based API instead. (modal: audio_upload) [deprecated since Discourse 3.1] [removal in Discourse 3.2] [deprecation id: discourse.modal-controllers] [info: https://meta.discourse.org/t/268057]
e.default @ deprecated.js:64
show @ modal.js:126
e.default @ show-modal.js:36
showAudioUploadModal @ audio-upload-initializer.js:15
send @ action_support.js:20
r @ index.js:388
_triggerAction @ d-button.js:166
click @ d-button.js:144
deprecated.js:64 Deprecation notice: DButton no longer supports @action as a string. Please refactor to use an closure action instead. [deprecation id: discourse.d-button-action-string]
e.default @ deprecated.js:64
y @ d-button.js:91
createComponent @ base-component-manager.js:31
createComponent @ ember-component-manager.js:50
create @ manager.js:419
create @ glimmer-component-with-deprecated-parent-view.js:11
(anonymous) @ runtime.js:2679
evaluate @ runtime.js:1052
evaluateSyscall @ runtime.js:4263
evaluateInner @ runtime.js:4234
evaluateOuter @ runtime.js:4227
next @ runtime.js:5058
_execute @ runtime.js:5045
execute @ runtime.js:5038
handleException @ runtime.js:4372
handleException @ runtime.js:4580
throw @ runtime.js:4319
evaluate @ runtime.js:2091
_execute @ runtime.js:4306
execute @ runtime.js:4291
rerender @ runtime.js:4606
render @ index.js:6751
(anonymous) @ index.js:7013
Mt @ runtime.js:4139
_renderRoots @ index.js:6996
_renderRootsTransaction @ index.js:7039
_revalidate @ index.js:7072
p.invoke @ queue.ts:201
p.flush @ queue.ts:98
h.flush @ deferred-action-queues.ts:75
$._end @ index.ts:616
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._end @ index.ts:623
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._end @ index.ts:623
_boundAutorunEnd @ index.ts:257
Promise.then (async)
n @ platform.ts:28
flush @ index.js:41
$._scheduleAutorun @ index.ts:803
$._ensureInstance @ index.ts:791
$.ensureInstance @ index.ts:579
scheduleRevalidate @ index.js:5390
dirtyTag @ validator.js:411
R @ validator.js:486
setter @ validator.js:702
c @ index.js:3501
close @ modal.js:62
close @ modal.js:177
show @ modal.js:122
e.default @ show-modal.js:36
showAudioUploadModal @ audio-upload-initializer.js:15
send @ action_support.js:20
r @ index.js:388
_triggerAction @ d-button.js:166
click @ d-button.js:144

팁을 주시면 감사하겠습니다!

2개의 좋아요

이 테마 컴포넌트를 수정했습니다.

6개의 좋아요

iPad와 iPhone에서는 여기서부터 더 이상 진행할 수 없습니다:

기억이 맞다면, 이전에도 같은 문제였습니다.

이것은 별개의 문제이며, 아마도 오래전부터 존재해 온 문제일 것입니다. 곧 확인해 보겠습니다.

4개의 좋아요

저도 같은 증상인데, 브라우저에서 발생하는 거예요. 콘솔에서 나온 몇 가지 오류 메시지가 있습니다. 이 프로젝트를 다시 살려주셔서 감사합니다, Angus.

2개의 좋아요

사파리 문제인 것 같습니다. 잠시 후 확인해 보겠습니다.

1개의 좋아요

네, Safari 문제이고 꽤 오래전부터 존재해 왔습니다(사실 이 기능은 Safari에서 아마도 한 번도 제대로 동작한 적이 없을 겁니다).

이 컴포넌트는 이전과 마찬가지로 Chrome과 Firefox에서는 정상적으로 동작합니다. 컴포넌트를 5년 전의 npm 라이브러리 대신 네이티브 JavaScript MediaRecorder API를 사용하도록 추가로 업데이트했지만, 네이티브 MediaRecorder API와 audio 요소를 Safari에서 원활하게 동작하게 하려면 더 시간을 내어 작업해 주실 분이 필요합니다. 해당 특정 작업에 대한 PR을 작성하는 데 기꺼이 도움을 드리겠습니다.

@peter.be @denvergeeks 해당 작업에만 집중해 주실 의향이 있으신지 궁금합니다. 저장소의 유지보수자로 등록해 드리는 것도 기꺼이 환영합니다.

6개의 좋아요

Angus님, 감사합니다. 지금은 이 문제를 더 깊이 파고들 시간이 없습니다.

참고로, 플러그인을 개발할 때 처음에는 MediaRecorder API를 사용해 봤지만, 코덱/포맷 문제 때문에 모든 브라우저에서 녹음과 재생을 지원하는 방법은 없다는 것을 금방 알게 되었습니다. 항상 작동하지 않는 조합이 있기 때문입니다. 이 부분에 꽤 많은 시간을 들였지만, 제가 아는 한 MediaRecorder API를 사용할 때 모든 브라우저를 지원하는 방법은 없습니다. 그래서 mp3 인코더를 사용했습니다.

3개의 좋아요

@peter.be와 @angus 감사합니다. 테스트를 해본 결과, 이 TC가 이제 작동하는 것을 확인했습니다. 다만 예외가 있습니다:

  1. MacOS에서 Safari에서는 작동하지 않습니다 (MacOS에서 Chrome에서는 작동합니다)

  2. iOS에서는 Safari, Chrome, Firefox, Brave 모두에서 작동하지 않습니다

가능한 해결책 중 하나는 다음과 같습니다…

https://stackoverflow.com/questions/76532656/media-recorder-api-browser-compatibility

3개의 좋아요

@denvergeeks 다른 브라우저에서 재생을 테스트해 보셨나요?

저는 테스트했을 때 녹음은 잘 되었는데, 특히 Safari에서 Chrome으로, 또는 그 반대로 전환할 때 항상 재생 문제가 발생했습니다. 정확히 기억이 나지 않지만, Chrome으로 녹음한 파일은 어떤 코덱을 사용하든 Safari에서 재생되지 않았습니다.

하지만 당신은 이 부분을 테스트했고 모든 조합(다른 브라우저 간 녹음 및 재생)에서 잘 작동한다고 말씀하셨죠?

네, @peter.be – 방금 테스트를 해보았는데…

Windows의 Chrome에서 녹음한 파일:

  • MacOS의 Chrome에서 재생됩니다
  • MacOS의 FireFox에서 재생됩니다

  • MacOS의 Safari에서는 재생되지 않습니다
  • iOS의 Chrome에서는 재생되지 않습니다
  • iOS의 Safari에서는 재생되지 않습니다
  • iOS의 FireFox에서는 재생되지 않습니다
  • iOS의 Brave에서는 재생되지 않습니다
1개의 좋아요

맞아요…

그래서 mp3 인코더를 사용하기로 했어요. 단순히 MediaRecorder API만으로는 모든 브라우저를 지원하지 못하거든요.

1개의 좋아요

그러니까… 플러그인은 작동하는데 컴포넌트는 작동하지 않는 상황이 다시 반복된 거군요 :wink:

2개의 좋아요

컴포넌트는 항상 그랬던 것과 동일한 방식으로 작동합니다. 이에 대한 명확한 관심을 고려하여, 이번 주 후반에 사파리 관련 문제를 해결할 시간을 찾아보겠습니다.

4개의 좋아요

자, 이제 이 컴포넌트는

  • 모든 브라우저(아이OS의 Safari, Firefox, Chrome 포함)를 지원합니다.
  • 외부 의존성을 사용하지 않습니다.
  • 최신 Discourse와 함께 작동합니다.
9개의 좋아요

영국 친구들이 말하듯, 정말 훌륭하네요.

잘 작동합니다. 감사합니다.

2개의 좋아요

Disney Zootopia의 신나고 정말 재미있는 GIF

2개의 좋아요