criado repositório jhonny-editor no Gitea adicionado script admin/deploy.command com commit automático atualizado admin/DEV-NOTES.md com template limpo Resumo: - 48 arquivos alterados - 26 novos - 19 modificados - 3 removidos 22 files changed, 658 insertions(+), 568 deletions(-) Arquivos: - AGENTS.md - admin/DEV-NOTES.md - admin/OpenCut.command - admin/commit.command - admin/deploy.command - admin/update.command - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css - code/engine/ARQUITETURA.md - code/engine/arquitetura/README.md - code/engine/gerar_relatorio_timeline.py - code/engine/integracoes/apple_speech/apple_speech_transcriber.swift - code/engine/integracoes/apple_speech/provider_de_transcricao_apple.py - code/engine/integracoes/midia/__init__.py - code/engine/integracoes/whisper/provider_de_transcricao_local.py - code/engine/scanner/__init__.py - code/engine/scanner/coordenacao/__init__.py - code/engine/scanner/descoberta/__init__.py - code/engine/scanner/modelos.py - code/engine/scanner/transcricao_da_timeline.py - code/src/tools/discovery.ts - :memory:.ses - admin/Jhonny.command - admin/inativos/OpenCut.command - admin/inativos/update.command - code/docs/glossario-analise-emocional.md - code/docs/levantamento-apple-vision-e-apple-intelligence.md - code/docs/levantamento-ferramentas-analise-visual-local.md - code/engine/arquitetura/biblioteca-inteligente-de-videos.md - code/engine/executar_scanner.py - code/engine/integracoes/huggingface/ - code/engine/integracoes/midia/extracao_de_metadados.py - code/engine/integracoes/visual/ - code/engine/requirements-visual.txt - code/engine/scanner/configuracao_visual.py - code/engine/scanner/descoberta/descoberta_de_arquivos.py - code/engine/scanner/metadados.py - code/engine/scanner/relatorio_visual.py - code/engine/scanner/retakes/ - code/engine/scanner/visual.py - code/engine/testes/test_analise_visual_local.py - code/engine/testes/test_arquivos_e_metadados.py - code/engine/testes/test_provider_de_transcricao_apple.py - code/relatorios/analise-brools/ - code/relatorios/analise-visual/ - code/relatorios/audio/arquivos/ - code/relatorios/transcricao-timeline.md
83 lines
3.2 KiB
Swift
83 lines
3.2 KiB
Swift
import Foundation
|
|
import Speech
|
|
|
|
/// Encapsula o ciclo de vida do Apple Speech para a Engine.
|
|
///
|
|
/// A classe não conhece o scanner, o catálogo ou a persistência. Sua única
|
|
/// responsabilidade é converter um arquivo de mídia em JSON temporal. O
|
|
/// processo é Foundation-only para poder ser executado como CLI sem iniciar
|
|
/// AppKit ou NSApplication.
|
|
final class TranscritorDeFalaApple {
|
|
private let semaforo = DispatchSemaphore(value: 0)
|
|
private var payload: [String: Any] = ["segmentos": []]
|
|
private var codigoDeSaida: Int32 = 0
|
|
private var finalizado = false
|
|
|
|
func executar(argumentos: [String]) -> Int32 {
|
|
guard argumentos.count >= 3 else {
|
|
return finalizar("uso: apple-speech-transcriber arquivo locale [somente_no_dispositivo]", 2)
|
|
}
|
|
|
|
let arquivo = URL(fileURLWithPath: argumentos[1])
|
|
let locale = Locale(identifier: argumentos[2])
|
|
let somenteNoDispositivo = argumentos.count > 3 && argumentos[3] == "true"
|
|
|
|
guard let recognizer = SFSpeechRecognizer(locale: locale), recognizer.isAvailable else {
|
|
return finalizar("Apple Speech indisponível para o locale solicitado", 3)
|
|
}
|
|
|
|
SFSpeechRecognizer.requestAuthorization { [weak self] status in
|
|
guard let self else { return }
|
|
guard status == .authorized else {
|
|
self.finalizar("Permissão do Apple Speech não concedida", 4)
|
|
return
|
|
}
|
|
|
|
let request = SFSpeechURLRecognitionRequest(url: arquivo)
|
|
request.shouldReportPartialResults = false
|
|
if somenteNoDispositivo && recognizer.supportsOnDeviceRecognition {
|
|
request.requiresOnDeviceRecognition = true
|
|
}
|
|
recognizer.recognitionTask(with: request) { [weak self] resultado, erro in
|
|
guard let self else { return }
|
|
if let resultado, resultado.isFinal {
|
|
self.payload["segmentos"] = resultado.bestTranscription.segments.map {
|
|
["inicio": $0.timestamp, "fim": $0.timestamp + $0.duration,
|
|
"texto": $0.substring, "confianca": $0.confidence] as [String: Any]
|
|
}
|
|
self.finalizar(nil, 0)
|
|
} else if let erro {
|
|
self.finalizar("Falha no Apple Speech: \(erro.localizedDescription)", 5)
|
|
}
|
|
}
|
|
}
|
|
|
|
semaforo.wait()
|
|
imprimirResultado()
|
|
return codigoDeSaida
|
|
}
|
|
|
|
@discardableResult
|
|
private func finalizar(_ mensagem: String?, _ codigo: Int32) -> Int32 {
|
|
guard !finalizado else { return codigoDeSaida }
|
|
finalizado = true
|
|
if let mensagem { fputs(mensagem + "\n", stderr) }
|
|
codigoDeSaida = codigo
|
|
semaforo.signal()
|
|
return codigo
|
|
}
|
|
|
|
private func imprimirResultado() {
|
|
guard let dados = try? JSONSerialization.data(withJSONObject: payload),
|
|
let texto = String(data: dados, encoding: .utf8) else {
|
|
fputs("Não foi possível serializar o resultado do Apple Speech\n", stderr)
|
|
codigoDeSaida = 6
|
|
return
|
|
}
|
|
print(texto)
|
|
}
|
|
}
|
|
|
|
let transcritor = TranscritorDeFalaApple()
|
|
exit(transcritor.executar(argumentos: CommandLine.arguments))
|