Skip to content

Commit

Permalink
Cambridge dictionary #77: Speak words.
Browse files Browse the repository at this point in the history
  • Loading branch information
filimo committed Mar 9, 2020
1 parent 98df838 commit 841d262
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 9 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ Explore features, limitations and bugs *SwiftUI, Combine and Catalyst*.
## Releases
### Download .dmg from [here](https://github.com/filimo/ReaderTranslator/releases)

**1.12.2**
Speaking selected words and phrases by Cambridge speakers

- [Cambridge dictionary #77](https://github.com/filimo/ReaderTranslator/issues/77)

**1.12.0**
- [DeepL implemented #79](https://github.com/filimo/ReaderTranslator/issues/79)

Expand Down
8 changes: 8 additions & 0 deletions ReaderTranslator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@
F058C7FE2397FBCE002C84F0 /* longman.json in Resources */ = {isa = PBXBuildFile; fileRef = F058C7F12397A180002C84F0 /* longman.json */; };
F058C800239930F2002C84F0 /* BookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F058C7FF23992F92002C84F0 /* BookmarksView.swift */; };
F058C801239930F3002C84F0 /* BookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F058C7FF23992F92002C84F0 /* BookmarksView.swift */; };
F0623E422416D1C2005BC86C /* CambridgeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0623E412416D1C2005BC86C /* CambridgeStore.swift */; };
F0623E432416D1CA005BC86C /* CambridgeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0623E412416D1C2005BC86C /* CambridgeStore.swift */; };
F0623E442416D1D6005BC86C /* CambridgeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0623E412416D1C2005BC86C /* CambridgeStore.swift */; };
F064B4A423CC569400F28314 /* CGFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F064B4A323CC569400F28314 /* CGFloat.swift */; };
F064B4A523CC569400F28314 /* CGFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F064B4A323CC569400F28314 /* CGFloat.swift */; };
F064B4A623CC569400F28314 /* CGFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F064B4A323CC569400F28314 /* CGFloat.swift */; };
Expand Down Expand Up @@ -645,6 +648,7 @@
F058C7F12397A180002C84F0 /* longman.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = longman.json; sourceTree = "<group>"; };
F058C7FF23992F92002C84F0 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
F062029423812264002EEAEE /* YTranslatorRepresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YTranslatorRepresenter.swift; sourceTree = "<group>"; };
F0623E412416D1C2005BC86C /* CambridgeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CambridgeStore.swift; sourceTree = "<group>"; };
F064B4A323CC569400F28314 /* CGFloat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGFloat.swift; sourceTree = "<group>"; };
F064B4A923CC598400F28314 /* NumberFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormatter.swift; sourceTree = "<group>"; };
F065095623ADF6A3003D2410 /* AudioStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioStore.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1253,6 +1257,7 @@
F065095D23AE0353003D2410 /* ViewsStore.swift */,
F0418B97239A32FD00B59C66 /* BookmarksStore.swift */,
F0553727239BE3C700BA24BA /* LongmanStore.swift */,
F0623E412416D1C2005BC86C /* CambridgeStore.swift */,
F0039EC223447E24002F3F95 /* SharedContainer.swift */,
F06DB115234611BB00C2DE90 /* ExtensionManager.swift */,
);
Expand Down Expand Up @@ -1964,6 +1969,7 @@
F0C4EDA7234926D400CCD97A /* ReversoRepresenter.swift in Sources */,
F0EE0A1023478C86004A5EAD /* DOMEvent.swift in Sources */,
F0C4EDAA2349271C00CCD97A /* ViewRepresentable.swift in Sources */,
F0623E432416D1CA005BC86C /* CambridgeStore.swift in Sources */,
F0AD8B1F236414070017C22F /* TranslateAction.swift in Sources */,
F06DB1002344856300C2DE90 /* Store.swift in Sources */,
F0BB43742344844800ADBEF1 /* PDFKitView.swift in Sources */,
Expand Down Expand Up @@ -2235,6 +2241,7 @@
F099421A23AD43D8003CF1EB /* YTranslatorView.swift in Sources */,
F099422D23AD44E7003CF1EB /* AudioPlayer.swift in Sources */,
F0C02D4823B27CA100B393A5 /* Logger.swift in Sources */,
F0623E442416D1D6005BC86C /* CambridgeStore.swift in Sources */,
F099423123AD450A003CF1EB /* WKPageView.swift in Sources */,
F099422F23AD44F9003CF1EB /* OpenPanel.swift in Sources */,
F099422423AD445F003CF1EB /* WebViewContainer.swift in Sources */,
Expand Down Expand Up @@ -2267,6 +2274,7 @@
F0F256BD233D309F00C9D719 /* String.swift in Sources */,
F0AB12A3233F5798005B9F2A /* ReaderView_Pdf.swift in Sources */,
F08D9404239C0E4400147ECE /* BookmarksView_List_Row.swift in Sources */,
F0623E422416D1C2005BC86C /* CambridgeStore.swift in Sources */,
F0AD8B1E236414070017C22F /* TranslateAction.swift in Sources */,
F0EDFB25239E49480048CFD1 /* AudioPlayer.swift in Sources */,
F02B04B723A2894700F93B84 /* PeerBrowser.swift in Sources */,
Expand Down
10 changes: 7 additions & 3 deletions ReaderTranslator/Components/SpeechSynthesizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,13 @@ class SpeechSynthesizer {
if stopSpeaking { return }
}

