From 14ebf7c135f0ce302def7a859986445d212ac98b Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:53:05 -0500 Subject: [PATCH 1/3] feat(TranscribeStreaming): Adding support for missing languages. --- .../AWSTranscribeStreamingModel.h | 49 +- .../AWSTranscribeStreamingModel.m | 483 ++++++++++++++++-- .../AWSTranscribeStreamingResources.m | 48 +- .../AWSTranscribeStreamingSwiftTests.swift | 321 ++++-------- AWSTranscribeStreamingTests/hola_mundo.wav | Bin 0 -> 74958 bytes CHANGELOG.md | 5 +- 6 files changed, 629 insertions(+), 277 deletions(-) create mode 100644 AWSTranscribeStreamingTests/hola_mundo.wav diff --git a/AWSTranscribeStreaming/AWSTranscribeStreamingModel.h b/AWSTranscribeStreaming/AWSTranscribeStreamingModel.h index 7ceb19c7743..cbcdc2465c4 100644 --- a/AWSTranscribeStreaming/AWSTranscribeStreamingModel.h +++ b/AWSTranscribeStreaming/AWSTranscribeStreamingModel.h @@ -1,5 +1,5 @@ // -// Copyright 2010-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. @@ -50,8 +50,48 @@ typedef NS_ENUM(NSInteger, AWSTranscribeStreamingLanguageCode) { AWSTranscribeStreamingLanguageCodeJaJP, AWSTranscribeStreamingLanguageCodeKoKR, AWSTranscribeStreamingLanguageCodeZhCN, - AWSTranscribeStreamingLanguageCodeHiIN, AWSTranscribeStreamingLanguageCodeThTH, + AWSTranscribeStreamingLanguageCodeEsES, + AWSTranscribeStreamingLanguageCodeArSA, + AWSTranscribeStreamingLanguageCodePtPT, + AWSTranscribeStreamingLanguageCodeCaES, + AWSTranscribeStreamingLanguageCodeArAE, + AWSTranscribeStreamingLanguageCodeHiIN, + AWSTranscribeStreamingLanguageCodeZhHK, + AWSTranscribeStreamingLanguageCodeNlNL, + AWSTranscribeStreamingLanguageCodeNoNO, + AWSTranscribeStreamingLanguageCodeSvSE, + AWSTranscribeStreamingLanguageCodePlPL, + AWSTranscribeStreamingLanguageCodeFiFI, + AWSTranscribeStreamingLanguageCodeZhTW, + AWSTranscribeStreamingLanguageCodeEnIN, + AWSTranscribeStreamingLanguageCodeEnIE, + AWSTranscribeStreamingLanguageCodeEnNZ, + AWSTranscribeStreamingLanguageCodeEnAB, + AWSTranscribeStreamingLanguageCodeEnZA, + AWSTranscribeStreamingLanguageCodeEnWL, + AWSTranscribeStreamingLanguageCodeDeCH, + AWSTranscribeStreamingLanguageCodeAfZA, + AWSTranscribeStreamingLanguageCodeEuES, + AWSTranscribeStreamingLanguageCodeHrHR, + AWSTranscribeStreamingLanguageCodeCsCZ, + AWSTranscribeStreamingLanguageCodeDaDK, + AWSTranscribeStreamingLanguageCodeFaIR, + AWSTranscribeStreamingLanguageCodeGlES, + AWSTranscribeStreamingLanguageCodeElGR, + AWSTranscribeStreamingLanguageCodeHeIL, + AWSTranscribeStreamingLanguageCodeIdID, + AWSTranscribeStreamingLanguageCodeLvLV, + AWSTranscribeStreamingLanguageCodeMsMY, + AWSTranscribeStreamingLanguageCodeRoRO, + AWSTranscribeStreamingLanguageCodeRuRU, + AWSTranscribeStreamingLanguageCodeSrRS, + AWSTranscribeStreamingLanguageCodeSkSK, + AWSTranscribeStreamingLanguageCodeSoSO, + AWSTranscribeStreamingLanguageCodeTlPH, + AWSTranscribeStreamingLanguageCodeUkUA, + AWSTranscribeStreamingLanguageCodeViVN, + AWSTranscribeStreamingLanguageCodeZuZA, }; typedef NS_ENUM(NSInteger, AWSTranscribeStreamingMediaEncoding) { @@ -308,6 +348,11 @@ typedef NS_ENUM(NSInteger, AWSTranscribeStreamingMediaEncoding) { */ @property (nonatomic, strong) NSError* _Nullable limitExceededException; +/** +
The service is currently unavailable. Try your request later.
+ */ +@property (nonatomic, strong) NSError* _Nullable serviceUnavailableException; + /**A portion of the transcription of the audio stream. Events are sent periodically from Amazon Transcribe to your application. The event can be a partial transcription of a section of the audio stream, or it can be the entire transcription of that portion of the audio stream.
*/ diff --git a/AWSTranscribeStreaming/AWSTranscribeStreamingModel.m b/AWSTranscribeStreaming/AWSTranscribeStreamingModel.m index d100b5ecf79..0e4a957d966 100644 --- a/AWSTranscribeStreaming/AWSTranscribeStreamingModel.m +++ b/AWSTranscribeStreaming/AWSTranscribeStreamingModel.m @@ -1,5 +1,5 @@ // -// Copyright 2010-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. @@ -168,32 +168,152 @@ + (NSValueTransformer *)languageCodeJSONTransformer { return @(AWSTranscribeStreamingLanguageCodeFrFR); } if ([value caseInsensitiveCompare:@"en-AU"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeEnAU); - } - if ([value caseInsensitiveCompare:@"it-IT"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeItIT); - } - if ([value caseInsensitiveCompare:@"de-DE"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeDeDE); - } - if ([value caseInsensitiveCompare:@"pt-BR"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodePtBR); - } - if ([value caseInsensitiveCompare:@"ja-JP"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeJaJP); - } - if ([value caseInsensitiveCompare:@"ko-KR"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeKoKR); - } - if ([value caseInsensitiveCompare:@"zh-CN"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeZhCN); - } - if ([value caseInsensitiveCompare:@"hi-IN"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeHiIN); - } - if ([value caseInsensitiveCompare:@"th-TH"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeThTH); - } + return @(AWSTranscribeStreamingLanguageCodeEnAU); + } + if ([value caseInsensitiveCompare:@"it-IT"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeItIT); + } + if ([value caseInsensitiveCompare:@"de-DE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeDeDE); + } + if ([value caseInsensitiveCompare:@"pt-BR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodePtBR); + } + if ([value caseInsensitiveCompare:@"ja-JP"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeJaJP); + } + if ([value caseInsensitiveCompare:@"ko-KR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeKoKR); + } + if ([value caseInsensitiveCompare:@"zh-CN"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZhCN); + } + if ([value caseInsensitiveCompare:@"th-TH"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeThTH); + } + if ([value caseInsensitiveCompare:@"es-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEsES); + } + if ([value caseInsensitiveCompare:@"ar-SA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeArSA); + } + if ([value caseInsensitiveCompare:@"pt-PT"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodePtPT); + } + if ([value caseInsensitiveCompare:@"ca-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeCaES); + } + if ([value caseInsensitiveCompare:@"ar-AE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeArAE); + } + if ([value caseInsensitiveCompare:@"hi-IN"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeHiIN); + } + if ([value caseInsensitiveCompare:@"zh-HK"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZhHK); + } + if ([value caseInsensitiveCompare:@"nl-NL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeNlNL); + } + if ([value caseInsensitiveCompare:@"no-NO"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeNoNO); + } + if ([value caseInsensitiveCompare:@"sv-SE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSvSE); + } + if ([value caseInsensitiveCompare:@"pl-PL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodePlPL); + } + if ([value caseInsensitiveCompare:@"fi-FI"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeFiFI); + } + if ([value caseInsensitiveCompare:@"zh-TW"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZhTW); + } + if ([value caseInsensitiveCompare:@"en-IN"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnIN); + } + if ([value caseInsensitiveCompare:@"en-IE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnIE); + } + if ([value caseInsensitiveCompare:@"en-NZ"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnNZ); + } + if ([value caseInsensitiveCompare:@"en-AB"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnAB); + } + if ([value caseInsensitiveCompare:@"en-ZA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnZA); + } + if ([value caseInsensitiveCompare:@"en-WL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnWL); + } + if ([value caseInsensitiveCompare:@"de-CH"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeDeCH); + } + if ([value caseInsensitiveCompare:@"af-ZA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeAfZA); + } + if ([value caseInsensitiveCompare:@"eu-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEuES); + } + if ([value caseInsensitiveCompare:@"hr-HR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeHrHR); + } + if ([value caseInsensitiveCompare:@"cs-CZ"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeCsCZ); + } + if ([value caseInsensitiveCompare:@"da-DK"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeDaDK); + } + if ([value caseInsensitiveCompare:@"fa-IR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeFaIR); + } + if ([value caseInsensitiveCompare:@"gl-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeGlES); + } + if ([value caseInsensitiveCompare:@"el-GR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeElGR); + } + if ([value caseInsensitiveCompare:@"he-IL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeHeIL); + } + if ([value caseInsensitiveCompare:@"id-ID"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeIdID); + } + if ([value caseInsensitiveCompare:@"lv-LV"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeLvLV); + } + if ([value caseInsensitiveCompare:@"ms-MY"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeMsMY); + } + if ([value caseInsensitiveCompare:@"ro-RO"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeRoRO); + } + if ([value caseInsensitiveCompare:@"ru-RU"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeRuRU); + } + if ([value caseInsensitiveCompare:@"sr-RS"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSrRS); + } + if ([value caseInsensitiveCompare:@"sk-SK"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSkSK); + } + if ([value caseInsensitiveCompare:@"so-SO"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSoSO); + } + if ([value caseInsensitiveCompare:@"tl-PH"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeTlPH); + } + if ([value caseInsensitiveCompare:@"uk-UA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeUkUA); + } + if ([value caseInsensitiveCompare:@"vi-VN"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeViVN); + } + if ([value caseInsensitiveCompare:@"zu-ZA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZuZA); + } return @(AWSTranscribeStreamingLanguageCodeUnknown); } reverseBlock:^NSString *(NSNumber *value) { switch ([value integerValue]) { @@ -221,10 +341,90 @@ + (NSValueTransformer *)languageCodeJSONTransformer { return @"ko-KR"; case AWSTranscribeStreamingLanguageCodeZhCN: return @"zh-CN"; - case AWSTranscribeStreamingLanguageCodeHiIN: - return @"hi-IN"; case AWSTranscribeStreamingLanguageCodeThTH: return @"th-TH"; + case AWSTranscribeStreamingLanguageCodeEsES: + return @"es-ES"; + case AWSTranscribeStreamingLanguageCodeArSA: + return @"ar-SA"; + case AWSTranscribeStreamingLanguageCodePtPT: + return @"pt-PT"; + case AWSTranscribeStreamingLanguageCodeCaES: + return @"ca-ES"; + case AWSTranscribeStreamingLanguageCodeArAE: + return @"ar-AE"; + case AWSTranscribeStreamingLanguageCodeHiIN: + return @"hi-IN"; + case AWSTranscribeStreamingLanguageCodeZhHK: + return @"zh-HK"; + case AWSTranscribeStreamingLanguageCodeNlNL: + return @"nl-NL"; + case AWSTranscribeStreamingLanguageCodeNoNO: + return @"no-NO"; + case AWSTranscribeStreamingLanguageCodeSvSE: + return @"sv-SE"; + case AWSTranscribeStreamingLanguageCodePlPL: + return @"pl-PL"; + case AWSTranscribeStreamingLanguageCodeFiFI: + return @"fi-FI"; + case AWSTranscribeStreamingLanguageCodeZhTW: + return @"zh-TW"; + case AWSTranscribeStreamingLanguageCodeEnIN: + return @"en-IN"; + case AWSTranscribeStreamingLanguageCodeEnIE: + return @"en-IE"; + case AWSTranscribeStreamingLanguageCodeEnNZ: + return @"en-NZ"; + case AWSTranscribeStreamingLanguageCodeEnAB: + return @"en-AB"; + case AWSTranscribeStreamingLanguageCodeEnZA: + return @"en-ZA"; + case AWSTranscribeStreamingLanguageCodeEnWL: + return @"en-WL"; + case AWSTranscribeStreamingLanguageCodeDeCH: + return @"de-CH"; + case AWSTranscribeStreamingLanguageCodeAfZA: + return @"af-ZA"; + case AWSTranscribeStreamingLanguageCodeEuES: + return @"eu-ES"; + case AWSTranscribeStreamingLanguageCodeHrHR: + return @"hr-HR"; + case AWSTranscribeStreamingLanguageCodeCsCZ: + return @"cs-CZ"; + case AWSTranscribeStreamingLanguageCodeDaDK: + return @"da-DK"; + case AWSTranscribeStreamingLanguageCodeFaIR: + return @"fa-IR"; + case AWSTranscribeStreamingLanguageCodeGlES: + return @"gl-ES"; + case AWSTranscribeStreamingLanguageCodeElGR: + return @"el-GR"; + case AWSTranscribeStreamingLanguageCodeHeIL: + return @"he-IL"; + case AWSTranscribeStreamingLanguageCodeIdID: + return @"id-ID"; + case AWSTranscribeStreamingLanguageCodeLvLV: + return @"lv-LV"; + case AWSTranscribeStreamingLanguageCodeMsMY: + return @"ms-MY"; + case AWSTranscribeStreamingLanguageCodeRoRO: + return @"ro-RO"; + case AWSTranscribeStreamingLanguageCodeRuRU: + return @"ru-RU"; + case AWSTranscribeStreamingLanguageCodeSrRS: + return @"sr-RS"; + case AWSTranscribeStreamingLanguageCodeSkSK: + return @"sk-SK"; + case AWSTranscribeStreamingLanguageCodeSoSO: + return @"so-SO"; + case AWSTranscribeStreamingLanguageCodeTlPH: + return @"tl-PH"; + case AWSTranscribeStreamingLanguageCodeUkUA: + return @"uk-UA"; + case AWSTranscribeStreamingLanguageCodeViVN: + return @"vi-VN"; + case AWSTranscribeStreamingLanguageCodeZuZA: + return @"zu-ZA"; default: return nil; } @@ -305,11 +505,131 @@ + (NSValueTransformer *)languageCodeJSONTransformer { if ([value caseInsensitiveCompare:@"zh-CN"] == NSOrderedSame) { return @(AWSTranscribeStreamingLanguageCodeZhCN); } + if ([value caseInsensitiveCompare:@"th-TH"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeThTH); + } + if ([value caseInsensitiveCompare:@"es-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEsES); + } + if ([value caseInsensitiveCompare:@"ar-SA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeArSA); + } + if ([value caseInsensitiveCompare:@"pt-PT"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodePtPT); + } + if ([value caseInsensitiveCompare:@"ca-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeCaES); + } + if ([value caseInsensitiveCompare:@"ar-AE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeArAE); + } if ([value caseInsensitiveCompare:@"hi-IN"] == NSOrderedSame) { return @(AWSTranscribeStreamingLanguageCodeHiIN); } - if ([value caseInsensitiveCompare:@"th-TH"] == NSOrderedSame) { - return @(AWSTranscribeStreamingLanguageCodeThTH); + if ([value caseInsensitiveCompare:@"zh-HK"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZhHK); + } + if ([value caseInsensitiveCompare:@"nl-NL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeNlNL); + } + if ([value caseInsensitiveCompare:@"no-NO"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeNoNO); + } + if ([value caseInsensitiveCompare:@"sv-SE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSvSE); + } + if ([value caseInsensitiveCompare:@"pl-PL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodePlPL); + } + if ([value caseInsensitiveCompare:@"fi-FI"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeFiFI); + } + if ([value caseInsensitiveCompare:@"zh-TW"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZhTW); + } + if ([value caseInsensitiveCompare:@"en-IN"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnIN); + } + if ([value caseInsensitiveCompare:@"en-IE"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnIE); + } + if ([value caseInsensitiveCompare:@"en-NZ"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnNZ); + } + if ([value caseInsensitiveCompare:@"en-AB"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnAB); + } + if ([value caseInsensitiveCompare:@"en-ZA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnZA); + } + if ([value caseInsensitiveCompare:@"en-WL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEnWL); + } + if ([value caseInsensitiveCompare:@"de-CH"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeDeCH); + } + if ([value caseInsensitiveCompare:@"af-ZA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeAfZA); + } + if ([value caseInsensitiveCompare:@"eu-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeEuES); + } + if ([value caseInsensitiveCompare:@"hr-HR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeHrHR); + } + if ([value caseInsensitiveCompare:@"cs-CZ"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeCsCZ); + } + if ([value caseInsensitiveCompare:@"da-DK"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeDaDK); + } + if ([value caseInsensitiveCompare:@"fa-IR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeFaIR); + } + if ([value caseInsensitiveCompare:@"gl-ES"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeGlES); + } + if ([value caseInsensitiveCompare:@"el-GR"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeElGR); + } + if ([value caseInsensitiveCompare:@"he-IL"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeHeIL); + } + if ([value caseInsensitiveCompare:@"id-ID"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeIdID); + } + if ([value caseInsensitiveCompare:@"lv-LV"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeLvLV); + } + if ([value caseInsensitiveCompare:@"ms-MY"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeMsMY); + } + if ([value caseInsensitiveCompare:@"ro-RO"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeRoRO); + } + if ([value caseInsensitiveCompare:@"ru-RU"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeRuRU); + } + if ([value caseInsensitiveCompare:@"sr-RS"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSrRS); + } + if ([value caseInsensitiveCompare:@"sk-SK"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSkSK); + } + if ([value caseInsensitiveCompare:@"so-SO"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeSoSO); + } + if ([value caseInsensitiveCompare:@"tl-PH"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeTlPH); + } + if ([value caseInsensitiveCompare:@"uk-UA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeUkUA); + } + if ([value caseInsensitiveCompare:@"vi-VN"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeViVN); + } + if ([value caseInsensitiveCompare:@"zu-ZA"] == NSOrderedSame) { + return @(AWSTranscribeStreamingLanguageCodeZuZA); } return @(AWSTranscribeStreamingLanguageCodeUnknown); } reverseBlock:^NSString *(NSNumber *value) { @@ -338,26 +658,90 @@ + (NSValueTransformer *)languageCodeJSONTransformer { return @"ko-KR"; case AWSTranscribeStreamingLanguageCodeZhCN: return @"zh-CN"; - case AWSTranscribeStreamingLanguageCodeHiIN: - return @"hi-IN"; case AWSTranscribeStreamingLanguageCodeThTH: return @"th-TH"; - default: - return nil; - } - }]; -} - -+ (NSValueTransformer *)mediaEncodingJSONTransformer { - return [AWSMTLValueTransformer reversibleTransformerWithForwardBlock:^NSNumber *(NSString *value) { - if ([value caseInsensitiveCompare:@"pcm"] == NSOrderedSame) { - return @(AWSTranscribeStreamingMediaEncodingPcm); - } - return @(AWSTranscribeStreamingMediaEncodingUnknown); - } reverseBlock:^NSString *(NSNumber *value) { - switch ([value integerValue]) { - case AWSTranscribeStreamingMediaEncodingPcm: - return @"pcm"; + case AWSTranscribeStreamingLanguageCodeEsES: + return @"es-ES"; + case AWSTranscribeStreamingLanguageCodeArSA: + return @"ar-SA"; + case AWSTranscribeStreamingLanguageCodePtPT: + return @"pt-PT"; + case AWSTranscribeStreamingLanguageCodeCaES: + return @"ca-ES"; + case AWSTranscribeStreamingLanguageCodeArAE: + return @"ar-AE"; + case AWSTranscribeStreamingLanguageCodeHiIN: + return @"hi-IN"; + case AWSTranscribeStreamingLanguageCodeZhHK: + return @"zh-HK"; + case AWSTranscribeStreamingLanguageCodeNlNL: + return @"nl-NL"; + case AWSTranscribeStreamingLanguageCodeNoNO: + return @"no-NO"; + case AWSTranscribeStreamingLanguageCodeSvSE: + return @"sv-SE"; + case AWSTranscribeStreamingLanguageCodePlPL: + return @"pl-PL"; + case AWSTranscribeStreamingLanguageCodeFiFI: + return @"fi-FI"; + case AWSTranscribeStreamingLanguageCodeZhTW: + return @"zh-TW"; + case AWSTranscribeStreamingLanguageCodeEnIN: + return @"en-IN"; + case AWSTranscribeStreamingLanguageCodeEnIE: + return @"en-IE"; + case AWSTranscribeStreamingLanguageCodeEnNZ: + return @"en-NZ"; + case AWSTranscribeStreamingLanguageCodeEnAB: + return @"en-AB"; + case AWSTranscribeStreamingLanguageCodeEnZA: + return @"en-ZA"; + case AWSTranscribeStreamingLanguageCodeEnWL: + return @"en-WL"; + case AWSTranscribeStreamingLanguageCodeDeCH: + return @"de-CH"; + case AWSTranscribeStreamingLanguageCodeAfZA: + return @"af-ZA"; + case AWSTranscribeStreamingLanguageCodeEuES: + return @"eu-ES"; + case AWSTranscribeStreamingLanguageCodeHrHR: + return @"hr-HR"; + case AWSTranscribeStreamingLanguageCodeCsCZ: + return @"cs-CZ"; + case AWSTranscribeStreamingLanguageCodeDaDK: + return @"da-DK"; + case AWSTranscribeStreamingLanguageCodeFaIR: + return @"fa-IR"; + case AWSTranscribeStreamingLanguageCodeGlES: + return @"gl-ES"; + case AWSTranscribeStreamingLanguageCodeElGR: + return @"el-GR"; + case AWSTranscribeStreamingLanguageCodeHeIL: + return @"he-IL"; + case AWSTranscribeStreamingLanguageCodeIdID: + return @"id-ID"; + case AWSTranscribeStreamingLanguageCodeLvLV: + return @"lv-LV"; + case AWSTranscribeStreamingLanguageCodeMsMY: + return @"ms-MY"; + case AWSTranscribeStreamingLanguageCodeRoRO: + return @"ro-RO"; + case AWSTranscribeStreamingLanguageCodeRuRU: + return @"ru-RU"; + case AWSTranscribeStreamingLanguageCodeSrRS: + return @"sr-RS"; + case AWSTranscribeStreamingLanguageCodeSkSK: + return @"sk-SK"; + case AWSTranscribeStreamingLanguageCodeSoSO: + return @"so-SO"; + case AWSTranscribeStreamingLanguageCodeTlPH: + return @"tl-PH"; + case AWSTranscribeStreamingLanguageCodeUkUA: + return @"uk-UA"; + case AWSTranscribeStreamingLanguageCodeViVN: + return @"vi-VN"; + case AWSTranscribeStreamingLanguageCodeZuZA: + return @"zu-ZA"; default: return nil; } @@ -418,6 +802,7 @@ + (NSDictionary *)JSONKeyPathsByPropertyKey { @"conflictException" : @"ConflictException", @"internalFailureException" : @"InternalFailureException", @"limitExceededException" : @"LimitExceededException", + @"serviceUnavailableException" : @"ServiceUnavailableException", @"transcriptEvent" : @"TranscriptEvent", }; } diff --git a/AWSTranscribeStreaming/AWSTranscribeStreamingResources.m b/AWSTranscribeStreaming/AWSTranscribeStreamingResources.m index f69c74f540b..185cc17d8ba 100644 --- a/AWSTranscribeStreaming/AWSTranscribeStreamingResources.m +++ b/AWSTranscribeStreaming/AWSTranscribeStreamingResources.m @@ -1,5 +1,5 @@ // -// Copyright 2010-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. @@ -209,8 +209,48 @@ - (nonnull NSString *)definitionString { \"ja-JP\",\ \"ko-KR\",\ \"zh-CN\",\ + \"th-TH\",\ + \"es-ES\",\ + \"ar-SA\",\ + \"pt-PT\",\ + \"ca-ES\",\ + \"ar-AE\",\ \"hi-IN\",\ - \"th-TH\"\ + \"zh-HK\",\ + \"nl-NL\",\ + \"no-NO\",\ + \"sv-SE\",\ + \"pl-PL\",\ + \"fi-FI\",\ + \"zh-TW\",\ + \"en-IN\",\ + \"en-IE\",\ + \"en-NZ\",\ + \"en-AB\",\ + \"en-ZA\",\ + \"en-WL\",\ + \"de-CH\",\ + \"af-ZA\",\ + \"eu-ES\",\ + \"hr-HR\",\ + \"cs-CZ\",\ + \"da-DK\",\ + \"fa-IR\",\ + \"gl-ES\",\ + \"el-GR\",\ + \"he-IL\",\ + \"id-ID\",\ + \"lv-LV\",\ + \"ms-MY\",\ + \"ro-RO\",\ + \"ru-RU\",\ + \"sr-RS\",\ + \"sk-SK\",\ + \"so-SO\",\ + \"tl-PH\",\ + \"uk-UA\",\ + \"vi-VN\",\ + \"zu-ZA\"\ ]\ },\ \"LimitExceededException\":{\ @@ -402,6 +442,10 @@ - (nonnull NSString *)definitionString { \"ConflictException\":{\ \"shape\":\"ConflictException\",\ \"documentation\":\"A new stream started with the same session ID. The current stream has been terminated.
\"\ + },\ + \"ServiceUnavailableException\":{\ + \"shape\":\"ServiceUnavailableException\",\ + \"documentation\":\"The service is currently unavailable. Try your request later.
\"\ }\ },\ \"documentation\":\"Represents the transcription result stream from Amazon Transcribe to your application.
\",\ diff --git a/AWSTranscribeStreamingTests/AWSTranscribeStreamingSwiftTests.swift b/AWSTranscribeStreamingTests/AWSTranscribeStreamingSwiftTests.swift index f6bda773a2e..97ec35a5bf4 100644 --- a/AWSTranscribeStreamingTests/AWSTranscribeStreamingSwiftTests.swift +++ b/AWSTranscribeStreamingTests/AWSTranscribeStreamingSwiftTests.swift @@ -40,265 +40,140 @@ class AWSTranscribeStreamingSwiftTests: XCTestCase { transcribeStreamingClient = AWSTranscribeStreaming(forKey: AWSTranscribeStreamingSwiftTests.transcribeClientKey) AWSDDLog.sharedInstance.logLevel = .info - AWSDDLog.sharedInstance.add(AWSDDTTYLogger.sharedInstance) + AWSDDLog.add(AWSDDOSLogger.sharedInstance) } - func testStreamingExample() throws { - AWSDDLog.sharedInstance.logLevel = .info - AWSDDLog.add(AWSDDTTYLogger.sharedInstance) - - let bundle = Bundle(for: AWSTranscribeStreamingSwiftTests.self) - guard let audioPath = bundle.path(forResource: "hello_world", ofType: "wav") else { - XCTFail("Can't find audio path") - return - } - - let audioURL = URL(fileURLWithPath: audioPath) - let audioData = try Data(contentsOf: audioURL) - - guard let request = AWSTranscribeStreamingStartStreamTranscriptionRequest() else { - XCTFail("request unexpectedly nil") - return - } - - request.languageCode = .enUS - request.mediaEncoding = .pcm - request.mediaSampleRateHertz = 8000 - - // Set up delegate and its expectations - let delegate = MockTranscribeStreamingClientDelegate() - - // Connection open/close - let webSocketIsConnected = expectation(description: "Web socket is connected") - let webSocketIsClosed = expectation(description: "Web socket is closed") - delegate.connectionStatusCallback = { status, error in - if status == .connected { - DispatchQueue.main.async { - webSocketIsConnected.fulfill() - } - } - - if status == .closed && error == nil { - DispatchQueue.main.async { - webSocketIsClosed.fulfill() - } - } - } + func testStreamingExamples() throws { + let audioExamplesMap: [AWSTranscribeStreamingLanguageCode: String] = [ + .enUS: "hello_world", + .deDE: "guten_tag", + .esES: "hola_ mundo" + ] - // Event: for this test, we expect to only receive transcriptions, not errors - let receivedFinalTranscription = expectation(description: "Received final transcription") - delegate.receiveEventCallback = { event, error in - if let error = error { - XCTFail("Unexpected error receiving event: \(error)") - return - } - - guard let event = event else { - XCTFail("event unexpectedly nil") - return - } - - guard let transcriptEvent = event.transcriptEvent else { - XCTFail("transcriptEvent unexpectedly nil: event may be an error \(event)") + for (languageCode, fileName) in audioExamplesMap { + let bundle = Bundle(for: AWSTranscribeStreamingSwiftTests.self) + guard let audioPath = bundle.path(forResource: fileName, ofType: "wav") else { + XCTFail("Can't find audio path: \(fileName).wav") return } - guard let results = transcriptEvent.transcript?.results else { - print("No results, waiting for next event") - return - } + let audioURL = URL(fileURLWithPath: audioPath) + let audioData = try Data(contentsOf: audioURL) - guard let firstResult = results.first else { - print("firstResult nil--possibly a partial result: \(event)") + guard let request = AWSTranscribeStreamingStartStreamTranscriptionRequest() else { + XCTFail("request unexpectedly nil") return } - guard let isPartial = firstResult.isPartial as? Bool else { - XCTFail("isPartial unexpectedly nil, or cannot cast NSNumber to Bool") - return - } + request.languageCode = languageCode + request.mediaEncoding = .pcm + request.mediaSampleRateHertz = 24000 - guard !isPartial else { - print("Partial result received, waiting for next event (results: \(results))") - return - } + // Set up delegate and its expectations + let delegate = MockTranscribeStreamingClientDelegate() - print("Received final transcription event (results: \(results))") - DispatchQueue.main.async { - receivedFinalTranscription.fulfill() - } - } - - let callbackQueue = DispatchQueue(label: "testStreamingExample") - transcribeStreamingClient.setDelegate(delegate, callbackQueue: callbackQueue) + // Connection open/close + let webSocketIsConnected = expectation(description: "Web socket is connected") + let webSocketIsClosed = expectation(description: "Web socket is closed") + delegate.connectionStatusCallback = { status, error in + if status == .connected { + DispatchQueue.main.async { + webSocketIsConnected.fulfill() + } + } - // Now that we have a delegate ready to receive the "open" event, we can start the transcription request - transcribeStreamingClient.startTranscriptionWSS(request) + if status == .closed && error == nil { + DispatchQueue.main.async { + webSocketIsClosed.fulfill() + } + } + } - wait(for: [webSocketIsConnected], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) + // Event: for this test, we expect to only receive transcriptions, not errors + let receivedFinalTranscription = expectation(description: "Received final transcription") + delegate.receiveEventCallback = { event, error in + if let error = error { + XCTFail("Unexpected error receiving event: \(error)") + return + } - // Now that the web socket is connected, it is safe to proceed with streaming + guard let event = event else { + XCTFail("event unexpectedly nil") + return + } - let headers = [ - ":content-type": "audio/wav", - ":message-type": "event", - ":event-type": "AudioEvent" - ] - - let chunkSize = 4096 - let audioDataSize = audioData.count - - var currentStart = 0 - var currentEnd = min(chunkSize, audioDataSize - currentStart) + guard let transcriptEvent = event.transcriptEvent else { + XCTFail("transcriptEvent unexpectedly nil: event may be an error \(event)") + return + } - while currentStart < audioDataSize { - let dataChunk = audioData[currentStart ..< currentEnd] - transcribeStreamingClient.send(dataChunk, headers: headers) + guard let results = transcriptEvent.transcript?.results else { + print("No results, waiting for next event") + return + } - currentStart = currentEnd - currentEnd = min(currentStart + chunkSize, audioDataSize) - } - - print("Sending end frame") - self.transcribeStreamingClient.sendEndFrame() + guard let firstResult = results.first else { + print("firstResult nil--possibly a partial result: \(event)") + return + } - print("Waiting for final transcription event") - wait(for: [receivedFinalTranscription], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) - - print("Ending transcription") - transcribeStreamingClient.endTranscription() - - print("Waiting for websocket to close") - wait(for: [webSocketIsClosed], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) - } - - func testStreamingExampleDeutsche() throws { - AWSDDLog.sharedInstance.logLevel = .info - AWSDDLog.add(AWSDDTTYLogger.sharedInstance) - - let bundle = Bundle(for: AWSTranscribeStreamingSwiftTests.self) - guard let audioPath = bundle.path(forResource: "guten_tag", ofType: "wav") else { - XCTFail("Can't find audio path") - return - } - - let audioURL = URL(fileURLWithPath: audioPath) - let audioData = try Data(contentsOf: audioURL) - - guard let request = AWSTranscribeStreamingStartStreamTranscriptionRequest() else { - XCTFail("request unexpectedly nil") - return - } + guard let isPartial = firstResult.isPartial as? Bool else { + XCTFail("isPartial unexpectedly nil, or cannot cast NSNumber to Bool") + return + } - request.languageCode = .deDE - request.mediaEncoding = .pcm - request.mediaSampleRateHertz = 24000 - - // Set up delegate and its expectations - let delegate = MockTranscribeStreamingClientDelegate() - - // Connection open/close - let webSocketIsConnected = expectation(description: "Web socket is connected") - let webSocketIsClosed = expectation(description: "Web socket is closed") - delegate.connectionStatusCallback = { status, error in - if status == .connected { - DispatchQueue.main.async { - webSocketIsConnected.fulfill() + guard !isPartial else { + print("Partial result received, waiting for next event (results: \(results))") + return } - } - - if status == .closed && error == nil { + + print("Received final transcription event (results: \(results))") DispatchQueue.main.async { - webSocketIsClosed.fulfill() + receivedFinalTranscription.fulfill() } } - } - // Event: for this test, we expect to only receive transcriptions, not errors - let receivedFinalTranscription = expectation(description: "Received final transcription") - delegate.receiveEventCallback = { event, error in - if let error = error { - XCTFail("Unexpected error receiving event: \(error)") - return - } - - guard let event = event else { - XCTFail("event unexpectedly nil") - return - } - - guard let transcriptEvent = event.transcriptEvent else { - XCTFail("transcriptEvent unexpectedly nil: event may be an error \(event)") - return - } + let callbackQueue = DispatchQueue(label: "testStreamingExample") + transcribeStreamingClient.setDelegate(delegate, callbackQueue: callbackQueue) - guard let results = transcriptEvent.transcript?.results else { - print("No results, waiting for next event") - return - } + // Now that we have a delegate ready to receive the "open" event, we can start the transcription request + transcribeStreamingClient.startTranscriptionWSS(request) - guard let firstResult = results.first else { - print("firstResult nil--possibly a partial result: \(event)") - return - } + wait(for: [webSocketIsConnected], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) - guard let isPartial = firstResult.isPartial as? Bool else { - XCTFail("isPartial unexpectedly nil, or cannot cast NSNumber to Bool") - return - } + // Now that the web socket is connected, it is safe to proceed with streaming - guard !isPartial else { - print("Partial result received, waiting for next event (results: \(results))") - return - } + let headers = [ + ":content-type": "audio/wav", + ":message-type": "event", + ":event-type": "AudioEvent" + ] - print("Received final transcription event (results: \(results))") - DispatchQueue.main.async { - receivedFinalTranscription.fulfill() - } - } - - let callbackQueue = DispatchQueue(label: "testStreamingExample") - transcribeStreamingClient.setDelegate(delegate, callbackQueue: callbackQueue) + let chunkSize = 4096 + let audioDataSize = audioData.count - // Now that we have a delegate ready to receive the "open" event, we can start the transcription request - transcribeStreamingClient.startTranscriptionWSS(request) + var currentStart = 0 + var currentEnd = min(chunkSize, audioDataSize - currentStart) - wait(for: [webSocketIsConnected], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) + while currentStart < audioDataSize { + let dataChunk = audioData[currentStart ..< currentEnd] + transcribeStreamingClient.send(dataChunk, headers: headers) - // Now that the web socket is connected, it is safe to proceed with streaming + currentStart = currentEnd + currentEnd = min(currentStart + chunkSize, audioDataSize) + } - let headers = [ - ":content-type": "audio/wav", - ":message-type": "event", - ":event-type": "AudioEvent" - ] - - let chunkSize = 4096 - let audioDataSize = audioData.count - - var currentStart = 0 - var currentEnd = min(chunkSize, audioDataSize - currentStart) + print("Sending end frame") + self.transcribeStreamingClient.sendEndFrame() - while currentStart < audioDataSize { - let dataChunk = audioData[currentStart ..< currentEnd] - transcribeStreamingClient.send(dataChunk, headers: headers) + print("Waiting for final transcription event") + wait(for: [receivedFinalTranscription], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) - currentStart = currentEnd - currentEnd = min(currentStart + chunkSize, audioDataSize) - } - - print("Sending end frame") - self.transcribeStreamingClient.sendEndFrame() + print("Ending transcription") + transcribeStreamingClient.endTranscription() - print("Waiting for final transcription event") - wait(for: [receivedFinalTranscription], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) - - print("Ending transcription") - transcribeStreamingClient.endTranscription() - - print("Waiting for websocket to close") - wait(for: [webSocketIsClosed], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) + print("Waiting for websocket to close") + wait(for: [webSocketIsClosed], timeout: AWSTranscribeStreamingSwiftTests.networkOperationTimeout) + } } - } diff --git a/AWSTranscribeStreamingTests/hola_mundo.wav b/AWSTranscribeStreamingTests/hola_mundo.wav new file mode 100644 index 0000000000000000000000000000000000000000..81ec393067b8d366b91f5d9bed3b4cf5c311db9f GIT binary patch literal 74958 zcmeGDb=(z2|38jz$LVtgbt$MN_VGTQq{oh
zq)7ispQA;l2Ds)w0Pl+Kve%he^ls`e>NOcRmKdr&M*qS%qW95b`Xw;4HXp3|i4xn%
zZ^4Ko1$g&V{|&4j$x%FVYx%G|Ty7|Lke*0a5^|*3E=>g{F>w=lX^U6Rldli=n
z`Bq$zI!SlHcw%|%Nwh_*M|4+gSbT;wUYV;@1^Jo;-kpY2Nj<7u2II6TN}h5<9tYlO
zT~?Zb{ RWO!7br`qpz6eiG93S)K0Qk
zc24n=YNJ}MJ*=x?IAjc(vdsG|O;hHi+)eqAvOT4H%4s~MzrZWfWz#PvmvOPtfv5D6
zzOa6w?j3m8chxaf70icjARJ$lw~}9$wUfOCHrFD_TyYDLp7ufuVK!vkJ;7J+9-9~4
z6L}t{!*xUBfS!B8f7564vYtEc-LCo0bVmjI4cm0vGTSa&y6w94uyv93jdilEf<0>A
z3@&6jyz}3B>jx%>u13U6`}hs+0hKBGS-MNUNLdNH-GFws-UOd5oy;{YtmWsFJ1Mr5
zdnx~>v`yKMRrf4&0rNN0B9qrx#yH;~H4M}Hb;ERvwd)~)>7qKIR4X??H)+273-HJ0
zNHgG7;&<^nQFmHOz2de(l5~na%iNBgk3NOeu}XMR=v%N>uyLThe~S;^13Xndz1@Gf
z);fQ6?6nuR`)z8w(QdNmu`6)2uphD4ahRMhoJ(C(-CsQSe8yn&@Sy1L%%=EVj-jfG
z&q`yEfz49?sVSg)tgm3)V!Dj?5o4*9(iNkBAHG9U?n7?f*L=_P6a1tdfqZPV;W%`x
zX6wFaYifsqU(#Q-4F29aE8YTg^EcTmDJyA=`TaF8sOr*1;X^Ph@ieZBSHvn{2yiXe
z00~te9v8|8s)N1Yg=U#A!`l_@sN-4be&LeA55y8jQ^#n$|2+=7
zH-a5&?$cCd`YBCgE?rCWR8h!BchhI-y7X?WmAc^_(Hm5+3k@a;ytR>ZeY!H8fV?zD
zg((x=2`kZM=&?TZIJz40yFqQGwo`YgHyCwWDF
>Cg
~2^aj0I@6wIYb=5uB{-EVF6Ezmi4Rs53
zCG{h;_K}iP)K|Qa7ncu_?Ug=&F8?}lRk0Vk+E38ShcR|1^G|{Fb2ZU1@i_iN`~=&U
zmBNx^4wIMJ27Axh`&xWpf=IzuzBXBj}#SEtyIm`<2B2){dIG6t94`a=k*c&aAQ%^CDU5-cjo$*
zT9#AhGk7ZwFn2WnY+h*AnTr4u`j{b~{&($1O(*q3pnE=%eUjdltQLP0RTG`1mcX*9
zZlVjUWl}IVHHa;au7p2
&OQ@H{;?R5N(U
zU*0G6{_Sq#D&g!23ZJ&Uvih=Dz?jNVZEv(st7IK%LGD
z9LTA%MY6@R`LfBfp|XCm9=P)(e8iLnhc!)Rl&NG=SxlM<6wXV)f8Q=$EB#eEOxi+P
zOd133-a+8N){+DvLFpupi?%?g;Suz~P2d%GgO>Xaz8wr7jzIDx;!Xl1u0Y~Jd}h2N
zG`n{K(XlRkcDb1w%vxwR)?xBM*VY~T5W5pQ9Xo*IaBMepZ-0r6g^#b5u^s5Y6S1qY
zm$7W{>BUTbXczQk<}!am4%-@thxy{mfz7uH(yg=5HgWK?C@K9b9f#khY*9DyE%A5o
z`5=<^m7bHz@&5cxc1U&w$e@p8&wv5^NoJLK;klWTo8^_{E#
1~J`Hv&&b
zf(~Zk$#5rP&y~39A?E@2M|f_^0WEd_t_ZV%Ter@$9_Xo`0n2WZXEOTOi=Jokn~VOo
z2>Dxa9Iy4f;dupKe*#E=H9R4pJfFn-Ft4XR5bnC+7?|vt3XY+k8!#L1FN_V!*g(+z
zB@nS*z