cancellableLongmanSpeak = LongmanStore.shared.fetchInfo(text: text)
.sink { isSoundExist in
if isSoundExist {
cancellableLongmanSpeak = Publishers.CombineLatest(
LongmanStore.shared.fetchInfo(text: text),
CambridgeStore.shared.fetchInfo(text: text))
.sink { isLongmanSoundExist, isCambridgeSoundExist in
if isCambridgeSoundExist {
CambridgeStore.shared.play()
}else if isLongmanSoundExist {
LongmanStore.shared.play()
} else {
speakByEngine(text: text, voiceName: voiceName, isVoiceEnabled: isVoiceEnabled)
Expand Down
114 changes: 114 additions & 0 deletions ReaderTranslator/Stores/CambridgeStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// CambridgeStore.swift
// ReaderTranslator
//
// Created by Viktor Kushnerov on 9/3/20.
// Copyright © 2020 Viktor Kushnerov. All rights reserved.
//

import AVFoundation
import Combine
import Foundation
import SwiftSoup
import SwiftUI

private var cancellable: AnyCancellable?
private var player: AVAudioNetPlayer?

struct CambridgeSentence: Hashable {
static let empty = Self(text: "No sentences", url: URL.empty)

let text: String
let url: URL
}

typealias CambridgeSentences = [CambridgeSentence]

final class CambridgeStore: NSObject, ObservableObject {
private override init() { super.init() }
static var shared = CambridgeStore()

@Published var audioRate: Float = 1
@Published var word = ""

private let defaultURL = "https://dictionary.cambridge.org/dictionary/english-russian/"
private var audioUrls = Stack<URL>()

func fetchInfo(text: String) -> AnyPublisher<Bool, Never> {
let text = text.encodeUrl
guard let url = URL(string: "\(defaultURL)\(text)") else {
return Just(false).eraseToAnyPublisher()
}

return URLSession.shared.dataTaskPublisher(for: url)
.map {
guard let html = String(data: $0.data, encoding: .utf8) else { return false }
do {
let document = try SwiftSoup.parse(html)

self.audioUrls.removeAll()
let isBreExist = self.addAudio(selector: ".uk [type='audio/mpeg']", document: document)
let isAmeExist = self.addAudio(selector: ".us [type='audio/mpeg']", document: document)

return isBreExist || isAmeExist
} catch {
Logger.log(type: .error, value: error)
}

return false
}
.catch { _ in
Just(false)
}
.eraseToAnyPublisher()
}
}

extension CambridgeStore {
func addAudio(url: URL) {
audioUrls.push(url)
}

private func addAudio(selector: String, document: Document) -> Bool {
do {
guard let elm = try document.select(selector).first else { return false }
let string = try elm.attr("src")
guard let url = URL(string: "https://dictionary.cambridge.org/\(string)") else { return false }

addAudio(url: url)
return true
} catch {
Logger.log(type: .error, value: error)
}
return false
}
}

extension CambridgeStore {
func play() {
guard let url = audioUrls.pop() else { return }

if AudioStore.shared.isSpeakWords {
player = AVAudioNetPlayer()
player?.delegate = self
player?.play(url: url)
}
}
}

extension CambridgeStore: AVAudioNetPlayerDelegate {
func audioPlayerLoadDidFinishDidOccur() {}

func audioPlayerCreateSuccessOccur(player: AVAudioPlayer) {
player.enableRate = true
player.rate = audioRate
player.volume = AudioStore.shared.wordsVolume
player.play()
}

func audioPlayerLoadErrorDidOccur() { play() }
func audioPlayerCreateErrorDidOccur() { play() }

func audioPlayerDidFinishPlaying(_: AVAudioPlayer, successfully _: Bool) { play() }
func audioPlayerDecodeErrorDidOccur(_: AVAudioPlayer, error _: Error?) { play() }
}
7 changes: 3 additions & 4 deletions ReaderTranslator/Stores/LongmanStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ final class LongmanStore: NSObject, ObservableObject {
}

private let defaultURL = "https://www.ldoceonline.com/dictionary/"
private var audioUrls = Stack<URL>()

func fetchInfo(text: String) -> AnyPublisher<Bool, Never> {
let text = text.replacingOccurrences(of: " ", with: "-")
Expand All @@ -52,7 +51,7 @@ final class LongmanStore: NSObject, ObservableObject {
do {
let document = try SwiftSoup.parse(html)

self.audioUrls.removeAll()
Store.shared.audioUrls.removeAll()
let isBreExist = self.addAudio(selector: ".brefile", document: document)
let isAmeExist = self.addAudio(selector: ".amefile", document: document)

Expand Down Expand Up @@ -101,7 +100,7 @@ extension LongmanStore {
}

func addAudio(url: URL) {
audioUrls.push(url)
Store.shared.audioUrls.push(url)
}

private func addAudio(selector: String, document: Document) -> Bool {
Expand All @@ -121,7 +120,7 @@ extension LongmanStore {

extension LongmanStore {
func play() {
guard let url = audioUrls.pop() else { return }
guard let url = Store.shared.audioUrls.pop() else { return }

if AudioStore.shared.isSpeakWords {
player = AVAudioNetPlayer()
Expand Down
4 changes: 3 additions & 1 deletion ReaderTranslator/Stores/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import SwiftUI
final class Store: ObservableObject {
private init() {}
static var shared = Store()


var audioUrls = Stack<URL>()

let maxViewWidth: CGFloat = 400

@Published var translateAction = TranslateAction()
Expand Down
2 changes: 1 addition & 1 deletion ReaderTranslatorMac/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.12.1</string>
<string>1.12.2</string>
<key>CFBundleVersion</key>
<string>1800</string>
<key>LSApplicationCategoryType</key>
Expand Down

0 comments on commit 841d262

Please sign in to comment.